Skip to main content

ZefOps

Autogenerated for version 0.15.6.post1.

ApplyFunctions

Apply different functions to different elements in a list.
The use case is similar to using Zef's map[f1,f2,f3] with multiple
functions, but fills in the gap when the input is not a list / stream.
In this sense, it is closer to func.
The input list and list of functions must be of the same length.

---- Examples ----
['Hello', 'World'] | apply_functions[to_upper_case, to_lower_case] # => ['HELLO', 'world']

---- Signature ----
(Tuple[T1, ...,TN], Tuple[T1->TT1, ..., TN->TTN] ) -> Tuple[TT1, ...,TTN]

---- Tags ----
- used for: control flow
- operates on: ZefOps, Functions
- related zefop: func
- related zefop: reverse_args
- related zefop: map

Map

Apply a pure function elementwise to each item of a collection.
It can be used both for iterables and streams (observables).

Note: It is strongly recommended not to use impure functions
(with side effects, i.e. that change any state of the program)
inside "map". You may be looking for "for_each" or "tap" in that
case.

---- Examples -----
>>> [3, 4, 5] | map[add[1]] # => [4, 5, 6]
>>> [1, 2, 3] | map[str, add[100]] # => [('1', 101), ('2', 102), ('3', 103)]

---- Signature ----
(List[T1], (T1 -> T2)) -> List[T2]

---- Tags ----
- used for: control flow
- used for: function application
- related zefop: apply_functions

MapCat

Returns the flatten results of f applied to each item of x. Equivalent to:
`x | map[f] | concat`.

---- Examples ----
>>> [1,0,3] | flat_map[lambda x: x | repeat[x]] # => [1, 3, 3, 3]
>>> [z1,z2] | flat_map[Outs[RT]] # All outgoing relations on z1 and z2.

---- Signature ----
(Iterable, Function) => List

Reduce

None

Scan

An operator that take a list of values together with an initial state
an emits a sequence of resulting states.
Similar to 'reduce', but also emits all previous states if required.

This implies "scan[f][state_ini] | last = reduce[f][state_ini]".

Equivalent to Python's builtin 'accumulate', but named differently
to be consistent with ReactiveX and the nomenclature in the
live data streaming community.

This is also called 'scan' in reactiveX and Scala, 'scanl' in
Haskell and accumulate in python itertools. Similar to reduce,
but returns a sequence with each intermediate result. Essentially
this is the operator equivalent to event sourcing or the state model
in React/Redux and Elm.

Note: On the very first call s='a' and el='b' are used

---- Examples ----
>>> ['a', 'b', 'c', 'd', 'e'] | scan[lambda s, el: s+el]
>>> # => [ 'a', 'ab', 'abc', 'abcd', 'abcde']

---- Signature ----
(List[T1], ((T2, T1)->T2), T2) -> List[T2]

GroupBy

categories is optional and specifies additional keys/categories
to create, even if there are no occurrences. Passed as a list
[True, False]

---- Tags ----
- used for: control flow
- operates on: List

Transpose

This operator is essentially the operation of transposition on a matrix
that is infinite along one direction (vertical (m) for the
input matrix in normal A_(m,n) nomenclature).
It is different from "interleave" in that it produces a List of Lists,
whereas "interleave" flattens it out into one List.

Note that transposing is its own inverse: transposing twice leads to the
original List, if the operation succeeds.

---- Examples ----
>>> [ [2,3,4], [5,6,7] ] # => [ [2, 5], [3,6], [4,7] ]
>>>
>>> # terminates upon the shortest one:
>>> [ range(2, infinity), [5,6], [15,16,17] ] # => [[2, 5, 15], [3, 6, 16]]

---- Tags ----
- used for: list manipulation
- used for: linear algebra
- same naming as: C++ Ranges V3

Frequencies

None

Iterate

for a homomorphism f and initial val x: [x, f(x), f(f(x)), ...].
Inspired by the excellent talk by Joel Grus on functional Python.

---- Examples ----
>>> 42 | iterate[lambda x: x+1] # => [42, 43, 44, ...]
>>>
>>> # find all of Joe's friends on a graph up to third degree
>>> ([z_joe, ]
>>> | iterate[lambda z: z >> L[RT.FriendOf] | concat | distinct] # flatten out and remove duplicates
>>> | take[3]
>>> )


---- Signature ----
(T, (T->T)) -> List[T]

Identity

None

Length

None

Take

positive n: take n first items. Operates lazily and supports infinite iterators.
negative n: take n last items. Must evaluate entire iterable, does not terminate
for infinite iterables.


---- Signature ----
(List[T], Int) -> List[T]

TakeWhile

None

TakeWhilePair

use when termination of sequence depends on two successive elements.
Generates items until the predicate function returns false for the first time.
Emits both elements given to the predicate function upon the last call.

---- Examples ----
>>> (2
>>> | ops.iterate[lambda n: n+1 if n < 10 else 10]
>>> | take_while_pair[lambda m,n: m!=n]
>>> ) # => [2, 3, 4, 5, 6, 7, 8, 9, 10]

---- Signature ----
(List[T], ((T*T)->Bool)) -> List[T]

TakeUntil

Similar to take_while, but with negated predicate and
it includes the bounding element. Useful in some cases,
but take_while is more common.

---- Examples ----
>>> range(10) | take_until[lambda x: x>4] | collect # => [0, 1, 2, 3, 4, 5]

---- Signature ----
(List[T], (T->Bool)) -> List[T]

SkipWhile

None

Drop

Drop the first n elements of the sequence

Skip

Skip the first n elements of the sequence.
This can be done lazily.

For negative n the counting is done from the back.
This cannot be done lazily: one needs to know when the
list / iterator / stream ends before emitting the
first element.

---- Examples ----
>>> range(10) | skip[3] # => [3,4,5,6,7,8,9]
>>> ['a', 'b', 'c', 'd'] | skip[-2] # => ['a', 'b']

---- Signature ----
(List[T], Int) -> List[T]

Nth

Returns the nth element of an iterable or a stream.
Using negative 'n' takes from the back with 'nth[-1]' being the last element.
An Error value is returned if the index is out of bounds.
Note: this uses a zero-indexed convention.

---- Examples ----
>>>['a', 'b', 'c', 'd'] | nth[1] # => 'b'
>>>['a', 'b', 'c', 'd'] | nth[-2] # => 'c'
>>>['a', 'b', 'c', 'd'] | nth[10] # => Error['nth: index out of range using']

---- Arguments ----
iterable: a List[T] / LazyValue[List[T]] / Awaitable[List[T]]
n (Int): a non-negative integer specifying the index of the element to return.

----- Signature ----
(List[T], Int) -> Union[T, Error]
(LazyValue[List[T]], Int) -> LazyValue[Union[T, Error]]
(Awaitable[List[T]], Int) -> Awaitable[Union[T, Error]]

Filter

Filters an iterable or stream lazily.

---- Examples ----
>>> [1,2,3,4,5] | filter[lambda x: x%2 == 0] # => [2, 4]
>>> [1,2,3,4,5] | filter[greater_than[2]] # => [3, 4, 5]

---- Arguments ----
itr: a List[T] / LazyValue[List[T]] / Awaitable[List[T]]
pred (Func[(T,), Bool]): a predicate function

----- Signature ----
(List[T], (T->Bool)) -> List[T]

---- Tags ----
- used for: control flow
- operates on: List

SelectKeys

Given a dictionary, return a new dictionary containing
the same key value pairs, but for a specified subset of
keys only.

Based on Clojure's select-keys
https://clojuredocs.org/clojure.core/select-keys

---- Examples ----
>>> {'a': 3, 'b': 2, 'c': 1} | select_keys['a']['f'] # => {'a': 3}

----- Signature ----
(Dict[T1, T2], T1, ...) -> Dict[T1, T2]

---- Tags ----
- operates on: Dict
- related zefop: filter
- related zefop: get
- related zefop: get_in
- related zefop: merge

Modulo

The modulo function.

---- Examples ----
>>> 12 | mod[10] # => 2

----- Signature ----
(Int, Int) -> Int

---- Tags ----
- operates on: Int
- topic: Maths

SelectByField

An optimized equivalent of calling:
zrs | filter[Z >> O[rt] | value_or[None] | equals[val]]
although the case of val=None is not permitted.

This is implemented in C++. In the future when the native version is as
fast, this will deprecated.

----- Signature ----
(List[ZefRef], RelationType, Any) -> List[ZefRef]

---- Tags ----
- operates on: Graph
- related zefop: filter

Without

Returns the piped iterable without any items that are contained in the
argument. As a special case, dictionaries are returned without any keys
contained in the argument.

Note the syntax of `| without[1]` is not supported. The argument must be an
iterable.

Note that the type of the output is determined by the following operators,
e.g. `{1,2,3} | without[x] | collect` will return a list, even though the
input is a set.

---- Examples ----
>>> [1,2,3] | without[[2]] # => [1,3]
>>> {'a', 5, 10} | without['abc'] # => [10, 5]
>>> {'a': 1, 'b': 2} | without[['a']] # => {'b': 2}

---- Signature ----
(Iterable, Iterable) => List
(Dict, Iterable) => Dict

First

Return the first element of a list.

---- Examples ----
>>> [1,2,3] | first # => 1
>>> [] | first # => Error

---- Signature ----
List[T] -> Union[T, Error]

Second

Return the second element of a list.

---- Examples ----
>>> [1,2,3] | second # => 2
>>> [1,] | second # => Error

---- Signature ----
List[T] -> Union[T, Error]

Last

Return the last element of a list.

---- Examples ----
>>> [1,2,3] | first # => 3
>>> [] | first # => Error

---- Signature ----
List[T] -> Union[T, Error]

Single

Return the element from a list containing a single element only.

---- Examples ----
>>> [42,] | single # => 42
>>> [42, 43] | single # => Error

---- Signature ----
List[T] -> Union[T, Error]

SingleOr

Given an iterable which should contain either 0 or 1 elements and a default, return the element if it exists, otherwise return the default. Lists that are 2 or more elements long cause an Error

---- Examples ----
>>> [42] | single_or[None] # => 42
>>> [] | single_or[None] # => None
>>> [42, 43] | single_or[None] # => Error

---- Signature ----
List[T],T2 -> Union[T, T2, Error]

Zip

can be used with allputs piped in via one tuple,
or other Lists to zip withbeing curried in.

---- Examples ----
>>> (('a', 'b', 'c', 'd'), range(10)) | zip # => [('a', 0), ('b', 1), ('c', 2), ('d', 3)]
>>> range(10) | zip['a', 'b', 'c', 'd'] # => [(0, 'a'), (1, 'b'), (2, 'c'), (3, 'd')]
>>> range(10) | zip['a', 'b', 'c', 'd'][True,False,True] # => [(0, 'a', True), (1, 'b', False), (2, 'c', True)]
>>> (('a', 'b', 'c', 'd'), range(10)) | zip | zip # => [('a', 'b', 'c', 'd'), (0, 1, 2, 3)]

Concat

Concatenate a list of lists (or streams).
Can also be used to specify other lists to be concatenated as
additional args curried in.

---- Examples ----
>>> # A) all passed in via a List of Lists (no args curried into concat op)
>>> [
>>> [1,2,3],
>>> ['a', 'b', 'c', 'd'],
>>> ] | concat # => [1, 2, 3, 'a', 'b', 'c', 'd']
>>>
>>> # B) One list piped in, other lists to concatenated curried into op
>>> [1,2,3] | concat[ (42,43) ][('a','b','c')] # => [1, 2, 3, 42, 43, 'a', 'b', 'c']

---- Signature ----
(List[T1], List[T2], ...) -> List[T1 | T2 | ...]
(Stream[T1], Stream[T2], ...) -> Stream[T1 | T2 | ...]
(String, String, ...) -> String

---- Tags ----
- operates on: List
- operates on: Stream
- operates on: String
- related zefop: interleave
- related zefop: interleave_longest
- related zefop: merge
- related zefop: append
- related zefop: prepend
- used for: list manipulation
- used for: stream manipulation
- used for: string manipulation

Prepend

Prepend an element to a list / observable.
Special overload for strings as well.

---- Examples ----
>>> ['b', 'c'] | prepend['a'] # => ['a', 'b', 'c']
>>> 'morning' | prepend['good '] # => 'good morning'

---- Signature ----
(List[T1], T2) -> List[T1 | T2]
(Stream[T1], T2) -> Stream[T1 | T2]
(String, String) -> String

---- Tags ----
- operates on: List
- operates on: Stream
- operates on: String
- related zefop: append
- related zefop: insert_at
- used for: list manipulation
- used for: string manipulation

Append

Append an element to a list / observable.
Special overload for strings as well.

---- Examples ----
>>> ['b', 'c'] | append['d'] # => ['b', 'c', 'd']
>>> 'good' | append[' evening'] # => 'good evening'

---- Signature ----
(List[T1], T2) -> List[T1 | T2]
(Stream[T1], T2) -> Stream[T1 | T2]
(String, String) -> String

---- Tags ----
- operates on: List
- operates on: Stream
- operates on: String
- related zefop: prepend
- related zefop: insert_at
- used for: list manipulation
- used for: string manipulation

Interleave

Interleaves elements of an arbitrary number M of lists.
Length of output is determined by the length of the
shortest input list N_shortest: M*N_shortest

---- Examples ----
>>> # Either called with a list of of lists (or equivalent for streams)
>>> [
>>> [1,2,3],
>>> ['a', 'b', 'c', 'd']
>>> ] | interleave # => [1, 'a', 2, 'b', 3, 'c']
>>>
>>> # or with other lists to interleave with being curried in
>>> [1,2,3] | interleave[42 | repeat] # => [1, 42, 2, 42, 3, 42]
>>> [1,2,3] | interleave[42 | repeat][('a','b')] # => [1, 42, 'a', 2, 42, 'b']

---- Signature ----
List[List[T]] -> List[T]

---- Tags ----
- related zefop: interleave_longest
- related zefop: concat
- related zefop: merge
- operates on: List
- same naming as: C++ Ranges V3

InterleaveLongest

Interleaves elements of an arbitrary number M of lists.
Length of output is determined by the length of the
longest input list

---- Examples ----
>>> # Either called with a list of of lists (or equivalent for streams)
>>> [
>>> [1,2,3],
>>> ['a', 'b', 'c', 'd']
>>> ] | interleave_longest # => [1, 'a', 2, 'b', 3, 'c', 'd']
>>>
>>> # or with other lists to interleave with being curried in
>>> [1,2,3] | interleave_longest[[42]*5] # => [1, 42, 2, 42, 3, 42, 42, 42]
>>> [1,2,3] | interleave_longest[[42]*5][('a','b')] # => [1, 42, 'a', 2, 42, 'b', 3, 42, 42, 42]

---- Signature ----
List[List[T]] -> List[T]

---- Tags ----
- related zefop: interleave_longest
- related zefop: concat
- related zefop: merge
- operates on: List

Sort

An optional key function for sorting may be provided, e.g.
list_of_strs | sort[len]

---- Signature ----
List[T] -> List[T]
(List[T], (T->T2)) -> List[T] # T2 orderable

Now

Caution: impure!

The 'now' operator provides the link between the physical
time at which the computation is executed and the "time"
that is part of the inherent data that is operated on and
that is also often saved as data on graphs.

It can be used in a variety of contexts:
a) As a nullary function (i.e. calling it without arguments)
returns the current physical time.
b) As a transformational operator that changes the reference
frame to the currently executing process, i.e. the thread
performing the computation at the very time this operation
is called.

It is impure, since calling it at different physical times will
give different outputs.
Impurity is transitive: any function calling it is also impure.

---- Examples ----
now() # returns current time (of type Time)
g | now # latest graph slice
gs | now # latest graph slice
z_zr_my_entity | now # ZefRef -> ZefRef: fast forward to very latest reference frame at time of execution
z_ezr_my_entity | now # EZefRef -> ZefRef: fast forward to very latest reference frame at time of execution
z_ezr_my_entity[allow_tombstone] | now # EZefRef -> ZefRef: flag allows representing RAEs that were terminated.

---- Signature ----
() -> Time
Graph -> GraphSlice
GraphSlice -> GraphSlice
ZefRef -> ZefRef
EZefRef -> ZefRef
(ZefRef, ZefOp) -> ZefRef
(EZefRef, ZefOp) -> ZefRef

Events

Given a TX as a (E)ZefRef, return all events that occurred in that TX.
Given a ZefRef to a RAE, return all the events that happend on the RAE.

- filter_on allows for filtering on the type of the events; by default it is none which
returns all events.

---- Examples ----
>>> z_tx | events => [instantiated[z1], terminated[z3], value_assigned[z8][41][42] ]
>>> z_rae | events => [instantiated[z1], value_assigned[z8][41][42], terminated[z3]]
>>> z_rae | events[Instantiated] => [instantiated[z1]]
>>> z_rae | events[Instantiated | Assigned] => [instantiated[z1], value_assigned[z8][41][42]]

---- Signature ----
Union[ZefRef[TX], EZefRef[TX]] -> List[ZefOp[Union[Instantiated[ZefRef], Terminated[ZefRef], ValueAssigned[ZefRef, T]]]]

ToDelegate

None

DelegateOf

None

In

Traverse along a unique Incoming relation to the
thing attached to the source of that relation.
If there is no or multiple incoming relations,
it will return an Error.

For a ZefRef, it will always stay in the same time
slice of the given graph.

When Used on an EZefRef, the eternal graph is traversed.

---- Examples ----
>>> z1s_friend = z1 | In[RT.FriendOf]

---- Signature ----
ZefRef -> ZefRef | Error
EZefRef -> EZefRef | Error

Ins

Traverse along all incoming relation of the specified
type to the thing attached to the source of each relation.

For a ZefRef, it will always stay in the same time
slice of the given graph.

When Used on an EZefRef, the eternal graph is traversed.

---- Examples ----
>>> z1s_friend = z1 | Ins[RT.FriendOf]

---- Signature ----
ZefRef -> ZefRefs
EZefRef -> EZefRefs

Out

Traverse along a unique outgoing relation to the
thing attached to the target of that relation.
If there is no or multiple outgoing relations,
it will return an Error.
This function can also be used by specifying logical
subtypes. The edge type `rt` to traverse on can
be seen as the type to filter the instances of
outgoing edges on, i.e. as special case of pattern
matching.

The default value for `rt` is VT.Any, i.e. no filtering
on the relation type is performed and it is assumed
that a single outgoing relation of any type exists.

For a ZefRef, it will always stay in the same time
slice of the given graph.

When Used on an EZefRef, the eternal graph is traversed.

---- Examples ----
>>> z1s_friend = z1 | Out[RT.FriendOf]
>>> z1s_friend = z1 | Out

---- Signature ----
ZefRef -> ZefRef | Error
EZefRef -> EZefRef | Error

Outs

Traverse along all outgoing relation of the specified
type to the thing attached to the target of each relation.

For a ZefRef, it will always stay in the same time
slice of the given graph.

When Used on an EZefRef, the eternal graph is traversed.

---- Examples ----
>>> z1s_friend = z1 | Outs[RT.FriendOf]

---- Signature ----
ZefRef -> ZefRefs
EZefRef -> EZefRefs

InRel

Traverse onto a unique incoming relation of the specified
type and return the relation (it does NOT proceed to the source).
In case of no or multiple incoming relations of
the specified type, it will return an Error.

For a ZefRef, it will always stay in the same time
slice of the given graph.

When Used on an EZefRef, the eternal graph is traversed.

---- Examples ----
>>> z1s_friend = z1 | in_rel[RT.FriendOf]

---- Signature ----
ZefRef -> ZefRef | Error
EZefRef -> EZefRef | Error

InRels

Traverse onto all incoming relations of the specified
type and return the relations (it does NOT proceed
to the sources).

For a ZefRef, it will always stay in the same time
slice of the given graph.

When Used on an EZefRef, the eternal graph is traversed.

---- Examples ----
>>> z1s_friends = z1 | in_rels[RT.FriendOf]

---- Signature ----
ZefRef -> ZefRefs
EZefRef -> EZefRefs

OutRel

Traverse onto a unique outgoing relation of the specified
type and return the relation (*NOT* the target).
In case of no or multiple outgoing relations of
the specified type, it will return an Error.

For a ZefRef, it will always stay in the same time
slice of the given graph.

When Used on an EZefRef, the eternal graph is traversed.

---- Examples ----
>>> z1s_friend = z1 | out_rel[RT.FriendOf]

---- Signature ----
ZefRef -> ZefRef | Error
EZefRef -> EZefRef | Error

OutRels

Traverse onto all outgoing relations of the specified
type and return the relations (it does NOT proceed
to the targets).

For a ZefRef, it will always stay in the same time
slice of the given graph.

When Used on an EZefRef, the eternal graph is traversed.

---- Examples ----
>>> z1s_friends = z1 | out_rels[RT.FriendOf]

---- Signature ----
ZefRef -> ZefRefs
EZefRef -> EZefRefs

InOld

partial(func, *args, **keywords) - new function with partial application
of the given arguments and keywords.

InInOld

partial(func, *args, **keywords) - new function with partial application
of the given arguments and keywords.

OutOld

partial(func, *args, **keywords) - new function with partial application
of the given arguments and keywords.

OutOutOld

partial(func, *args, **keywords) - new function with partial application
of the given arguments and keywords.

InsOld

None

OutsOld

None

InsAndOutsOld

None

Source

None

Target

None

Value

None

Time

Return the time of the object.

---- Signature ----
ZefRef[TX] -> Time
EZefRef[TX] -> Time
GraphSlice -> Time

TimeSlice

Returns the time slice as an Int for GraphSlice.

Note: we may introduce z_tx | time_slice if we can find a convincing use case.
We're leaving it out for now, since this builds on the prior confusion
of identifying transactions themselves with time slice.
The new mental image is that reference frames are somewhat distinct from
TXs and a time slice applies to a reference frame, not a TX directly.

---- Examples ----
>>> my_graph_slice | time_slice

---- Signature ----
GraphSlice -> Int

Tx

let zr: ZefRef  ezr: EZefRef
zr | tx[instantiated] # keeps ref. frame of zr, returns a ZefRef:
zr | tx[terminated]

NextTX

Given a ZefRef/EZefRef to a tx, move to the next tx.
For a ZefRef with a reference frame, one cannot look
into the future, i.e. get to a TX that was not known
in that reference frame. Nil is returned in that case.

For Both ZeFfRef/EZefRef, Nil is returned when called
on the very lastest TX.

---- Examples ----
z2_tx = z_tx | next_tx | collect

---- Signature ----
ZefRef -> Union[ZefRef, Nil]
EZefRef -> Union[EZefRef, Nil]

PreviousTX

Given a ZefRef/EZefRef to a tx, move to the previous tx.
If one tries to go back from the very first tx, nil is
returned.

---- Signature ----
ZefRef -> Union[ZefRef, Nil]
EZefRef -> Union[EZefRef, Nil]

z2_tx = z_tx | previous_tx | collect

InstantiationTx

None

TerminationTx

None

Instances

None

Uid

None

Frame

Extract the reference frame (subject) of a ZefRef.
Return as a GraphSlice.

---- Examples ----
>>> z_tx_zr | frame # returns the subject (not object pointed to!) / reference frame as a GraphSlice
>>> z_et | frame # returns the reference frame (GraphSlice)
>>> z_aet | frame # returns the reference frame (GraphSlice)
>>> z_rt | frame # returns the reference frame (GraphSlice)

---- Signature ----
ZefRef -> GraphSlice

DiscardFrame

Given any kind of reference referring to a RAE,
it returns the frame-independent representation.

---- Signature ----
ZefRef[ET[T1]] -> Entity[T1]
ZefRef[AET[T1]] -> AtomicEntity[T1]
ZefRef[RT[T1]] -> Relation[T1]
ZefRef[BT.TX] -> TX # TODO
ZefRef[BT.Root] -> Graph # TODO

EZefRef[ET[T1]] -> Entity[T1]
EZefRef[AET[T1]] -> AtomicEntity[T1]
EZefRef[RT[T1]] -> Relation[T1]
EZefRef[BT.TX] -> TX # TODO
EZefRef[BT.Root] -> Graph # TODO

Entity[T1] -> Entity[T1]
AtomicEntity[T1] -> AtomicEntity[T1]
Relation[T1] -> Relation[T1]

---- Tags ----

BaseUid

None

OriginUid

used in constructing GraphDelta, could be useful elsewhere

OriginRAE

For RAEs, return an abstract entity, relation or atomic entity. For delegates, acts as the identity.

ExistsAt

None

IsZefRefPromotable

None

InFrame

Represent a RAE in a specified reference frame.
No changes are made to the graph and an error is returned if the operation is not possible,

- optional arguments: in_frame[allow_tombstone]

---- Examples ----
>>> z | in_frame[my_graph_slice] # z: ZefRef changes the reference frame, also if z itself points to a tx
>>> z | in_frame[allow_tombstone][my_graph_slice] # allow can opt in to represent RAEs that were terminated in a future state

---- Signature ----
(ZefRef, GraphSlice) -> Union[ZefRef, Error]
(EZefRef[TRAE], GraphSlice) -> Union[ZefRef, Error]

ToGraphSlice

Return the object (tx pointed to by a ZefRef[TX]) as a GraphSlice.
Works for ZefRef[TX] and EZefRef[TX] as well as (Graph, Time) pairs in both orders.
z_tx_zr | to_graph_slice # ZefRef -> GraphSlice, discards reference frame, returns a GraphSlice
z_tx_ezr | to_graph_slice # EZefRef -> GraphSlice returns a GraphSlice

---- Examples ----
>>> t1: Time = now()
>>> g | to_graph_slice[t1]
>>> t1 | to_graph_slice[g]

ToTx

Given a GraphSlice (reference frame), return a ZefRef[TX] to the
transaction that led to this state.
The ZefRef has the same transaction as object and subject (ref frame).

---- Examples ----
>>> my_graph_slice | to_tx # => ZefRef[TX]

---- Signature ----
GraphSlice -> ZefRef[TX]
(Graph, Time) -> ZefRef[TX]

TimeTravel

Move the reference frame in time only.
The temporal movement can absolute: p is a Time
or relative: p is a Int (move this number of graph slices) or a duration
i.e. the eternal graph of the reference frame
remains contant.

---- Examples ----
>>> # ---- relative time travel ----
>>> zr | time_travel[-3] # how many time slices to move
>>> zr | time_travel[-3.5*units.seconds]
>>> my_graph_slice | time_travel[-3]
>>>
>>> # ---- absolute time travel ----
>>> t1 = Time('October 20 2020 14:00 (+0100)')
>>> g | time_travel[t1]
>>> gs | time_travel[t1]
>>> ezr | time_travel[t1]
>>> zr | time_travel[t1]
>>>
>>> ezr | time_travel[allow_tombstone][t1]
>>> zr | time_travel[allow_tombstone][-3]
>>> zr | time_travel[allow_tombstone][-3.5*units.seconds]
>>> zr | time_travel[allow_tombstone][t1]

---- Signature ----
(ZefRef, Int) -> Union[ZefRef, Nil] # nil or error? What is the process / criterion?
(ZefRef, Duration) -> Union[ZefRef, Nil]
(GraphSlice, Int) -> Union[GraphSlice, Nil]
(GraphSlice, Duration) -> Union[GraphSlice, Nil]

(ZefRef, Time) -> Union[ZefRef, Nil]
(EZefRef, Time) -> Union[ZefRef, Nil]
(Graph, Time) -> Union[GraphSlice, Nil]
(GraphSlice, Time) -> Union[GraphSlice, Nil]

---- Tags ----
- used for: time traversal
- related zefop: time_slice

ToEZefRef

None

O

O[...]  is "optional", meaning it will return either 0,1 results.
If the RT.Edge exists it'll traverse that and return a ZefRef
If the RT.Edge doesn't exist, it'll return None or nothing .
If there are multiple RT.Edge it'll throw an error

---- Examples ----
>>> z1 >> O[RT.Foo] # of type {ZefRef, None, Error}

L

None

Terminate

None

AssignValue

None

ET

None

RT

None

AET

None

BT

None

FillOrAttach

None

Assert

None

HasOut

None

HasIn

None

Run

None

Tap

None

Push

A pure operator that constructs an effect (a value)
to push an element into a pushable stream.

---- Examples ----
>>> {'msg': 'hello'} | push[my_pushable_stream] # returns an effect
>>> my_msg_stream | subscribe[push[my_pushable_stream]] # messages are transformed into Effects and executed upon arrival
>>>
>>> # upcoming functionality
>>> my_effect | push[z_process] # wraps an effect as a task effect that will be routed and potentially executed on the specified process
>>> my_effect | push[zefhub]

---- Signature ----
(T, Stream) -> Effect

IsA


IsRepresentedAs

None

RepresentationType

Warning: this function is not complete and its behavior may change!!!!!!!!!!!

Returns a Zef ValueType_ (a value itself),
also when used with the python builtin supported
types, as well as with instances of Zef Values.

TODO:
my_ent | representation_type # Entity[ET.Foo]
z | representation_type # ZefRef[ET.Foo]
ez| representation_type # EZefRef[ET.Foo]

ET.Foo | representation_type # ET
ET | representation_type # ValueType

HasRelation

None

Relation

None

Relations

None

Chunk

This one is tricky. We'll be opinionated and eagerly evaluate the
elements in each chunk, as the chunks are being asked for. The
chunks are returned as a generator.

If the input is an iterable (could be infinite generator or not),
this returns a generator of generators. An operator downstream could
choose to evaluate the terms of a later iterator before those of an
earlier one emitted here. If we can't access via ...[], we have to
iteratively step through.

Be opinionated here: instead of going full in with checking whether
the iterable has random access ...[n] semantics (C++ has the more
detailed model built in for this), we'll perform caching.

Perform caching and store the evaluated values in case these are
to be accessed by the earlier iterable. Keep them up to the point
until the earlier iterable has consumed them, then discard them.

---- Examples ----
>>> range(8) | chunk[3] # => [[0, 1, 2], [3, 4, 5], [6, 7]]
>>> 'abcdefgh' | chunk_imp[3] # => ['abc', 'def', 'gh']

---- Signature ----
(List[T], Int) -> List[List[T]]
(Stream[T], Int) -> Stream[Stream[T]]
(String, Int) -> List[String]

---- Tags ----
operates on: List
operates on: String
operates on: Stream
used for: list manipulation
used for: string manipulation
used for: stream manipulation
related zefop: stride
related zefop: sliding
related zefop: slice

Sliding

Given a list, return a list of internal lists of length "window_size".
"stride_step" may be specified (optional) and determines how
many elements are taken forward in each step.
Default stride_step if not specified otherwise: 1.

---- Examples ----
>>> range(5) | sliding[3] # [(0, 1, 2), (1, 2, 3), (2, 3, 4)]
>>> range(5) | sliding[3][2] # [(0, 1, 2), (2, 3, 4)]

---- Signature ----
(List[T], Int, Int) -> List[List[T]]

---- Tags ----
- related zefop: stride
- related zefop: chunk
- related zefop: slice
- operates on: List

implementation follows Scala's, see https://stackoverflow.com/questions/32874419/scala-slidingn-n-vs-groupedn

Stride

Return a new list where only every nth
(specified by the stride step) is sampled.

---- Examples ----
>>> Range(8) | stride[3] # [0, 3, 6]

---- Signature ----
(List[T], Int) -> List[T]

---- Tags ----
operates on: List
operates on: String
operates on: Stream
used for: list manipulation
used for: string manipulation
used for: stream manipulation
related zefop: chunk
related zefop: sliding
related zefop: slice

Insert

Takes a dictionary / flatgraph together with something to insert
and returns a new dictionary / flatgraph with that inserted.
The input values are not mutated or harmed during this operation.


---- Examples ----
>>> {'a': 1} | insert['b'][2] -> {'a': 1, 'b': 2}
>>> FlatGraph() | insert[ET.God, RT.Favorite, Val(1/137)] | insert[ET.BigBang]

---- Signature ----
(Dict[T1][T2], T1, T2) -> Dict[T1][T2]

---- Tags ----
- level: easy
- used for: control flow
- operates on: Dict
- operates on: FlatGraph
- related zefop: insert_in
- related zefop: remove
- related zefop: update

InsertInto

Unclear whether we need this. To insert a key value
pair into a dictionary, one could cast to a dict and use merge.
But this may be shorter.

This function could also be used on Lists.

---- Examples ----
>>> (2, 'a') | insert_into[ range(4) ] # [0,1,'a',2,3]

---- Signature ----
((T1, T2), Dict[T3][T4]) -> Dict[T1|T3][T2|T4]

# TODO: make this work: (10, 'a') | insert_into[range(10) | map[add[100]]] | take[5] | c

ReverseArgs

Useful to transform one zefop into a new operator where
the only difference is that the arguments are reversed.
This applies when we want an operator where the dataflow
is determined by a different argument than the usual one.

Suppose we want to have the op 'insert_into', which is a
slight variation of "insert":
('my_key': 42) | insert_into[{'a':1}]

With this operator, we can construct "insert_into" on the fly:
>>> insert_into = reverse_args[insert]

---- Examples ----
>>> ('my_key': 42) | reverse_args[insert][{'a':1}] # {'my_key': 42, 'a': 1}
>>> 'x' | reverse_args[get][{'a':1, 'x': 42}] # 42

---- Signature ----
(T1, ZefOp[T..., T1][T2], T...) -> T2

---- Tags ----
- level: advanced
- used for: control flow
- operates on: ZefOps
- operates on: Functions
- related zefop: func
- related zefop: apply_functions

Remove

Given a key and a dictionary, returns a new dictionary
with the key value pair removed.

This operator is NOT overloaded for Lists, since this
could be confused with the mutating Python method on
lists: my_list.remove(2) removes the first occurrence
of the VALUE "2" and not at the location.
Use "remove_at" for lists.

---- Examples ----
>>> {'a': 1, 'b', 2} | remove['a'] -> {'b': 2}

---- Tags ----
- operates on: Dict
- related zefop: remote_at
- related zefop: remote_in
- related zefop: insert
- related zefop: get

Get

Typically used for key lookups, equivalent to '[]' operator.
External function equivalent to Python's '__get__' method.
Default values can be provided

---- Examples ----
>>> {'a': 42, 'b': 'hello'} | get['a'] # => 42
>>> {'a': 42, 'b': 'hello'} | get['a'] # => 42

----- Signature ----
(Dict[T1, T2], T1) -> T2
(Graph, T1) -> Any
(FlatGraph, T1) -> Any

---- Tags ----
- operates on: Dict
- operates on: Graph
- operates on: FlatGraph
- related zefop: get_in
- related zefop: get_field
- related zefop: insert
- related zefop: remove
- related zefop: select_in

GetField

Specific to python. Get the attribute of an object, equivalent to
getattr(obj, field).

---- Examples ----
# Note: the following example is much better expressed as ET("Machine")
>>> ET | get_field["Machine"] # => ET.Machine

# Note: the nodes of a NetworkX graph can be accessed via, e.g. list(nxg.nodes)
>>> nxg | get_field["nodes"] | filter[...]

----- Signature ----
(T, String) -> T2

---- Tags ----
- related zefop: get

Enumerate

Given an iterable, returns an iterable of pairs where
the first is an incrementing integer starting at zero.

---- Examples ----
>>> ['a', 'b', 'c'] | enumerate # [(1, 'a'), (2, 'b'), (3, 'c')]

---- Signature ----
List[T1] -> List[Tuple[Int, T1]]
Stream[T1] -> Stream[Tuple[Int, T1]]
String -> List[Tuple[Int, String]]

---- Tags ----
- used for: list manipulation
- used for: string manipulation
- used for: lazy transformation

Items

Return the key-value pairs of a dictionary as a tuple.

---- Examples ----
>>> {'a': 100, 42: 'die antwoord', 'c': True} | values # ( ('a', 100), (42, 'die antwoord'), ('c', True) )

---- Signature ----
Dict[T1][T2] -> List[Tuple[T1, T2]]

---- Tags ----
- used for: dict manipulation
- used for: list manipulation

Values

Return the values of a dictionary

---- Examples ----
>>> {'a': 100, 42: 'die antwoord', 'c': True} | values # (100, 'die antwoord', True)

---- Signature ----
Dict[T1][T2] -> List[T2]

---- Tags ----
- used for: dict manipulation
- used for: list manipulation

Keys

Return the keys of a dictionary

---- Examples ----
>>> {'a': 100, 42: 'die antwoord', 'c': True} | keys # ('a', 42, 'c')

---- Signature ----
Dict[T1][T2] -> List[T1]

---- Tags ----
- used for: dict manipulation
- used for: list manipulation

Reverse

Reverse a list or equivalent structure.

---- Examples ----
>>> [2,3,4] | reverse # [4,3,2]
>>> 'straw' | reverse # 'warts'

---- Signature ----
List[T1] -> List[T1]
Stream[T1] -> Stream[T1]
String -> String

---- Tags ----
- used for: list manipulation
- used for: stream manipulation

RaeType

None

AbstractType

None

Root

Retrieve the root node for a Graph or Graph Slice.

---- Examples ----
>>> g | root
>>> g | now | root

---- Signature ----
Graph -> EZefRef
GraphSlice -> ZefRef

---- Tags ----
- used for: graph traversal

Schema

Returns all schema nodes on the graph or alive in the given GraphSlice.
These are the delegate entities/relations which reference all instances on
the graph.

The optional argument `include_edges` can be set to True to also return the
low-level edges between these nodes, which is useful for plotting with
`graphviz`.

---- Examples ----
>>> g | schema[True] | graphviz # Shows the schema of graph g.
>>> g | now | schema[True] | graphviz # Shows the schema of graph g in the current time slice.

# A blank graph already has one TX delegate node.
>>> g = Graph()
>>> g | schema | collect
... [<EZefRef #65 DELEGATE TX at slice=0>]

---- Signature ----
(Graph, Bool) => List[EZefRef]
(GraphSlice, Bool) => List[ZefRef]

Z

None

Docstring

Return the docstring for a given ZefOp or Zef Function.

---- Examples ----
>>> docstring(nth)

SourceCode

Return the function body for a given ZefOp or Zef Function.

---- Examples ----
>>> source_code(insert_at) | to_clipboard | run

GetIn

Enable one-shot access to elements in nested dictionary
by specifying path as a tuple of keys.
Based on Clojure's 'get-in'.

---- Examples ----
>>> {'a': 1, 'b': {'c': 1}} | get_in[('b', 'c')] # => 1
>>> {'a': 1, 'b': {'c': 1}} | get_in[('b', 'wrong_key')][42] # => 42

InsertIn

---- Examples ----
>>> {'a': 1, 'b': {'c': 1}} | insert_in[('b', 'd')][42] # => {'a': 1, 'b': {'c': 1, 'd': 42}}

Update

Change the value for a given key / index by applying a
user provided function to the old value at that location.

Note: the equivalent ZefOp to use on Lists is "update_at"
(to be consistent with "remove_at" for Lists and
disambiguate acting on the value)

{'a': 5, 'b': 6} | update['a'][add[10]] # => {'a': 15, 'b': 6}

---- Tags ----
- related zefop: update_at
- related zefop: update_in
- related zefop: get
- related zefop: insert
- operates on: Dict
- used for: control flow
- used for: function application

UpdateIn

---- Examples ----
>>> {'a': 1, 'b': {'c': 1}} | update_in[('b', 'c')][add[1]] # => {'a': 1, 'b': {'c': 2}}

---- Signature ----
(Dict, List, T1->T2) -> Dict

---- Tags ----
- related zefop: update
- related zefop: update_at
- related zefop: insert_in
- related zefop: remove_in
- related zefop: get_in
- operates on: Dict
- used for: control flow
- used for: function application

UpdateAt

Apply a specified function "f" to a specified position of
a list using the existing element "el" as function input.
A new list is returned which differs only in that element
which is replaced with "f(el)".
When called with negative index n, this is interpreted as
reverse indexing with -1 referring to the last element.

---- Examples ----
>>> [5,6,7] | update_at[2][add[10]] # => [5,6,17]

---- Signature ----
(List[T1], T1->T2) -> List[T1|T2]

---- Tags ----
- related zefop: update
- related zefop: update_in
- related zefop: remove_at
- related zefop: replace_at
- operates on: List
- used for: control flow
- used for: function application

InsertAt

Insert a specified value at a specified position of
a list. A new list is returned, the input list is not mutated.
When called with negative index n, this is interpreted as
reverse indexing with -1 referring to the last element.

---- Examples ----
>>> [0,1,2] | insert_at[1]['hello'] # => [0,'hello',1,2]

---- Signature ----
(List[T1], T1->T2) -> List[T1|T2]
(List[T1], T2) -> List[T1|T2]

---- Tags ----
- related zefop: insert
- related zefop: insert_in
- related zefop: update_at
- related zefop: remove_at
- related zefop: replace_at
- operates on: List
- operates on: Stream
- operates on: String
- used for: list manipulation
- used for: stream manipulation
- used for: string manipulation

RemoveIn

Given a dictionary and a tuple describing a path into the dictionary,
this returns a new dictionary with the pointed to value removed.
The original dictionary is not mutated.

---- Examples ----
>>> {'a': 1, 'b': {'c': 1}} | remove_in[('b', 'c')] # => {'a': 1, 'b': {}}

---- Signature ----
(Dict[T1][T2], List) -> Dict[T1][T2]

---- Tags ----
- related zefop: remove
- related zefop: remove_in
- related zefop: remove_at
- related zefop: update_in
- related zefop: insert_in
- related zefop: get_in
- operates on: Dict
- used for: control flow
- used for: function application

RemoveAt

Using "remove" based on indexes would be confusing,
as Python's list remove, searches for the first
occurrence of that value and removes it.

['a', 'b', 'c'] | remove_at[1] # => ['a', 'c']
['a', 'b', 'c'] | remove_at[1][0] # => ['c']

---- Signature ----
List[T] & Length[Z] -> List[T] & Length[Z-1]

---- Tags ----
- related zefop: interleave_longest
- related zefop: concat
- related zefop: merge
- operates on: List

Merge

Merge a dictionaries: either one list of dicts or
dicts as multiple args.

Clojure has a similar operator:
https://clojuredocs.org/clojure.core/merge

---- Examples -----
[{'a': 1, 'b': 42}, {'a': 2, 'c': 43}] | merge # => {'a': 2, 'b': 42, 'c': 43}
{'a': 1, 'b': 42} | merge[ {'a': 2, 'c': 43} ]

---- Signature ----
List[Dict] -> Dict
(Dict, Dict) -> Dict
(Dict, Dict, Dict) -> Dict

---- Tags ----
* tool for: dictionaries
* similar: merge_with
...

MergeWith

Merge a list of maps, but give an operation to join the values
for matching keys.
The function provided for the merging must be able to operate on
the value type in the dictionary (it suffices if it works for the
value type of the matching keys only).

Based on the Clojure operator https://clojuredocs.org/clojure.core/merge-with

---- Examples ----
{'a': 1, 'b': 2} | merge_with[add][{'a': 3}] # => {'a': 4, 'b': 2}
{'a': 1, 'b': 2} | merge_with[add][{'a': 3}, {'b': 10, 'c': 5}] # => {'a': 4, 'b': 12, 'c': 5}
[{'a': [1], 'b': [2]}, {'a': [3]}] | merge_with[concat] # => {'a': [1, 2], 'b': [3]}

---- Signature ----
(Dict[T1, T2], ((T2,T2)->T2), Dict[T1, T2]) -> Dict[T1, T2]
(List[Dict[T1, T2]], ((T2,T2)->T2)) -> Dict[T1, T2]

IntToAlpha

Map an integer n to the nth letter of the alphabet.
Always lower case.

---- Examples ----
>>> 3 | int_to_alpha # => 'c'

---- Tags ----
- operates on: Int
- used for: string manipulation

PermuteTo

given a input list, as well as a list of indices,
return the list of elements arranged according to the
list of indices.

>>> ['a', 'b', 'c', 'd'] | permute_to[2,0,1] # => ['c', 'a', 'b']

---- Tags ----
- operates on: List
- used for: list manipulation

Cycle

Given a list, this wll produce another list loft the same structure
that cycles thorugh all elements of the original list n times.

e.g. [2,3] | cycle[3] -> [2,3,2,3,2,3]
cycle[None]: means never terminate

Repeat

None

Contains

None

ContainedIn

None

All

The all op has two different behaviours:

A) The first is to "find all of" for a graph-like or graph-slice-like
object. Of a GraphSlice/FlatGraph, this will return a ZefRef list of every RAE, and of a
Graph this will return a EZefRef list of every blob.

An optional argument can be a type that provides a filter on the kind of
items returned. It should always be true that: `g | all[Type]` is equivalent
to `g | all | filter[is_a[Type]]` however providing the Type to `all` can be
much more efficient.

B) The second behaviour is to test the truth of every element in a list. It
is similar to the builtin `all` function of python. If the list is empty,
returns True.

---- Examples ----
>>> g | now | all[ET] # all entities in the latest timeslice of the graph

>>> g | all[TX] # all transaction blobs in the graph

>>> [True,True] | all # => True
>>> [False,True] | all # => False
>>> [] | all # => True

Test whether all ZefRefs have an RT.Name relation.
>>> zrs | map[has_out[RT.Name] | all

---- Signature ----
List[T] -> Bool
GraphSliceLike -> List[ZefRef]
GraphLike -> List[EZefRef]
(GraphSliceLike, Type) -> List[ZefRef]
(GraphLike, Type) -> List[EZefRef]

---- Tags ----
- used for: predicates

Any

Given a list of booleans: check whether any of them are true.
Equivalent to the logical 'or' in propositional logic.
Also equivalent to Python's builtin 'any', but pipeable and
applicable to Streams.

An empty list will return False.

---- Examples ----
>>> [False, True, False] | any # => True

>>> [False, False, False] | any # => False

>>> [] | any # => False

Join

join a list of strings with a binding character.
>>> ['foo', 'bar'] | join['-'] # => 'foo-bar'

Trim

None

TrimLeft

None

TrimRight

None

Yo

None

Sign

Utility function that returns the sign of a number (+1/-1).
also: sign(0)=0

---- Signature ----
Union[Int, Float] -> SetOf[-1,0,1] | Error

IfThenElse

Unlike the apply version of this function. If pred evaluates
to true, value1 is returned otherwise value2.

---- Examples ----
>>> 4 | if_then_else[is_even]["even"]["odd"] # => "even"

---- Signature ----
(Any, (T->Bool), (Any), (Any)) -> Any

---- Tags ----
- used for: control flow
- used for: logic
- related zefop: if_then_else_apply
- related zefop: group_by
- related zefop: match
- related zefop: match_apply
- related zefop: filter

IfThenElseApply

Dispatches to one of two provided functions based on the boolean
result of the predicate function, given the input value.
The input value into the zefop is used by the predicate and
forwarded to the relevant case function.

---- Examples ----
>>> add_one, add_two = add[1], add[2]
>>> 4 | if_then_else[is_even][add_one][add_two] # => 5

---- Signature ----
((T->Bool), (T->T1), (T->T2)) -> Union[T1, T2]

---- Tags ----
- used for: control flow
- used for: logic
- related zefop: if_then_else
- related zefop: group_by
- related zefop: match
- related zefop: match_apply
- related zefop: filter

Attempt

Wrap an operator to catch errors. If anything goes wrong,
return the user provided alternative value.

---- Examples ----
>>> risky_chain = (
>>> map[lambda x: 1/x]
>>> | filter[greater_than[0]]
>>> | single
>>> )
>>> alternative_val = 42
>>>
>>> [-1,10,-3] | attempt[risky_chain][alternative_val] # => 0.1
>>> [-1,10,3] | attempt[risky_chain][alternative_val] # => 42 (single called on list with 2 items)
>>> [-1,0,-3] | attempt[risky_chain][alternative_val] # => 42 (division by zero)

---- Signature ----
(T, (T-> Union[T1, Error]), T2) -> Union[T1, T2]

---- Tags ----
- used for: control flow
- related zefop: if_then_else
- related zefop: expect
- related zefop: ensure
- related zefop: bypass

Bypass

specify one or a tuple of types that will be bypassed. Otherwise the specified
function will be called and the result returned.

---- Examples ----
>>> my_stream | bypass[Error][my_func] # any Error will just be forwarded
>>> my_stream | bypass[Error, Int][my_func]
>>> my_stream | bypass[set_of[_1 < 42]][my_func]

---- Signature ----
(List[T, T2], Type(T2), (T -> T3)) -> List[Union[T2, T3]]

Pattern

generates a predicate function given a parameterized pattern for
dicts or list.
It is often used inside the "match" operator.

---- Examples ----
>>> [1,2,3,4,5] | pattern[_any, 2, _any, 4] # => True
>>> [1,2,3,4,5] | pattern[_any, 2, _any, 5] # => False
>>>
>>> {'a': 1, 'b': 2} | pattern[{'a': Z}] # => True: is there any key a?
>>> {'a': 1, 'b': 2} | pattern[{'a': Z, 'b': 2}] # => True

---- Signature ----
(List[T], List[Union[T, _Any]]) -> Bool
(Dict[T1, T2], Dict[Union[T1, _Any], Union[T2, _Any]]) -> Bool

---- Tags ----
- operates on: Dict, List
- used for: control flow
- related zefop: match
- related zefop: match_apply
- related zefop: distinct_by

Replace

Replace any specified old value with a new value in a List.

---- Examples ----
>>> ['a', 'b', 'c', 'd'] | replace['b'][42] # ['a', 42, 'c', 'd']
>>> 'the zen of python' | replace['n']['f'] # 'the zef of pythof'

---- Signature ----
List[T1], T1, T2 -> List[T1 | T2]
Char = String & Length[1]
String, Char, Char -> String

---- Tags ----
- operates on: List, String
- used for: list manipulation
- related zefop: replace_at
- related zefop: insert_in
- related zefop: insert
- related zefop: remove

Distinct

Remove multiple occurrences of the same element (determined
via == comparison evaluating to True). Do this lazily, such
that this can also be applied to lazily.

---- Examples ----
>>> [4,5,4,1,5,4] | distinct # => [4, 5, 1]

---- Arguments ----
v: an iterable with elements that can be compared

---- Signature ----
List[T] -> List[T]
LazyValue[List[T]] -> LazyValue[List[T]]

---- Tags ----
- operates on: List, Stream
- used for: list manipulation
- related zefop: is_distinct
- related zefop: distinct_by

DistinctBy

Remove multiple occurrences of the same element determining equality
after passing through the function passed in.
Do this lazily, such that this can also be applied to lazily.

---- Examples ----
>>> [-1,2,1,-2] | distinct_by[lambda x: x*x] # => [-1, 2]

---- Arguments ----
v: an iterable with elements that can be compared
comparison_function: (T, T) -> Bool

---- Signature ----
(List[T], (T->Any)) -> List[T]
LazyValue[(List[T], (T->Any))] -> LazyValue[List[T]]

---- Tags ----
- operates on: List, Stream
- used for: list manipulation
- related zefop: is_distinct_by
- related zefop: distinct

IsDistinct

Used on an iterable / stream of values and
returns a boolean indicating whether all
values are distinct. i.e. as soon as any value
appears more than once, False is returned.

---- Examples ----
>>> [1,2,3] | is_distinct # => True
>>> [1,2,3,2] | is_distinct # => False

---- Arguments ----
v: an iterable with elements that can be compared

---- Signature ----
List[T] -> Bool
Stream[T] -> Bool
LazyValue[List[T]] -> Bool

---- Tags ----
- operates on: List, String
- used for: set theory
- related zefop: is_distinct_by
- related zefop: distinct

IsDistinctBy

Very similar to `is_distinct` zefop, but takes a user
provided function `fn` to compare the equality testing with.

---- Examples ----
>>> [1,2] | is_distinct_by[lambda x: x%2] # => True
>>> [1,2,3] | is_distinct_by[lambda x: x%2] # => False

---- Arguments ----
v: an iterable with elements that can be compared
fn: the function to be applied elementwise

---- Signature ----
List[T], Callable -> Bool
Stream[T], Callable -> Bool

---- Tags ----
- operates on: List, String
- used for: set theory
- related zefop: distinct_by
- related zefop: is_distinct

Shuffle

Shuffles the elements in a list. If a seed is
provided, this is a pure function.
If no seed is set to None, the system's
RNG is used to draw one. In this case this
operation is impure!


TODO: implement coeffects to get randomness from FX system.

---- Examples ----
>>> ['a', 'b', 'c', 'd'] | shuffle[189237] # => ['d', 'b', 'a', 'c']

---- Tags ----
- operates on: List
- used for: list manipulation
- used for: randomness

Slice

factor out python-style slicing: ...[5:8].
Negative indexes count from the back.
If three arguments are given from the range, the
last one denotes the step size.

---- Examples ----
>>> ['a', 'b', 'c', 'd'] | slice[1:2] # => ['b', 'c']
>>> 'abcdefgh' | slice_imp[1,6,2] # => 'bdf'

---- Tags ----
- operates on: List
- operates on: String
- used for: list manipulation
- used for: string manipulation
- related zefop: take
- related zefop: reverse
- related zefop: first
- related zefop: last
- related zefop: nth

Split

Split a List into a List[List] based on the occurrence of val.
The value that is split on is not contained in any of the output lists.

---- Examples ----
>>> 'abcdeabfb' | split['b'] # => ['a', 'cdea', 'f', '']
>>> [0,1,6,2,3,4,2,] | split[2] # => [[0, 1, 6], [3, 4], []]

---- Signature ----
(List[T], T) -> List[List[T]]

SplitIf

Similar to split, but the user provides a predicate function
that determines the positions to split on.

The symbols that are split on, are not included in the result.

---- Examples ----
>>> 'good4morning2to6you' | split_if[is_numeric] # => ['good', 'morning', 'to', 'you']
>>> range(10) | split_if[lambda x: x % 3 == 0 ] # => [[], [1, 2], [4, 5], [7, 8], []]

---- Signature ----
(List[T], T->Bool) -> List[List[T]]

---- Tags ----
- operates on: List, String
- used for: list manipulation
- used for: string manipulation
- related zefop: split
- related zefop: concat
- related zefop: trim

Graphviz

Returns a GraphViz visualization of the graph passed in.
By default it returns an error if more than 1000 RAEs are to be shown.

Zef graphs are meta-graphs: relations can themselves have outgoing relations
which allows mimicing the behavior of property graphs (with nodes/relations
allowed to contain "objects" / dictionaries).

GraphViz does not allow plotting relations out of relations (please let us
know if we're wrong here and you know a way!). Therefore we need to
map the meta-graph onto a larger true graph (each relation becoming a node)
and subsequently plot this. The downside is the visual clarity:
It may be hard to see which edges/lines are the true edges and which lines
are other incoming/outgoing edges to the actual edge.

As a slight improvement, each relation's color is based on it's type: this
allows identifying the actual relation as the one with and incoming and
outgoing arrow of the same color in case of ambiguity.

---- Examples ----
>>> g | now | all | graph_viz | collect
>>> g | now | all | graph_viz[simplified] | collect # No extra nodes introduced for relations. Hence: can't show relations out of relations.
>>> g | all | graph_viz | collect # show the full eternal graph


---- Signature ----
List[EZefRef] -> Image
List[ZefRef] -> Image
(List[ZefRef], ZefOp) -> Image

Always

Regardless of the input, always return the curried in value.

---- Examples ----
[1,2,3] | map[always[True]] # => [True, True, True]

match[
(equals[5], "special case"),
(always[True], "default")
]

---- Signature ----
(T, T2) -> T2

WithoutAbsorbed

Return the bare Type as if nothing had ever been absorbed.

---- Examples ----
>>> LazyValue(reduce[add[1]][42]) | without_absorbed # => reduce

---- Tags ----
- used for: control flow
- operates on: ZefOps, Value Types, Entity, Relation, AtomicEntity, ZefRef, EZefRef
- related zefop: absorbed
- related zefop: inject
- related zefop: inject_list
- related zefop: reverse_args

Absorbed

Extract the absorbed values from any absorbing type.

---- Examples ----
>>> reduce[add[1]][42] | absorbed # => (add[1], 42)

---- Tags ----
- used for: control flow
- operates on: ZefOps, Value Types, Entity, Relation, AtomicEntity, ZefRef, EZefRef
- related zefop: without_absorbed
- related zefop: inject
- related zefop: inject_list
- related zefop: reverse_args

CartesianProduct

Note that Python's itertools calls this "product only", but
but that term is too overloaded, e.g. the 'multiply' operator.

---- Examples ----
>>> [1,2,3] | cartesian_product[('a', 'b')] # => [ (1, 'a'), (2, 'a'), (3, 'a'), (1, 'b'), (2, 'b'), (3, 'b') ]
>>> [1,2,3] | cartesian_product[('a', 'b')][(True,False)] # => [ (1, 'a', True), ...
>>>
>>> or pass in all args:
>>> ([1,2,3], ['a', 'b']) | cartesian_product # => [ (1, 'a'), (2, 'a'), (3, 'a'), (1, 'b'), (2, 'b'), (3, 'b') ]

---- Signature ----
List[ List[T1], List[T2] ] -> List[ (T1, T2) ]
(List[T1], List[T2]) -> List[ (T1, T2) ]


---- Tags ----
- operates on: List
- used for: combinatorics
- related zefop: combinations
- related zefop: permutations

Permutations

Given a list of items, return a list of lists with all
permutations lazily. Order within the returned combination
does not play a role.

If given, the second argument is the length of each output.

---- Examples ----
>>> ['a', 'b', 'c'] | permutations
>>> # returns:
>>> # [
>>> # ['a', 'b', 'c'],
>>> # ['a', 'c', 'b'],
>>> # ['b', 'a', 'c'],
>>> # ['b', 'c', 'a'],
>>> # ['c', 'a', 'b'],
>>> # ['c', 'b', 'a']
>>> # ]

>>> [1,2,3] | permutations[2] # specify the number of elements in each sample
>>> # returns: [(1, 2), (1, 3), (2, 1), (2, 3), (3, 1), (3, 2)]

---- Signature ----
List[T] -> List[List[T]]
(List[T], Int) -> List[List[T]]

---- Tags ----
- operates on: List
- used for: combinatorics
- related zefop: combinations
- related zefop: cartesian_product

Combinations

Given a list of m elements, return all combinations
of length n. Order within the returned combination
does not play a role.

---- Examples ----
[1,2,3] | combinations[2] # => [(1, 2), (1, 3), (2, 3)]

---- Signature ----
(List[T], Int) -> List[List[T]]

Add

Adds two elements. Neither is a list.
Don't use this when summing up a list, use `sum`
in that case.

---- Examples ----
40 | add[2] # => 42

---- Signature ----
(T1, T2) -> T1 | T2

---- Tags ----
used for: maths
related zefop: sum
related zefop: subtract
related zefop: multiply
related zefop: divide

Sum

Sums up all elements in a List.
Similar to the `add` zefop, but to be used in the case of a
single argument being passed as a list.
`add` is used when exactly two elements are passed individually.