The type definition of TermFn is more complex than what it actually does; but that is only because it automatically handles functions with an unspecified (potentially infinite) number of parameters;

All it does tough is just adding the "$" method that replaces the papp call.

to give an idea here how the case of a function from a single input to a single output looks like;

we'll call this type TermLam even if there's no type called so in plu-ts

type TermLam<PIn extends PType, POut extends PType> =
    Term<PLam<PIn,POut>> & {
        $: ( input: Term<PIn> ) => Term<POut>