Implementation Details
The main classes in FuPy are
For evaluation tracing there are also
The classes FuPy.basics.Empty and
FuPy.basics.Unit are just used as specific types
(empty and singleton, respectively).
Func
Generic class FuPy.basics.Func[A, B] serves two roles:
Func[A, B]is the function space type for functions fromAtoB, also written asA -> B.It is a (thin) wrapper for function objects (or other callables), so that function combinators are supported as infix operators, by overloading
@(composition),|(case),&(split),+(functorial plus),*(functorial times),**(exponentiation).The underlying function is stored in the attribute
self.func.This wrapper also carries some additional information in attributes:
name: how the function prints, in Math notationtop: the top-level operator, in Math notation, if the function is an expression, and''otherwiserequired_args_count: the minimum number of required arguments ofself.func; this is used for auto-(un)packing of arguments
OperatorSection
Class FuPy.basics.OperatorSection is a helper class
to make operator sections work.
Only one instance of this class is needed
and it is made available as x_
(though it is recommended to rename it to _).
OperatorSection redefines various overloadable infix operators op (such as ‘@’),
so that
(_ op other),(other op _), and(_ op _),
to behave as the corresponding operator sections
Func(lambda x: x op other)Func(lambda x: other op x)Func(lambda x: Func(lambda y: x op y)
Lazy
TO BE COMPLETED
Interactions between Func, OperatorSection, Lazy
Suppose that
fandgare instances ofFunc_is an instance ofOperatorSectionthetaandtheta_are instances ofLazy
Here is how their various combinations under @ are handled.
f @ g:f.__matmul__(g)returnsFunc(lambda x: f(g(x)))f @ _:f.__matmul__(_)returnsNotImplemented; next_.__rmatmul__(f)returnsFunc(lambda x: f @ x)f @ theta:f.__matmul__(theta)returnsNotImplemented; nexttheta.__rmatmul__(f)evaluatestheta, say to valueg(which must be of typeFunc) and invokesf @ g, which (see above)) is handled byf.__matmul__(g)_ @ f:_.__matmul__(f) returnsFunc(lambda x: x @ f)`_ @ _:_.__matmul__(_) returnsFunc(lambda x: x @ _); when the latter is called, say onf, this results inf @ _` (see above)_ @ theta:_.__matmul__(theta) returnsFunc(lambda x: x @ theta); when the latter is called, say onf, this results inf @ theta` (see above)theta @ g:theta.__matmul__(g)evaluatestheta, say to valuef(which must be of typeFunc), and invokesf @ g, which is handled byf.__matmul__(g)theta @ _:theta.__matmul__returnsNotImplemented; next_.__rmatmul__(theta)returnsFunc(lambda x: theta @ x); when the latter is called, say ong, this results intheta @ g(see above)theta @ theta_:theta.__matmul__(theta_)evaluatestheta, say to the valuef(which must be of typeFunc) and invokesf @ theta_(see above)
Combinators | (case) and & (split) are handled similarly.
Combinators + (functorial plus) and * (functorial times)
are different, because these operators are also meaningful for types other than Func,
in particular numbers and strings.
Combinator **