haskpy.types.function.Function

Function

class Function(Function__f, Function__args=())[source]

Bases: Monad, Cartesian, Cocartesian, Semigroup

Monad instance for curried functions :: a -> b

The function is curried in such a way that the function can be applied one argument at a time, all arguments at the same time or the arguments divided arbitrarily among multiple calls. To illustrate this, consider the following two-argument function that returns another two-argument function:

@function
def foo(a, b):

    @function
    def bar(c, d):
        return a + b + c + d

    return bar

This is effectively a four-argument function for which any of the following examples is a valid way of calling it:

>>> foo("a")("b")("c")("d")
'abcd'
>>> foo("a", "b", "c", "d")
'abcd'
>>> foo("a")("b", "c")("d")
'abcd'

Function composition is equivalent to functorial mapping over a function. Therefore, functions can be composed at least in any of the following three ways:

>>> import haskpy as hp
>>> f = hp.function(lambda x: x + 3)
>>> g = lambda x: 2 * x
>>> h = lambda x: x ** 2
>>> (h ** g ** f)(2)
100
>>> hp.map(h, hp.map(g, f))(2)
100
>>> f.map(g).map(h)(2)
100

Here, ** for functions (or functors) means composing (or functorial mapping).

Note that Function objects accept only positional arguments. The number of those positional arguments is exactly the same as the number of required positional arguments in the underlying function. Any optional positional or keyword arguments become unusable. The reason for this is that it must be unambiguous at which point, for instance, map is applied. Also, f(a)(b) should always be equal to f(a, b) with curried functions. This might not be the case if there are optional arguments.

Note

Monoid instance of Function requires the knowledge of the contained monoid type in order to be able to create empty. The contained type is not known because Function class can be used to create functions of any type. This is just convenience and simpler user interface. If you need Monoid instance of Function, use FunctionMonoid function to create such a class. Note though that the Semigroup instance is available in this Function without needing to use FunctionMonoid.

__add__(other)

Append two monoids

Using + operator to append two monoid values seems natural because that’s what Python is doing by default because lists are concatenated with +.

__attrs_post_init__()[source]
__call__(*args)[source]

Call self as a function.

property __code__
__eq_test__(g, data, input_strategy=integers())[source]
__get__(obj, objtype)[source]

Support instance methods.

See: https://stackoverflow.com/a/3296318

__lshift__(x)

Sequence with << similarly as with <* and << in Haskell

__matmul__(x)

Application operand @ applies similarly as <*> in Haskell

f @ x translates to f.apply_to(x), x.apply(f) and apply(f, x).

Why @ operator?

  • It’s not typically used as often as some other more common operators so less risk for confusion.

  • The operator is not a commutative as isn’t apply either.

  • If we see matrix as some structure, then matrix multiplication takes both left and right operand inside this structure and gives a result also inside this structure, similarly as apply does. So it’s an operator for two operands having a similar structure.

  • The operator evaluates the contained function(s) at the contained value(s). Thus, f “at” x makes perfect sense.

__mod__(f)

Use % as bind operator similarly as >>= in Haskell

That is, x % f is equivalent to bind(x, f) and x.bind(f).

Why % operator?

  • It’s not very often used so less risk for confusion.

  • It’s not commutative as isn’t bind either.

  • It is similar to bind in a sense that the result has the same unit as the left operand while the right operand has different unit.

  • The symbol works visually as a line “binds” two circles and on the other hand two circles tell about two similar structures on both sides but those structures are just on different “level”.

__pow__(x)[source]
__rpow__(f)

Lifting operator ** lifts similarly as <$> in Haskell

f ** x translates to x.map(f) and map(f, x).

Why ** operator?

  • It’s not typically used as often as multiplication or addition so less risk of confusion.

  • It’s not commutative operator as isn’t lifting either.

  • The two operands have very different roles. They are not at the same “level”.

  • The right operand is “higher”, that is, it’s inside a structure and the left operand is kind of “raised to the power” of the second operand, where the “power” is the functorial structure.

  • The same operand is also used for function composition because function composition is just mapping. Visually the symbol can be seen as chaining two stars similarly as function composition chains two functions.

__rshift__(x)

Sequence with >> similarly as with *> and >> in Haskell

property __signature__
append(g)[source]

(a -> b) -> (a -> b) -> (a -> b)

apply(g)[source]

(a -> b) -> (a -> b -> c) -> a -> c

apply_first(x)

Combine two actions, keeping only the result of the first

Apply f => f a -> f b -> f a
apply_second(x)

Combine two actions, keeping only the result of the second

Apply f => f a -> f b -> f b
apply_to(x)

f (a -> b) -> f a -> f b

Default implementation is based on apply.

bind(g)[source]

(a -> b) -> (b -> a -> c) -> a -> c

check_args(attribute, value)[source]
check_f(attribute, value)[source]
contramap(g)[source]

(b -> c) -> (a -> b) -> (a -> c)

contrareplace(x)

f b -> b -> f a

dimap(g, h)[source]

(b -> c) -> (a -> b) -> (c -> d) -> (a -> d)

first()[source]

(a -> b) -> (a, c) -> (b, c)

flap(x)

Functor f => f (a -> b) -> a - > f b

join()

m (m a) -> m a

Default implementation is based on bind:

self :: m (m a)

identity :: m a -> m a

bind :: m (m a) -> (m a -> m a) -> m a

left()[source]

(a -> b) -> Either a c -> Either b c

map(g)[source]

(a -> b) -> (b -> c) -> (a -> c)

pure(x)[source]

a -> m a

replace(x)

Haskell ($>) operator

right()[source]

(a -> b) -> Either c a -> Either c b

second()[source]

(a -> b) -> (c, a) -> (c, b)