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 singledispatch functions, verbs are dispatched by the type of their 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 to change the default behavior, you can register a generic function 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 that can be used as arguments to another verb without passing the data argument. They can also work as normal verbs.
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 elements
# 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 data is piped in, it is evaluated
[1, 2, 3] >> times(2) # [2, 4, 6]