Top

phi.api module

from .python_builder import P

Then0 = P.Then0
Then = P.Then
Then1 = P.Then1
Then2 = P.Then2
Then3 = P.Then3
Then4 = P.Then4
Then5 = P.Then5
ThenAt = P.ThenAt
Read = P.Read
Write = P.Write
Val = P.Val
Pipe = P.Pipe
Obj = P.Obj
Rec = P.Rec
Context = P.Context
With = P.With
Ref = P.Ref
Dict = P.Dict
F = P.F
ReadList = P.ReadList
List = P.List
Seq = P.Seq
If = P.If

Module variables

var Obj

var Read

var Rec

var Ref

Functions

def Context(

*args)

Builder Core. Also available as a global function as phi.Context.

Returns the context object of the current dsl.With statemente.

Arguments

  • *args: By design Context accepts any number of arguments and completely ignores them.

This is a classmethod and it doesnt return a Builder/Expression by design so it can be called directly:

from phi import P, Context, Obj

def read_file(z):
    f = Context()
    return f.read()

lines = P.Pipe(
    "text.txt",
    P.With( open,
        read_file,
        Obj.split("\n")
    )
)

Here we called Context with no arguments to get the context back, however, since you can also give this function an argument (which it will ignore) it can be passed to the DSL so we can rewrite the previous as:

from phi import P, Context, Obj

lines = P.Pipe(
    "text.txt",
    P.With( open,
        Context, # f
        Obj.read()
        Obj.split("\n")
    )
)

Context yields an exception when used outside of a With block.

Also see

  • phi.builder.Builder.Obj
  • dsl
@staticmethod
def Context(*args):
    """
ilder Core**. Also available as a global function as `phi.Context`.
rns the context object of the current `dsl.With` statemente.
guments**
*args**: By design `Context` accepts any number of arguments and completely ignores them.
 is a classmethod and it doesnt return a `Builder`/`Expression` by design so it can be called directly:
from phi import P, Context, Obj
def read_file(z):
    f = Context()
    return f.read()
lines = P.Pipe(
    "text.txt",
    P.With( open,
        read_file,
        Obj.split("\\n")
    )
)
 we called `Context` with no arguments to get the context back, however, since you can also give this function an argument (which it will ignore) it can be passed to the DSL so we can rewrite the previous as:
from phi import P, Context, Obj
lines = P.Pipe(
    "text.txt",
    P.With( open,
        Context, # f
        Obj.read()
        Obj.split("\\n")
    )
)
text` yields an exception when used outside of a `With` block.
so see**
hi.builder.Builder.Obj`
sl](https://cgarciae.github.io/phi/dsl.m.html)
    """
    if _WithContextManager.WITH_GLOBAL_CONTEXT is utils.NO_VALUE:
        raise Exception("Cannot use 'Context' outside of a 'With' block")
    return _WithContextManager.WITH_GLOBAL_CONTEXT

def Dict(

self, **branches)

def Dict(self, **branches):
    gs = { key : _parse(value)._f for key, value in branches.items() }
    def h(x, state):
        ys = {}
        for key, g in gs.items():
            y, state = g(x, state)
            ys[key] = y
        return _RecordObject(**ys), state
    return self.__then__(h)

def F(

self, expr)

def F(self, expr):
    return self >> expr

def If(

self, condition, *then, **kwargs)

If

If(Predicate, *Then)

Having conditionals expressions a necesity in every language, Phi includes the If expression for such a purpose.

Arguments

  • Predicate : a predicate expression uses to determine if the Then or Else branches should be used.
  • *Then : an expression to be excecuted if the Predicate yields True, since this parameter is variadic you can stack expression and they will be interpreted as a tuple phi.dsl.Seq.

This class also includes the Elif and Else methods which let you write branched conditionals in sequence, however the following rules apply

  • If no branch is entered the whole expression behaves like the identity
  • Elif can only be used after an If or another Elif expression
  • Many Elif expressions can be stacked sequentially
  • Else can only be used after an If or Elif expression

Examples

from phi import P, If

assert "Between 2 and 10" == P.Pipe(
    5,
    If(P > 10,
        "Greater than 10"
    ).Elif(P < 2,
        "Less than 2"
    ).Else(
        "Between 2 and 10"
    )
)
def If(self, condition, *then, **kwargs):
    """
**
If(Predicate, *Then)
ng conditionals expressions a necesity in every language, Phi includes the `If` expression for such a purpose.
guments**
Predicate** : a predicate expression uses to determine if the `Then` or `Else` branches should be used.
*Then** : an expression to be excecuted if the `Predicate` yields `True`, since this parameter is variadic you can stack expression and they will be interpreted as a tuple `phi.dsl.Seq`.
 class also includes the `Elif` and `Else` methods which let you write branched conditionals in sequence, however the following rules apply
 no branch is entered the whole expression behaves like the identity
lif` can only be used after an `If` or another `Elif` expression
ny `Elif` expressions can be stacked sequentially
lse` can only be used after an `If` or `Elif` expression
xamples **
from phi import P, If
assert "Between 2 and 10" == P.Pipe(
    5,
    If(P > 10,
        "Greater than 10"
    ).Elif(P < 2,
        "Less than 2"
    ).Else(
        "Between 2 and 10"
    )
)
    """
    cond_f = _parse(condition)._f
    then_f = E.Seq(*then)._f
    else_f = utils.state_identity
    ast = (cond_f, then_f, else_f)
    g = _compile_if(ast)
    expr = self.__then__(g, **kwargs)
    expr._ast = ast
    expr._root = self
    return expr

def List(

self, *branches, **kwargs)

While Seq is sequential, phi.dsl.Expression.List allows you to split the computation and get back a list with the result of each path. While the list literal should be the most incarnation of this expresion, it can actually be any iterable (implements __iter__) that is not a tuple and yields a valid expresion.

The expression

k = List(f, g)

is equivalent to

k = lambda x: [ f(x), g(x) ]

In general, the following rules apply after compilation:

General Branching

List(f0, f1, ..., fn)

is equivalent to

lambda x: [ f0(x), f1(x), ..., fn(x) ]

Composing & Branching

It is interesting to see how braching interacts with composing. The expression

Seq(f, List(g, h))

is almost equivalent to

List( Seq(f, g), Seq(f, h) )

As you see its as if f where distributed over the List. We say almost because their implementation is different

def _lambda(x):
    x = f(x)
    return [ g(x), h(x) ]

vs

lambda x: [ g(f(x)), h(f(x)) ]

As you see f is only executed once in the first one. Both should yield the same result if f is a pure function.

Examples

form phi import P, List

avg_word_length = P.Pipe(
    "1 22 333",
    lambda s: s.split(' '), # ['1', '22', '333']
    lambda l: map(len, l), # [1, 2, 3]
    List(
        sum # 1 + 2 + 3 == 6
    ,
        len # len([1, 2, 3]) == 3
    ),
    lambda l: l[0] / l[1] # sum / len == 6 / 3 == 2
)

assert avg_word_length == 2

The previous could also be done more briefly like this

form phi import P, Obj, List

avg_word_length = P.Pipe(
    "1 22 333", Obj
    .split(' ')  # ['1', '22', '333']
    .map(len)    # [1, 2, 3]
    .List(
        sum  #sum([1, 2, 3]) == 6
    ,
        len  #len([1, 2, 3]) == 3
    ),
    P[0] / P[1]  #6 / 3 == 2
)

assert avg_word_length == 2

In the example above the last expression

P[0] / P[1]

works for a couple of reasons

  1. The previous expression returns a list
  2. In general the expression P[x] compiles to a function with the form lambda obj: obj[x]
  3. The class Expression (the class from which the object P inherits) overrides most operators to create functions easily. For example, the expression

    (P * 2) / (P + 1)

compile to a function of the form

lambda x: (x * 2) / (x + 1)

Check out the documentatio for Phi lambdas.

def List(self, *branches, **kwargs):
    """
e `Seq` is sequential, `phi.dsl.Expression.List` allows you to split the computation and get back a list with the result of each path. While the list literal should be the most incarnation of this expresion, it can actually be any iterable (implements `__iter__`) that is not a tuple and yields a valid expresion.
expression
k = List(f, g)
quivalent to
k = lambda x: [ f(x), g(x) ]
eneral, the following rules apply after compilation:
neral Branching**
List(f0, f1, ..., fn)
quivalent to
lambda x: [ f0(x), f1(x), ..., fn(x) ]
mposing & Branching**
s interesting to see how braching interacts with composing. The expression
Seq(f, List(g, h))
almost* equivalent to
List( Seq(f, g), Seq(f, h) )
ou see its as if `f` where distributed over the List. We say *almost* because their implementation is different
def _lambda(x):
    x = f(x)
    return [ g(x), h(x) ]
lambda x: [ g(f(x)), h(f(x)) ]
ou see `f` is only executed once in the first one. Both should yield the same result if `f` is a pure function.
Examples
form phi import P, List
avg_word_length = P.Pipe(
    "1 22 333",
    lambda s: s.split(' '), # ['1', '22', '333']
    lambda l: map(len, l), # [1, 2, 3]
    List(
        sum # 1 + 2 + 3 == 6
    ,
        len # len([1, 2, 3]) == 3
    ),
    lambda l: l[0] / l[1] # sum / len == 6 / 3 == 2
)
assert avg_word_length == 2
previous could also be done more briefly like this
form phi import P, Obj, List
avg_word_length = P.Pipe(
    "1 22 333", Obj
    .split(' ')  # ['1', '22', '333']
    .map(len)    # [1, 2, 3]
    .List(
        sum  #sum([1, 2, 3]) == 6
    ,
        len  #len([1, 2, 3]) == 3
    ),
    P[0] / P[1]  #6 / 3 == 2
)
assert avg_word_length == 2
he example above the last expression
P[0] / P[1]
s for a couple of reasons
he previous expression returns a list
n general the expression `P[x]` compiles to a function with the form `lambda obj: obj[x]`
he class `Expression` (the class from which the object `P` inherits) overrides most operators to create functions easily. For example, the expression
(P * 2) / (P + 1)
ile to a function of the form
lambda x: (x * 2) / (x + 1)
k out the documentatio for Phi [lambdas](https://cgarciae.github.io/phi/lambdas.m.html).
    """
    gs = [ _parse(code)._f for code in branches ]
    def h(x, state):
        ys = []
        for g in gs:
            y, state = g(x, state)
            ys.append(y)
        return (ys, state)
    return self.__then__(h, **kwargs)

def Pipe(

self, *sequence, **kwargs)

Pipe runs any phi.dsl.Expression. Its highly inspired by Elixir's |> (pipe) operator.

Arguments

  • *sequence: any variable amount of expressions. All expressions inside of sequence will be composed together using phi.dsl.Expression.Seq.
  • **kwargs: Pipe forwards all kwargs to phi.builder.Builder.Seq, visit its documentation for more info.

The expression

Pipe(*sequence, **kwargs)

is equivalent to

Seq(*sequence, **kwargs)(None)

Normally the first argument or Pipe is a value, that is reinterpreted as a phi.dsl.Expression.Val, therfore, the input None is discarded.

Examples

from phi import P

def add1(x): return x + 1
def mul3(x): return x * 3

x = P.Pipe(
    1,     #input
    add1,  #1 + 1 == 2
    mul3   #2 * 3 == 6
)

assert x == 6

The previous using lambdas to create the functions

from phi import P

x = P.Pipe(
    1,      #input
    P + 1,  #1 + 1 == 2
    P * 3   #2 * 3 == 6
)

assert x == 6

Also see

def Pipe(self, *sequence, **kwargs):
    """
e` runs any `phi.dsl.Expression`. Its highly inspired by Elixir's [|> (pipe)](https://hexdocs.pm/elixir/Kernel.html#%7C%3E/2) operator.
guments**
*sequence**: any variable amount of expressions. All expressions inside of `sequence` will be composed together using `phi.dsl.Expression.Seq`.
**kwargs**: `Pipe` forwards all `kwargs` to `phi.builder.Builder.Seq`, visit its documentation for more info.
expression
Pipe(*sequence, **kwargs)
quivalent to
Seq(*sequence, **kwargs)(None)
ally the first argument or `Pipe` is a value, that is reinterpreted as a `phi.dsl.Expression.Val`, therfore, the input `None` is discarded.
amples**
from phi import P
def add1(x): return x + 1
def mul3(x): return x * 3
x = P.Pipe(
    1,     #input
    add1,  #1 + 1 == 2
    mul3   #2 * 3 == 6
)
assert x == 6
previous using [lambdas](https://cgarciae.github.io/phi/lambdas.m.html) to create the functions
from phi import P
x = P.Pipe(
    1,      #input
    P + 1,  #1 + 1 == 2
    P * 3   #2 * 3 == 6
)
assert x == 6
so see**
hi.builder.Builder.Seq`
sl](https://cgarciae.github.io/phi/dsl.m.html)
ompile](https://cgarciae.github.io/phi/dsl.m.html#phi.dsl.Compile)
ambdas](https://cgarciae.github.io/phi/lambdas.m.html)
    """
    state = kwargs.pop("refs", {})
    return self.Seq(*sequence, **kwargs)(None, **state)

def ReadList(

self, *branches, **kwargs)

Same as phi.dsl.Expression.List but any string argument x is translated to Read(x).

def ReadList(self, *branches, **kwargs):
    """
 as `phi.dsl.Expression.List` but any string argument `x` is translated to `Read(x)`.
    """
    branches = map(lambda x: E.Read(x) if isinstance(x, str) else x, branches)
    return self.List(*branches, **kwargs)

def Seq(

self, *sequence, **kwargs)

Seq is used to express function composition. The expression

Seq(f, g)

be equivalent to

lambda x: g(f(x))

As you see, its a little different from the mathematical definition. Excecution order flow from left to right, this makes reading and reasoning about code way more easy. This bahaviour is based upon the |> (pipe) operator found in languages like F#, Elixir and Elm. You can pack as many expressions as you like and they will be applied in order to the data that is passed through them when compiled an excecuted.

In general, the following rules apply for Seq:

General Sequence

Seq(f0, f1, ..., fn-1, fn)

is equivalent to

lambda x: fn(fn-1(...(f1(f0(x)))))

Single Function

Seq(f)

is equivalent to

f

Identity

The empty Seq

Seq()

is equivalent to

lambda x: x

Examples

from phi import P, Seq

f = Seq(
    P * 2,
    P + 1,
    P ** 2
)

assert f(1) == 9 # ((1 * 2) + 1) ** 2

The previous example using P.Pipe

from phi import P

assert 9 == P.Pipe(
    1,
    P * 2,  #1 * 2 == 2
    P + 1,  #2 + 1 == 3
    P ** 2  #3 ** 2 == 9
)
def Seq(self, *sequence, **kwargs):
    """
` is used to express function composition. The expression
Seq(f, g)
quivalent to
lambda x: g(f(x))
ou see, its a little different from the mathematical definition. Excecution order flow from left to right, this makes reading and reasoning about code way more easy. This bahaviour is based upon the `|>` (pipe) operator found in languages like F#, Elixir and Elm. You can pack as many expressions as you like and they will be applied in order to the data that is passed through them when compiled an excecuted.
eneral, the following rules apply for Seq:
neral Sequence**
Seq(f0, f1, ..., fn-1, fn)
quivalent to
lambda x: fn(fn-1(...(f1(f0(x)))))
ngle Function**
Seq(f)
quivalent to
f
entity**
empty Seq
Seq()
quivalent to
lambda x: x
Examples
from phi import P, Seq
f = Seq(
    P * 2,
    P + 1,
    P ** 2
)
assert f(1) == 9 # ((1 * 2) + 1) ** 2
previous example using `P.Pipe`
from phi import P
assert 9 == P.Pipe(
    1,
    P * 2,  #1 * 2 == 2
    P + 1,  #2 + 1 == 3
    P ** 2  #3 ** 2 == 9
)
    """
    fs = [ _parse(elem)._f for elem in sequence ]
    def g(x, state):
        return functools.reduce(lambda args, f: f(*args), fs, (x, state))
    return self.__then__(g, **kwargs)

def Then(

self, f, *args, **kwargs)

Then(f, ...) is equivalent to ThenAt(1, f, ...). Checkout phi.builder.Builder.ThenAt for more information.

def Then(self, f, *args, **kwargs):
    """
n(f, ...)` is equivalent to `ThenAt(1, f, ...)`. Checkout `phi.builder.Builder.ThenAt` for more information.
    """
    return self.ThenAt(1, f, *args, **kwargs)

def Then0(

self, f, *args, **kwargs)

Then0(f, ...) is equivalent to ThenAt(0, f, ...). Checkout phi.builder.Builder.ThenAt for more information.

def Then0(self, f, *args, **kwargs):
    """
n0(f, ...)` is equivalent to `ThenAt(0, f, ...)`. Checkout `phi.builder.Builder.ThenAt` for more information.
    """
    return self.ThenAt(0, f, *args, **kwargs)

def Then1(

self, f, *args, **kwargs)

Then(f, ...) is equivalent to ThenAt(1, f, ...). Checkout phi.builder.Builder.ThenAt for more information.

def Then(self, f, *args, **kwargs):
    """
n(f, ...)` is equivalent to `ThenAt(1, f, ...)`. Checkout `phi.builder.Builder.ThenAt` for more information.
    """
    return self.ThenAt(1, f, *args, **kwargs)

def Then2(

self, f, arg1, *args, **kwargs)

Then2(f, ...) is equivalent to ThenAt(2, f, ...). Checkout phi.builder.Builder.ThenAt for more information.

def Then2(self, f, arg1, *args, **kwargs):
    """
n2(f, ...)` is equivalent to `ThenAt(2, f, ...)`. Checkout `phi.builder.Builder.ThenAt` for more information.
    """
    args = (arg1,) + args
    return self.ThenAt(2, f, *args, **kwargs)

def Then3(

self, f, arg1, arg2, *args, **kwargs)

Then3(f, ...) is equivalent to ThenAt(3, f, ...). Checkout phi.builder.Builder.ThenAt for more information.

def Then3(self, f, arg1, arg2, *args, **kwargs):
    """
n3(f, ...)` is equivalent to `ThenAt(3, f, ...)`. Checkout `phi.builder.Builder.ThenAt` for more information.
    """
    args = (arg1, arg2) + args
    return self.ThenAt(3, f, *args, **kwargs)

def Then4(

self, f, arg1, arg2, arg3, *args, **kwargs)

Then4(f, ...) is equivalent to ThenAt(4, f, ...). Checkout phi.builder.Builder.ThenAt for more information.

def Then4(self, f, arg1, arg2, arg3, *args, **kwargs):
    """
n4(f, ...)` is equivalent to `ThenAt(4, f, ...)`. Checkout `phi.builder.Builder.ThenAt` for more information.
    """
    args = (arg1, arg2, arg3) + args
    return self.ThenAt(4, f, *args, **kwargs)

def Then5(

self, f, arg1, arg2, arg3, arg4, *args, **kwargs)

Then5(f, ...) is equivalent to ThenAt(5, f, ...). Checkout phi.builder.Builder.ThenAt for more information.

def Then5(self, f, arg1, arg2, arg3, arg4, *args, **kwargs):
    """
n5(f, ...)` is equivalent to `ThenAt(5, f, ...)`. Checkout `phi.builder.Builder.ThenAt` for more information.
    """
    args = (arg1, arg2, arg3, arg4) + args
    return self.ThenAt(5, f, *args, **kwargs)

def ThenAt(

self, n, f, *_args, **kwargs)

ThenAt enables you to create a partially apply many arguments to a function, the returned partial expects a single arguments which will be applied at the nth position of the original function.

Arguments

  • n: position at which the created partial will apply its awaited argument on the original function.
  • f: function which the partial will be created.
  • _args & kwargs: all *_args and **kwargs will be passed to the function f.
  • _return_type = None: type of the returned builder, if None it will return the same type of the current builder. This special kwarg will NOT be passed to f.

You can think of n as the position that the value being piped down will pass through the f. Say you have the following expression

D == fun(A, B, C)

all the following are equivalent

from phi import P, Pipe, ThenAt

D == Pipe(A, ThenAt(1, fun, B, C))
D == Pipe(B, ThenAt(2, fun, A, C))
D == Pipe(C, ThenAt(3, fun, A, B))

you could also use the shortcuts Then, Then2,..., Then5, which are more readable

from phi import P, Pipe

D == Pipe(A, P.Then(fun, B, C))
D == Pipe(B, P.Then2(fun, A, C))
D == Pipe(C, P.Then3(fun, A, B))

There is a special case not discussed above: n = 0. When this happens only the arguments given will be applied to f, this method it will return a partial that expects a single argument but completely ignores it

from phi import P

D == Pipe(None, P.ThenAt(0, fun, A, B, C))
D == Pipe(None, P.Then0(fun, A, B, C))

Examples

Max of 6 and the argument:

from phi import P

assert 6 == P.Pipe(
    2,
    P.Then(max, 6)
)

Previous is equivalent to

assert 6 == max(2, 6)

Open a file in read mode ('r')

from phi import P

f = P.Pipe(
    "file.txt",
    P.Then(open, 'r')
)

Previous is equivalent to

f = open("file.txt", 'r')

Split a string by whitespace and then get the length of each word

from phi import P

assert [5, 5, 5] == P.Pipe(
    "Again hello world",
    P.Then(str.split, ' ')
    .Then2(map, len)
)

Previous is equivalent to

x = "Again hello world"

x = str.split(x, ' ')
x = map(len, x)

assert [5, 5, 5] == x

As you see, Then2 was very useful because map accepts and iterable as its 2nd parameter. You can rewrite the previous using the PythonBuilder and the phi.builder.Builder.Obj object

from phi import P, Obj

assert [5, 5, 5] == P.Pipe(
    "Again hello world",
    Obj.split(' '),
    P.map(len)
)

Also see

  • phi.builder.Builder.Obj
  • PythonBuilder
  • phi.builder.Builder.RegisterAt
def ThenAt(self, n, f, *_args, **kwargs):
    """
nAt` enables you to create a partially apply many arguments to a function, the returned partial expects a single arguments which will be applied at the `n`th position of the original function.
guments**
n**: position at which the created partial will apply its awaited argument on the original function.
f**: function which the partial will be created.
_args & kwargs**: all `*_args` and `**kwargs` will be passed to the function `f`.
return_type = None`: type of the returned `builder`, if `None` it will return the same type of the current `builder`. This special kwarg will NOT be passed to `f`.
can think of `n` as the position that the value being piped down will pass through the `f`. Say you have the following expression
D == fun(A, B, C)
the following are equivalent
from phi import P, Pipe, ThenAt
D == Pipe(A, ThenAt(1, fun, B, C))
D == Pipe(B, ThenAt(2, fun, A, C))
D == Pipe(C, ThenAt(3, fun, A, B))
could also use the shortcuts `Then`, `Then2`,..., `Then5`, which are more readable
from phi import P, Pipe
D == Pipe(A, P.Then(fun, B, C))
D == Pipe(B, P.Then2(fun, A, C))
D == Pipe(C, P.Then3(fun, A, B))
e is a special case not discussed above: `n = 0`. When this happens only the arguments given will be applied to `f`, this method it will return a partial that expects a single argument but completely ignores it
from phi import P
D == Pipe(None, P.ThenAt(0, fun, A, B, C))
D == Pipe(None, P.Then0(fun, A, B, C))
amples**
of 6 and the argument:
from phi import P
assert 6 == P.Pipe(
    2,
    P.Then(max, 6)
)
ious is equivalent to
assert 6 == max(2, 6)
 a file in read mode (`'r'`)
from phi import P
f = P.Pipe(
    "file.txt",
    P.Then(open, 'r')
)
ious is equivalent to
f = open("file.txt", 'r')
t a string by whitespace and then get the length of each word
from phi import P
assert [5, 5, 5] == P.Pipe(
    "Again hello world",
    P.Then(str.split, ' ')
    .Then2(map, len)
)
ious is equivalent to
x = "Again hello world"
x = str.split(x, ' ')
x = map(len, x)
assert [5, 5, 5] == x
ou see, `Then2` was very useful because `map` accepts and `iterable` as its `2nd` parameter. You can rewrite the previous using the [PythonBuilder](https://cgarciae.github.io/phi/python_builder.m.html) and the `phi.builder.Builder.Obj` object
from phi import P, Obj
assert [5, 5, 5] == P.Pipe(
    "Again hello world",
    Obj.split(' '),
    P.map(len)
)
so see**
hi.builder.Builder.Obj`
ythonBuilder](https://cgarciae.github.io/phi/python_builder.m.html)
hi.builder.Builder.RegisterAt`
    """
    _return_type = None
    n_args = n - 1
    if '_return_type' in kwargs:
        _return_type = kwargs['_return_type']
        del kwargs['_return_type']
    @utils.lift
    def g(x):
        new_args = _args[0:n_args] + (x,) + _args[n_args:] if n_args >= 0 else _args
        return f(*new_args, **kwargs)
    return self.__then__(g, _return_type=_return_type)

def Val(

self, val, **kwargs)

The expression

Val(a)

is equivalent to the constant function

lambda x: a

All expression in this module interprete values that are not functions as constant functions using Val, for example

Seq(1, P + 1)

is equivalent to

Seq(Val(1), P + 1)

The previous expression as a whole is a constant function since it will return 2 no matter what input you give it.

def Val(self, val, **kwargs):
    """
expression
Val(a)
quivalent to the constant function
lambda x: a
expression in this module interprete values that are not functions as constant functions using `Val`, for example
Seq(1, P + 1)
quivalent to
Seq(Val(1), P + 1)
previous expression as a whole is a constant function since it will return `2` no matter what input you give it.
    """
    f = utils.lift(lambda z: val)
    return self.__then__(f, **kwargs)

def With(

self, context_manager, *body, **kwargs)

With

def With(context_manager, *body):

Arguments

  • context_manager: a context manager object or valid expression from the DSL that returns a context manager.
  • *body: any valid expression of the DSL to be evaluated inside the context. *body is interpreted as a tuple so all expression contained are composed.

As with normal python programs you sometimes might want to create a context for a block of code. You normally give a context manager to the with statemente, in Phi you use P.With or phi.With

Context

Python's with statemente returns a context object through as keyword, in the DSL this object can be obtained using the P.Context method or the phi.Context function.

Examples

from phi import P, Obj, Context, With, Pipe

text = Pipe(
    "text.txt",
    With( open, Context,
        Obj.read()
    )
)

The previous is equivalent to

with open("text.txt") as f:
    text = f.read()
def With(self, context_manager, *body, **kwargs):
    """
th**
def With(context_manager, *body):
guments**
context_manager**: a [context manager](https://docs.python.org/2/reference/datamodel.html#context-managers) object or valid expression from the DSL that returns a context manager.
*body**: any valid expression of the DSL to be evaluated inside the context. `*body` is interpreted as a tuple so all expression contained are composed.
ith normal python programs you sometimes might want to create a context for a block of code. You normally give a [context manager](https://docs.python.org/2/reference/datamodel.html#context-managers) to the [with](https://docs.python.org/2/reference/compound_stmts.html#the-with-statement) statemente, in Phi you use `P.With` or `phi.With`
ntext**
on's `with` statemente returns a context object through `as` keyword, in the DSL this object can be obtained using the `P.Context` method or the `phi.Context` function.
Examples
from phi import P, Obj, Context, With, Pipe
text = Pipe(
    "text.txt",
    With( open, Context,
        Obj.read()
    )
)
previous is equivalent to
with open("text.txt") as f:
    text = f.read()
    """
    context_f = _parse(context_manager)._f
    body_f = E.Seq(*body)._f
    def g(x, state):
        context, state = context_f(x, state)
        with context as scope:
            with _WithContextManager(scope):
                return body_f(x, state)
    return self.__then__(g, **kwargs)

def Write(

self, *state_args, **state_dict)

See phi.dsl.Expression.Read

def Write(self, *state_args, **state_dict):
    """See `phi.dsl.Expression.Read`"""
    if len(state_dict) + len(state_args) < 1:
        raise Exception("Please include at-least 1 state variable, got {0} and {1}".format(state_args, state_dict))
    if len(state_dict) > 1:
        raise Exception("Please include at-most 1 keyword argument expression, got {0}".format(state_dict))
    if len(state_dict) > 0:
        state_key = next(iter(state_dict.keys()))
        write_expr = state_dict[state_key]
        state_args += (state_key,)
        expr = self >> write_expr
    else:
        expr = self
    def g(x, state):
        update = { key: x for key in state_args }
        state = utils.merge(state, update)
        #side effect for convenience
        _StateContextManager.REFS.update(state)
        return x, state
    return expr.__then__(g)