The Either Monad

hymn.types.either - the either monad

class hymn.types.either.Either(value)

Bases: hymn.types.monadplus.MonadPlus, hymn.mixins.Ord

the either monad

computation with two possibilities

bind(f)

the bind operation of Either

apply function to the value if and only if this is a Right.

classmethod from_value(value)

wrap value in an Either monad

return a Right if the value is evaluated as true. Left otherwise.

plus(other)

the associative operation

unit

alias of Right

class hymn.types.either.Left(value)

Bases: hymn.types.either.Either

left of Either

plus(other)

the associative operation

class hymn.types.either.Right(value)

Bases: hymn.types.either.Either

right of Either

hymn.types.either.either(handle_left, handle_right, m)

case analysis for Either

apply either handle-left or handle-right to m depending on the type of it, raise TypeError if m is not an Either

hymn.types.either.either_m

alias of hymn.types.either.Either

hymn.types.either.failsafe(func)

decorator to turn func into monadic function of Either monad

hymn.types.either.is_left(m)

return True if m is a Left

hymn.types.either.is_right(m)

return True if m is a Right

hymn.types.either.unit

alias of hymn.types.either.Right

hymn.types.either.zero = Left('unknown error')

left of Either

hymn.types.either.to_either()

alias of from_value()

Hy Specific API

either-m

alias of Either

Tag Macro

| [f]

turn f into monadic function with failsafe()

Functions

->either
to-either

alias of Either.from_value()

left?

alias of is_left()

right?

alias of is_right()

Examples

Comparison

Either are comparable if the wrapped values are comparable. Right is greater than Left in any case.

=> (import [hymn.types.either [Left Right]])
=> (> (Right 2) (Right 1))
True
=> (< (Left 2) (Left 1))
False
=> (> (Left 2) (Right 1))
False

Do Notation

=> (import [hymn.types.either [Left Right]])
=> (require [hymn.macros [do-monad-return]])
=> (do-monad-return [a (Right 1) b (Right 2)] (+ a b))
Right(3)
=> (do-monad-return [a (Left 1) b (Right 2)] (+ a b))
Left(1)

Do Notation with :when

=> (import [hymn.types.either [either-m]])
=> (require [hymn.macros [do-monad-with]])
=> (defn safe-div [a b]
...    (do-monad-with either-m [:when (not (zero? b))] (/ a b)))
=> (safe-div 1 2)
Right(0.5)
=> (safe-div 1 0)
Left('unknown error')

Operations

Use ->either to create an Either from a value

=> (import [hymn.types.either [->either]])
=> (->either 42)
Right(42)
=> (->either None)
Left(None)

Use left?() and right?() to test the type

=> (import [hymn.types.either [Left Right left? right?]])
=> (right? (Right 42))
True
=> (left? (Left None))
True

either() applies function to value in the monad depending on the type

=> (import [hymn.types.either [Left Right either]])
=> (either print inc (Left 1))
1
=> (either print inc (Right 1))
2

failsafe() turns function into monadic one

=> (import [hymn.types.either [failsafe]])
=> (with-decorator failsafe (defn add1 [n] (inc (int n))))
=> (add1 "41")
Right(42)
=> (add1 "nan")
Left(ValueError("invalid literal for int() with base 10: 'nan'",))
=> (setv safe-div (failsafe /))
=> (safe-div 1 2)
Right(0.5)
=> (safe-div 1 0)
Left(ZeroDivisionError('division by zero',))

Tag Macro

=> (require [hymn.types.either [|]])
=> (#| int "42")
Right(42)
=> (#| int "nan")
Left(ValueError("invalid literal for int() with base 10: 'nan'",))
=> (setv safe-div #| /)
=> (safe-div 1 2)
Right(0.5)
=> (safe-div 1 0)
Left(ZeroDivisionError('division by zero',))