Lambdas โ
The type of functions in SmartPy is sp.TLambda(t1
, t2
, with_storage=None
, with_operations=False
) where t1
is the parameter type and t2
the result type; with_storage
and with_operations
are the lambda context parameters.
By default, lambdas have no effect or context (besides possibly failure) but it is often useful to introduce them.
Contexts โ
The two possible context parameters are with_storage
and with_operations
.
with_storage="read-only"
orwith_storage="read-write"
, for functions that read or read-write the storage.with_operations=True
, for functions that create operations.
Intuitively, a lambda with a non-trivial context (with_storage
and/or with_operations
) is a regular lambda with:
- an augmented type describing contextual use of storage and/or operations;
- some code around its input (for storage) and/or output (for storage and/or operations) to take care of reading and writing needs.
The corresponding type in Michelson is
Michelson lambda.See reference Lambdas template.
Operations โ
Build a lambda โ
sp.build_lambda(l,ย with_storage=None,ย with_operations=False,ย recursive=False)
Build a SmartPy lambda from a Python function or lambda.
with_storage="read-only"
orwith_storage="read-write"
, for functions that read or read-write the storage.with_operations=True
, for functions that create operations.recursive=True
, for recursive lambdas. A second parameter specifies the function name used for recursive calls, e.g.:sp.build_lambda((lambdaย x,ย f: ...f(x-1)....),ย recursive=True)
. Recursive lambdas cannot have storage or operations effects.
For example, sp.build_lambda(lambdaย x: x +ย 3)
represents a function that takes an argument x
and returns x +ย 3
.
This function is usually useless as it is called automatically by SmartPy in most contexts.
Example โ
# Explicit call
logic = sp.build_lambda(lambda x : x + 3)
x = logic(3); # A SmartPy expression that evaluates to 6
# sp.build_lambda is called automatically
# because it needs to be converted to a SmartPy expression
self.data.f = lambda x : x + 3
# This is regular Python
logic = lambda x : x + 3
x = logic(3); # 6
# Recursive factorial function
sp.build_lambda((lambda n, fact: sp.eif(n<=1, 1, n*fact(n-1))), recursive=True)
Define a private lambda โ
@sp.private_lambda(with_storage=None,ย with_operations=False,ย wrap_call=False)
Decorator to introduce a lambda that is also a private variable.
This is used for functions that are expected to be used more than once.
Values are returned by using sp.result(value)
.
WARNING
Deprecated aliases
@sp.global_lambda
is deprecated in favor of
@sp.private_lambda(with_storage=None, with_operations=False, wrap_call=False)
def f(self, params): # Please note that we need a self parameter here.
...
@sp.sub_entry_point
is deprecated in favor of
@sp.private_lambda(with_storage="read-write", with_operations=True, wrap_call=True)
def f(self, params):
...
We have the usual parameters to describe the context plus wrap_call
:
with_storage="read-only"
orwith_storage="read-write"
, for functions that read or read-write the storage.with_operations=True
, for functions that create operations.wrap_call=True
, resolves calls immediately (with an implicitsp.compute
).recursive=True
, for recursive lambdas. A second parameter specifies the function name used for recursive calls (see example below).
Any combination of the parameters for sp.private_lambda(...)
is allowed, but recursive=True
excludes storage and operations effects.
See reference WorldCalculator template.
Example โ
class MyContract(sp.Contract):
# ...
@sp.private_lambda()
def transformer(self, x):
sp.result(x + 5)
@sp.private_lambda(with_operations=True, with_storage="read-write", wrap_call=True)
def set_delegate(self):
sp.set_delegate(sp.some(self.data.baker))
@sp.entrypoint
def ep(self, params):
self.data.result = self.transformer(params)
self.set_delegate() # wrap_call=True is necessary because "self.set_delegate()" result is not assigned to the storage or used in a later step
# A recursive factorial function. You can also replace 'f' with 'factorial'.
@sp.private_lambda(recursive=True)
def factorial(self, n, f):
sp.if n <= 1:
sp.result(n)
sp.else:
sp.result(n * f(n-1))
Calling lambdas โ
f(x)
Call a lambda.
If f
is of type sp.TLambda(t1, t2)
and x
is of type t1
then f(x)
is of type t2
.
INFO
As for every SmartPy expression, simply writing y =ย f(x)
doesn't directly compute f(x)
. It builds an expression that will be computed if used in some action so its output needs to be used or we need to wrap the call in some sp.compute(...)
.
f.apply(x)
Partially apply a lambda.
If f
is of type sp.TLambda(sp.TPair(tp1, tp2), target)
and x
is of type tp1
then f.apply(x)
is of type sp.TLambda(tp2, target)
.