Allow natural function notations like (1,2) *dot* (3,4) for dot((1,2), (3,4)) or 1 /frac/ 3 for Fraction(1,3), pipes and other useful operators to functions.

## Project description

Always wanted to add custom operators to your functions ?

```
a = 2 + (1,2,3) /dot/ (4,5,6) # a = 2 + dot((1,2,3), (4,5,6))
Y = [1,2,7,0,2,0] |no_zero |plus(1) |to(set) # Y == {2,3,8}
square = elipartial(pow, ..., 2) # square = lambda x: pow(x, 2)
display = hex *compose* ord # display = lambda x: hex(ord(x))
```

This example shows how infix operators can be created,
the library also introduces bash like *pipes* and
shortcuts to create partial functions or function
composition inspired by functional languages.

# Using infix

Infix operators can be created using the `infix`

class.

It works for existing functions, like `numpy.dot`

:

```
import numpy
dot = infix(numpy.dot)
a = (1,2,3) /dot/ (4,5,6) # use as an infix
```

If you already have `dot`

in your namespace, don't worry, it still works as a function:

```
a = dot((1,2,3), (4,5,6)) # still works as a function
```

Or for custom functions as a decorator:

```
@infix
def crunch(x,y):
"""
Do a super crunchy operation between two numbers.
"""
return x + 2 * y
a = 1 |crunch| 2 # a = crunch(1, 2)
a = crunch(1, 2) # still works
help(crunch.function) # to get help about the initial function
```

Any binary operator can be used, `1 |f| 2`

can be written `1 *f* 2`

, `1 %f% 2`

or `1 << f << 2`

but `/`

or `|`

should be clean for all use cases.
Beware if you use `**`

, the operator is right to left:

```
b = 1 **f** 2 # a = f(2, 1)
```

# Useful for dot and cross product

Dot and cross products are used heavily in mathematics and physics as an infix operator `·`

or `×`

.

```
import numpy
dot = infix(numpy.dot)
a = (1,2,3) /dot/ (4,5,6)
a = (1,2,3) |dot| (4,5,6) # same
r = 2 + (1,2,3) /dot/ (4,5,6) # here "/" has priority over "+" like in normal python
r = 2 + (1,2,3) *dot* (4,5,6) # for a dot PRODUCT, "*" seems logical
r = 2 + dot((1,2,3), (4,5,6)) # still works as a function
cross = infix(numpy.cross)
tau = (1,2) /cross/ (3,4)
Z = (1,2,3) /cross/ (4,5,6)
```

# Using `|`

for low priority

In some use cases, one want to mix classic operators with function operators,
the `|`

operator may be used as a low priority operator.

```
Y = A + B |dot| C # is parsed as Y = (A + B) |dot| C
Y = A + B /dot/ C # is parsed as Y = A + (B /dot/ C)
```

# Useful for fractions

When using the `fractions`

module, often you want to transition from `float`

to `Fraction`

.
Your current code uses `/`

for division and you can just replace the slashes with `/frac/`

, the expression stays natural to read.

```
from fractions import Fraction
frac = infix(Fraction)
a = 1 + 1 / 3 # 1.3333...
a = 1 + 1 /frac/ 3 # Fraction(4, 3)
b = 2 * Fraction(a + 3, a + 1) # very different from '(a + 3) / (a + 1)'
b = 2 * (a + 3) /frac/ (a + 1) # almost identical to '(a + 3) / (a + 1)'
```

# Useful for ranges, do you like `2..5`

in ruby?

In many languages, iterating over a range has a notational shortcut, like `2..5`

in ruby.
Now you can even write `for i in 1 /inclusive/ 5`

in python.

```
@infix
def inclusive(a,b):
return range(a, b+1)
for i in 2 /inclusive/ 5:
print(i) # 2 3 4 5
for i in inclusive(2, 5):
print(i) # 2 3 4 5
```

However, redefining `range = infix(range)`

is a bad idea because it would break code like `isinstance(x, range)`

.
In that particuliar example, I would choose `exclusive = infix(range)`

.

# Useful for isinstance, do you like `instanceof`

in Java and Js?

In Java and Javascript, testing the class of an object is done via `x instanceof Class`

,
the python builtin `isinstance`

could be enhanced with infix notation or be renamed to `instanceof`

.

```
isinstance = infix(isinstance)
assert 1 /isinstance/ int
assert [] /isinstance/ (list, tuple)
assert 1 / 2 |isinstance| float
```

# Useful for pipes: postfix (alias to)

In bash, a functionality called *pipes* is useful to reuse an expression and change the behavior by just adding code *at the end*.
The library can be used for that.

```
@postfix
def no_zero(L):
return [x for x in L if x != 0]
@postfix
def plus_one(L):
return [x+1 for x in L]
Y = [1,2,7,0,2,0] |no_zero |plus_one # Y == [2,3,8,3]
Y = plus_one(no_zero([1,2,7,0,2,0])) # Y == [2,3,8,3]
```

Using `to = postfix`

makes it quite readable in a pipe.

```
>>> from funcoperators import postfix as to # funcoperators version 0.x
>>> from funcoperators import to # funcoperators version 1.x
>>> 'hello' |to(str.upper) |to(lambda x:x + '!') |to('I said "{}"'.format) |to(print)
I said "HELLO!"
>>> print('I said "{}"'.format('hello'.upper() + '!'))
I said "HELLO!"
```

# Pipes with arguments: pipe factory

Sometimes, pipes want extra information, for example in our last example, `no_zero`

is a special case of a pipe that filters out a value,
use the *pipe factory* recipe like so:

```
def filter_out(x):
@postfix
def f(L):
return [y for y in L if y != x]
return f
# shorter with a lambda
def filter_out(x):
return postfix(lambda L:[y for y in L if y != x])
L = [1,2,7,0,2,0] | filter_out(0) # L == [2,3,8,3]
from funcoperators import mapwith
s = '1 2 7 2'.split() | mapwith(int) | to(sum) # s = 12 = sum(map(int, '1 2 7 2'.split()))
from funcoperators import mapwith as Map
from funcoperators import filterwith as Filter
S = '1 2 7 2'.split() | Map(int) | Filter(lambda x:x < 5) | to(set)
```

# Useful for format and join

```
>>> 42 | format /curryright/ 'x'
'2a'
>>> formatwith = lambda fmt: postfix(lambda value: format(value, fmt))
>>> 52 |formatwith('x')
'2a'
>>> from funcoperators import to
>>> 3.1415 |to('{:.02}'.format)
'3.1'
>>> [1, 2, 7, 2] |mapwith(str) |to('/'.join) |to(print)
1/2/7/2
```

# Function composition (compose, alias circle)

In mathematics and functional programming languages, function composition is naturally used using a `circle`

operator to write things like `h = f ∘ g`

.

```
s = hex(ord('A')) # s = '0x41'
from funcoperators import compose
display = hex /compose/ ord
s = display('A') # s = '0x41'
display = hex *circle* ord
from funcoperators import compose as o
display = hex -o- ord # looks like a dot
```

`compose`

can have more than two functions.

```
f = compose(str.upper, hex, lambda x:x+1, ord) # simple function
f = str.upper /compose/ hex /compose/ (lambda x:x+1) /compose/ ord # operator
```

## Using call for inline compose

```
>>> print(5 + 2 * 10, 'B', sep='/')
25
>>> print |call(5 + 2 * 10, 'B', sep='/')
25/B
>>> print(','.join('abcdef' + 3 * 'x'))
a,b,c,d,e,f,x,x,x
>>> print *compose* ' '.join |call('abcdef' + 3 * 'x')
a,b,c,d,e,f,x,x,x
>>> compose(print, ','.join) |call('abcdef' + 3 * 'x')
a,b,c,d,e,f,x,x,x
```

```
>>> len |infixcall| 'hallo' * 3
15
```

```
>>> print |callargs| ('a', 5)
a 5
```

# More partial syntax

The library adds sugar to functools.partial, using functions called `curry`

(and variants like `curryright`

, `simplecurry`

) and `partially`

. The name `curry`

comes from other languages.

```
def f(x,y,z):
return x + y + z
from funcoperators import curry
g = f /curry/ 5
y = f(2,1) # y = 8
from funcoperators import curryright
square = pow /curryright/ 2 # square(x) = x ** 2
square = curryright(pow, 2) # square(x) = x ** 2
from funcoperators import provide_right # alias provide_right = curryright
square = provide_right(pow, 2) # square(x) = x ** 2
square = pow /provide_right/ 2 # square(x) = x ** 2
from funcoperators import simplecurry
g = f |simplecurry(1, z=3)
y = g(2)
```

`partially`

allows to *upgrade* a function to provide methods like `f.partial`

and provides `f[arg]`

to curry.

```
from funcoperators import partially
@partially
def f(x,y,z):
return x - y + 2 * z
r = f(1,2,3)
g = f[1] # g = a function with two arguments: y,z
r = g(2,3)
r = f[1](2,3)
r = f[1][2][3]()
# Notice that "f[1,2]" doesn't work because it gives only one argument: a tuple (@see partiallymulti)
g = f[1] # gives positional arguments
g = f.val(1) # gives positional arguments
g = f.key(z=3) # gives keyword arguments
g = f.partial(1, z=3) # gives positional and keyword arguments
# alias part = assuming = given = where = partial
g = f.part(1, z=3)
g = f.where(1, z=3)
g = f.given(1, z=3)
```

`partiallymulti`

allows `f[arg1, arg2]`

.

```
from funcoperators import partiallymulti
@partiallymulti
def f(x,y,z):
return x - y + 2 * z
r = f(1,2,3)
g = f[1,2] # g = a function with one argument: z
r = g(3)
```

# Using partiallyauto

In functional languages, function composition is sometimes not dissociable from function call,
`partiallyauto`

only works for methods with N fixed positional arguments.

```
@partiallyauto
def f(x,y,z):
return x - y + 2 * z
r = f(1,2,3) # r = 6
r = f(1)(2)(3) # r = 6
r = f(1)(2,3) # r = 6
g = f(1) # g = a function with two arguments
r = g(2,3) # r = 6
k = g(2) # k = a function with one argument
```

# Using Ellipsis

Python's `functools.partial`

only works for arguments that will be provided later, one must use keywords arguments.
However, not all functions accept keywords arguments, like the builtin `pow`

, one can use `curryright`

because pow only has two arguments.

```
square = curryright(pow, 2) # square(x) = x ** 2
```

The library also proposes to use Python's `...`

(`Ellipsis`

) as a natural placeholder for arguments.
The functions using this convention have a name beginning with `eli`

.

```
tenexp = elipartial(pow, 10) # = pow(10, something)
y = tenexp(2) # 10 ** 2
square = elipartial(pow, ..., 2) # = pow(something, 2)
y = square(5) # 5 ** 2
square = pow |elicurry(..., 2) # = pow(something, 2)
y = square(5) # 5 ** 2
```

If you like the `partially`

and `partiallymulti`

syntax, there is `bracket`

that has all the concepts in one class.

```
@bracket
def f(x,y,z):
return x - y + 2 * z
r = f(1,2,3)
g = f[1, ..., 3] # g = a function with one argument: y
r = g(2)
g = f.partial(1, ..., 3) # as a method
g = f.partial(1, z=3) # allowing keyword arguments
```

Here is a more complex example using `elicurry`

, we define `show`

to be the `print`

function with arguments `1`

, *something*, `3`

and keyword argument `sep='/'`

.

```
show = print |elicurry(1, ..., 3, sep='/')
show(2) # prints 1/2/3
```

Let's note that `elicurry`

has many aliases:

```
show = print |elicurry(1, ..., 3, sep='/')
show = print |with_arguments(1, ..., 3, sep='/')
show = print |deferredcall(1, ..., 3, sep='/')
show = print |latercall(1, ..., 3, sep='/')
```

# More examples

See more examples in the test cases in source code.

# Release Notes

Version 1.0 created some non backward-compatible change (`call`

) and included useful use cases (`to`

, `mapwith`

)

## Project details

## Release history Release notifications | RSS feed

## Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.