Verbs
Verbs
Registering a verb
You can use register_verb
to register a verb. Here are the arguments of register_verb
:
- cls: The default type to register for _default backend if TypeHolder, it is a generic function, and not counted as a real implementation.
- func: The function works as a verb.
If
None
(not provided), this function will return a decorator. - context: The context to evaluate the arguments
- kw_context: The context to evaluate the keyword arguments
- name: and
- qualname: and
- doc: and
- module: The meta information about the function to overwrite
func
's or when it's not available fromfunc
-
dependent: Whether the verb is dependent.
>>> @register_verb(context=Context.EVAL, dependent=True) >>> def length(data): >>> return len(data) >>> # with dependent=True >>> # length() -> VerbCall, waiting for data to evaluate >>> # with dependent=False >>> # length() -> TypeError, argument data is missing
-
ast_fallback: What's the supposed way to call the verb when AST node detection fails.
- piping - Suppose this verb is called like
data >> verb(...)
- normal - Suppose this verb is called like
verb(data, ...)
- piping_warning - Suppose piping call, but show a warning
- normal_warning - Suppose normal call, but show a warning
- raise - Raise an error
- piping - Suppose this verb is called like
Verbs are pipeable
from pipda import register_verb
@register_verb(int)
def increment(data):
return data + 1
1 >> increment() # 2
# You can also call it normally
increment(1) # 2
More about pipeable, see Piping.
Verbs are dispatchable
Like single-dispatched functions, verbs are dispatchable by the type of its first argument.
from pipda import register_verb
@register_verb(int)
def increment(data):
return data + 1
@increment.register(str)
def _increment(data):
return data + '1'
1 >> increment() # 2
'1' >> increment() # '11'
What if a verb is not registered for a type? It will be dispatched to default generic function, which raises a NotImplementedError
.
[] >> increment() # NotImplementedError
If you want change the default behavior, you can register a generic function by yourself, using a type holder.
from pipda import register_verb
from pipda.utils import TypeHolder
@register_verb(TypeHolder)
def increment(data):
return data + 1
1.1 >> increment() # 2.1
Verbs evaluate other arguments using the first one as data
from pipda import register_verb, Context, Symbolic
f = Symbolic()
@register_verb(list, context=Context.EVAL)
def add(data, other):
"""Add other to each element of data"""
return [d + other for d in data]
[1, 2, 3] >> add(1) # [2, 3, 4]
# Using the first element of data
[1, 2, 3] >> add(f[0]) # [2, 3, 4]
More about contexts, see Contexts.
Verbs pass down the context if not specified in the arguments
from pipda import register_func, register_verb, Context, Symbolic
f = Symbolic()
@register_func() # no context specified
def double(data):
return data * 2
@register_verb(list, context=Context.EVAL)
def add(data, other):
return [d + other for d in data]
[1, 2, 3] >> add(double(f[0])) # [3, 4, 5]
Dependent verbs
Dependent verbs are functions can be used in another verb without passing the data argument. It can also work as a normal verb.
from pipda import register_verb, Context, Symbolic
f = Symbolic()
@register_verb(list, dependent=True)
def times(data, n):
"""Times each element of data with n"""
return [d * n for d in data]
@register_verb(list, context=Context.EVAL)
def add(data, other):
"""Add other to each element of data"""
return [d + other for d in data]
# times 2 to each element and add the first element to all elementss
# Note that we don't pass the first argument to times
[1, 2, 3] >> add(times(2)[0]) # [3, 4, 5]
# When called directly:
times(2) # VerbCall object
times([1, 2, 3], 2) # VerbCall object
# But when a data piped in, it is evaluated
[1, 2, 3] >> times(2) # [2, 4, 6]