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 _),
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)
Where are Func objects created?
Func()constructor; end-user should not have to call thisfunc()decorator; end-user calls this as
@func
def even(x: int) -> bool:
return x % 2 == 0
Function combinators supported in
Func:@ | & + * ** ^FuPylambda abstractions defined byla('la_expr')Operator sections defined via operators in
OperatorSectionOperators wrapped in
FuPy’soperator_module
Lazy
TO BE COMPLETED
Where are Lazy objects (thunks) created?
Lazy()constructor call; end-user should not have to call thislazy(f)for functionf; used e.g. infixlazy('expr')Some function combinators supported in
Func
Where are Lazy objects evaluated?
When explicitly calling
Lazy._get(); end-user should not have to call thisVia
evaluate()orutils.force()When calling them:
Lazy.__call__()When indexing them:
Lazy.__getitem__()When operating on them
See operators in
Lazy(To Be Completed)
In
guard(p)(a),(f | g)(a), and(f + g)(a),awill be evaluated
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 ** (iterated composition)
Combinator ^ (functorial exponentiation)