use functools to avoid bookkeeping of extra args