3.2 The Base Classes
3.2.1 Applicative Functor
(require algebraic/control/applicative) | package: algebraic |
A functor with application, providing operations to
class
Further, any definition must satisfy the following:
identity
composition
(<*> (<*> (<*> (pure ..) u) v) w) = (<*> u (<*> v w))
homomorphism
(<*> (pure f) (pure x)) = (pure (f x))
interchange
(<*> u (pure y)) = (<*> (pure (>> $ y)) u)
The other members have the following default definitions, which may be overridden with equivalent specialized implementations:
As a consequence of these laws, the functor instance for f will satisfy
It may be useful to note that supposing, for all x and y,
(>> p (q x y)) = (.. (>> f x) (>> g y))
it follows from the above that
(>> liftA2 p (liftA2 q u v)) = (.. (>> liftA2 f u) (>> liftA2 g v))
If f is also a Monad, it should satisfy
(which implies that pure and <*> satisfy the applicative functor laws).
3.2.1.1 Members
Minimal instance: pure, (<*> or liftA2)
class member
class member
procedure
(liftA2 f a b) → applicative
f : procedure? a : applicative b : applicative
Some functors support an implementation of liftA2 that is more efficient than the default one. In particular, if fmap is an expensive operation, it is likely better to use liftA2 than to fmap over the structure and then use <*>.
procedure
(*> a b) → applicative
a : applicative b : applicative
This is essentially the same as (>> liftA2 (flip const)), but if the functor instance has an optimized <$, it may be better to use that instead.
procedure
(<* a b) → applicative
a : applicative b : applicative
3.2.1.2 Helpers
procedure
(<**> a f b) → applicative
a : applicative f : applicative b : applicative
procedure
(<*>~ f a) → applicative
f : applicative a : applicative
Eager applicative chains get noisy as they grow because Racket functions must be curried explicitly.
> (with-instance box-applicative (<*> (pure +) (pure 1)) (<*> (<*> (pure (>>* +)) (pure 1)) (pure 2)) (<*> (<*> (<*> (pure (>>* (>>* +))) (pure 1)) (pure 2)) (pure 3))) '#&6
The function (in this case, +) must be wrapped in one >>* for each applicative argument or <*> will fail.
This variant wraps the function with >>*. Unless lazy semantics are desired, make the final operator in the chain eager (e.g., <*>) to evaluate the whole chain by force.
> (with-instance box-applicative (<*> (pure +) (pure 1)) (<*> (<*>~ (pure +) (pure 1)) (pure 2)) (<*> (<*>~ (<*>~ (pure +) (pure 1)) (pure 2)) (pure 3))) '#&6
procedure
(<**>~ a f b) → applicative
a : applicative f : applicative b : applicative
3.2.2 Functor
(require algebraic/data/functor) | package: algebraic |
The functor class is used for types that can be mapped over.
class
3.2.2.1 Members
Minimal instance: fmap
procedure
f : procedure? a : functor
3.2.2.2 Helpers
procedure
f : procedure? a : functor
The name of this operator is an allusion to $. Whereas $ is function application, <$> is function application lifted over a functor.
Examples:
Convert from a Maybe Int to a Maybe String using ~a:
> (with-instance maybe-functor (<$> ~a Nothing)) Nothing
> (with-instance maybe-functor (<$> ~a (Just 3))) (Just "3")
Double each element of a list:
> (with-instance list-functor (<$> (>> * 2) '(1 2 3))) '(2 4 6)
procedure
f : procedure? a : functor
3.2.3 Monad
(require algebraic/control/monad) | package: algebraic |
Basic operations on monads, and a generic do-notation.
class
Monads and Applicatives should relate as follows:
The above laws imply:
and that pure and <*> satisfy the applicative functor laws.
3.2.3.1 Members
Minimal instance: >>=
3.2.3.2 Helpers
3.2.3.3 Do-Notations
syntax
(do do-expr ... expr)
syntax
(do~ do-expr ... expr)
do-expr = formals <- monad-expr | let id expr | let-values formals expr | monad-expr
do composes a seies of do-exprs with a final expr to determine its result.
do~ is similar, except it expects each do-expr to be warpped in a thunk, and it produces a thunk.
A do-expr has one of the following forms:
formals <- monad-expr Evaluates the monad-expr and binds the monad’s contents according to formals.
Examples:
> (with-instance list-monad (do (x) <- '(1 2 3 4) (return (add1 x)))) '(2 3 4 5)
let id expr Evaluates the expr and binds the result to id.
Example:
> (with-instance truthy-monad ((do~ let `(a . ,b) '(a 1 2 3) (return b)))) '(1 2 3)
let-values formals expr Evaluates the expr and binds the result according to formals.
Example:
monad-expr Evaluates the monad-expr and discards the result.
Examples:
> (with-instance box-monad (do #&1 (return 2))) '#&2
Examples:
3.2.4 Monoid
(require algebraic/control/monoid) | package: algebraic |
The class of monoids (types with an associative binary operation that has an identity).
The method names refer to the monoid of lists under concatenation, but there are many other instances.
Some types can be viewed as a monoid in more than one way, e.g. both addition and multiplication on numbers.
3.2.4.1 Members
value
mempty : any/c
NOTE: This method is redundant and has the default implementation mappend = <>.
For most types, the default definition for mconcat will be used, but the function is included in the class definition so that an optimized version can be provided for specific types.
3.2.5 Semigroup
(require algebraic/data/semigroup) | package: algebraic |
The class of semigroups (types with an associative binary operation).
class
3.2.5.1 Members
procedure
as : (non-empty-listof any/c)
The default definition should be sufficient, but this can be overridden for efficiency.
procedure
n : exact-positive-integer? a : any/c
Given that this works on a semigroup it is allowed to fail if you request 0 or fewer repetitions, and the default definition wll do so.
3.2.5.2 Helpers
procedure
(stimes-default b a) → any
b : exact-positive-integer? a : Semigroup