Idris2/libs/prelude/Prelude/Interfaces.idr
2022-09-24 10:20:25 +01:00

575 lines
17 KiB
Idris

module Prelude.Interfaces
import Builtin
import Prelude.Basics
import Prelude.EqOrd
import Prelude.Num
%default total
-------------
-- ALGEBRA --
-------------
||| Sets equipped with a single binary operation that is associative. Must
||| satisfy the following laws:
|||
||| + Associativity of `<+>`:
||| forall a b c, a <+> (b <+> c) == (a <+> b) <+> c
public export
interface Semigroup ty where
constructor MkSemigroup
(<+>) : ty -> ty -> ty
||| Sets equipped with a single binary operation that is associative, along with
||| a neutral element for that binary operation. Must satisfy the following
||| laws:
|||
||| + Associativity of `<+>`:
||| forall a b c, a <+> (b <+> c) == (a <+> b) <+> c
||| + Neutral for `<+>`:
||| forall a, a <+> neutral == a
||| forall a, neutral <+> a == a
public export
interface Semigroup ty => Monoid ty where
constructor MkMonoid
neutral : ty
public export
Semigroup () where
_ <+> _ = ()
public export
Monoid () where
neutral = ()
public export
Semigroup a => Semigroup b => Semigroup (a, b) where
(x, y) <+> (v, w) = (x <+> v, y <+> w)
public export
Monoid a => Monoid b => Monoid (a, b) where
neutral = (neutral, neutral)
public export
Semigroup Ordering where
LT <+> _ = LT
GT <+> _ = GT
EQ <+> o = o
public export
Monoid Ordering where
neutral = EQ
public export
Semigroup b => Semigroup (a -> b) where
(f <+> g) x = f x <+> g x
public export
Monoid b => Monoid (a -> b) where
neutral _ = neutral
---------------------------------------------------------
-- FUNCTOR, BIFUNCTOR, APPLICATIVE, ALTERNATIVE, MONAD --
---------------------------------------------------------
||| Functors allow a uniform action over a parameterised type.
||| @ f a parameterised type
public export
interface Functor f where
constructor MkFunctor
||| Apply a function across everything of type 'a' in a parameterised type
||| @ f the parameterised type
||| @ func the function to apply
map : (func : a -> b) -> f a -> f b
||| An infix alias for `map`, applying a function across everything of type 'a'
||| in a parameterised type.
||| @ f the parameterised type
||| @ func the function to apply
public export
(<$>) : Functor f => (func : a -> b) -> f a -> f b
(<$>) func x = map func x
||| Flipped version of `<$>`, an infix alias for `map`, applying a function across
||| everything of type 'a' in a parameterised type.
||| @ f the parameterised type
||| @ func the function to apply
public export
(<&>) : Functor f => f a -> (func : a -> b) -> f b
(<&>) x func = map func x
||| Run something for effects, replacing the return value with a given parameter.
public export
(<$) : Functor f => b -> f a -> f b
(<$) b = map (const b)
||| Flipped version of `<$`.
public export
($>) : Functor f => f a -> b -> f b
($>) fa b = map (const b) fa
||| Run something for effects, throwing away the return value.
%inline
public export
ignore : Functor f => f a -> f ()
ignore = map (const ())
namespace Functor
||| Composition of functors is a functor.
public export
[Compose] (l : Functor f) => (r : Functor g) => Functor (f . g) where
map = map . map
||| Bifunctors
||| @f The action of the Bifunctor on pairs of objects
||| A minimal definition includes either `bimap` or both `mapFst` and `mapSnd`.
public export
interface Bifunctor f where
constructor MkBifunctor
||| The action of the Bifunctor on pairs of morphisms
|||
||| ````idris example
||| bimap (\x => x + 1) reverse (1, "hello") == (2, "olleh")
||| ````
|||
total
bimap : (a -> c) -> (b -> d) -> f a b -> f c d
bimap f g = mapFst f . mapSnd g
||| The action of the Bifunctor on morphisms pertaining to the first object
|||
||| ````idris example
||| mapFst (\x => x + 1) (1, "hello") == (2, "hello")
||| ````
|||
total
mapFst : (a -> c) -> f a b -> f c b
mapFst f = bimap f id
||| The action of the Bifunctor on morphisms pertaining to the second object
|||
||| ````idris example
||| mapSnd reverse (1, "hello") == (1, "olleh")
||| ````
|||
total
mapSnd : (b -> d) -> f a b -> f a d
mapSnd = bimap id
public export
mapHom : Bifunctor f => (a -> b) -> f a a -> f b b
mapHom f = bimap f f
public export
interface Functor f => Applicative f where
constructor MkApplicative
pure : a -> f a
(<*>) : f (a -> b) -> f a -> f b
public export
(<*) : Applicative f => f a -> f b -> f a
a <* b = map const a <*> b
public export
(*>) : Applicative f => f a -> f b -> f b
a *> b = map (const id) a <*> b
%allow_overloads pure
%allow_overloads (<*)
%allow_overloads (*>)
namespace Applicative
||| Composition of applicative functors is an applicative functor.
public export
[Compose] (l : Applicative f) => (r : Applicative g) => Applicative (f . g)
using Functor.Compose where
pure = pure . pure
fun <*> x = [| fun <*> x |]
||| An alternative functor has a notion of disjunction.
||| @f is the underlying applicative functor
||| We expect (f a, empty, (<|>)) to be a type family of monoids.
public export
interface Applicative f => Alternative f where
constructor MkAlternative
empty : f a
(<|>) : f a -> Lazy (f a) -> f a
||| Monad
||| @m The underlying functor
||| A minimal definition includes either `(>>=)` or `join`.
public export
interface Applicative m => Monad m where
constructor MkMonad
||| Also called `bind`.
total
(>>=) : m a -> (a -> m b) -> m b
||| Also called `flatten` or mu.
total
join : m (m a) -> m a
-- default implementations
(>>=) x f = join (f <$> x)
join x = x >>= id
%allow_overloads (>>=)
||| Right-to-left monadic bind, flipped version of `>>=`.
public export
(=<<) : Monad m => (a -> m b) -> m a -> m b
(=<<) = flip (>>=)
||| Sequencing of effectful composition
public export
(>>) : Monad m => m () -> Lazy (m b) -> m b
a >> b = a >>= \_ => b
||| Left-to-right Kleisli composition of monads.
public export
(>=>) : Monad m => (a -> m b) -> (b -> m c) -> (a -> m c)
(>=>) f g = \x => f x >>= g
||| Right-to-left Kleisli composition of monads, flipped version of `>=>`.
public export
(<=<) : Monad m => (b -> m c) -> (a -> m b) -> (a -> m c)
(<=<) = flip (>=>)
||| `guard a` is `pure ()` if `a` is `True` and `empty` if `a` is `False`.
public export
guard : Alternative f => Bool -> f ()
guard x = if x then pure () else empty
||| Conditionally execute an applicative expression when the boolean is true.
public export
when : Applicative f => Bool -> Lazy (f ()) -> f ()
when True f = f
when False f = pure ()
||| Execute an applicative expression unless the boolean is true.
%inline public export
unless : Applicative f => Bool -> Lazy (f ()) -> f ()
unless = when . not
---------------------------
-- FOLDABLE, TRAVERSABLE --
---------------------------
||| The `Foldable` interface describes how you can iterate over the elements in
||| a parameterised type and combine the elements together, using a provided
||| function, into a single result.
||| @ t The type of the 'Foldable' parameterised type.
||| A minimal definition includes `foldr`
public export
interface Foldable t where
constructor MkFoldable
||| Successively combine the elements in a parameterised type using the
||| provided function, starting with the element that is in the final position
||| i.e. the right-most position.
||| @ func The function used to 'fold' an element into the accumulated result
||| @ init The starting value the results are being combined into
||| @ input The parameterised type
foldr : (func : elem -> acc -> acc) -> (init : acc) -> (input : t elem) -> acc
||| The same as `foldr` but begins the folding from the element at the initial
||| position in the data structure i.e. the left-most position.
||| @ func The function used to 'fold' an element into the accumulated result
||| @ init The starting value the results are being combined into
||| @ input The parameterised type
foldl : (func : acc -> elem -> acc) -> (init : acc) -> (input : t elem) -> acc
foldl f z t = foldr (flip (.) . flip f) id t z
||| Test whether the structure is empty.
||| @ acc The accumulator value which is specified to be lazy
null : t elem -> Bool
null xs = foldr {acc = Lazy Bool} (\ _,_ => False) True xs
||| Similar to `foldl`, but uses a function wrapping its result in a `Monad`.
||| Consequently, the final value is wrapped in the same `Monad`.
foldlM : Monad m => (funcM : acc -> elem -> m acc) -> (init : acc) -> (input : t elem) -> m acc
foldlM fm a0 = foldl (\ma, b => ma >>= flip fm b) (pure a0)
||| Produce a list of the elements contained in the parametrised type.
toList : t elem -> List elem
toList = foldr (::) []
||| Maps each element to a value and combine them.
||| For performance reasons, this should wherever
||| be implemented with tail recursion.
||| @ f The function to apply to each element.
foldMap : Monoid m => (f : a -> m) -> t a -> m
foldMap f = foldr ((<+>) . f) neutral
||| Combine each element of a structure into a monoid.
public export
concat : Monoid a => Foldable t => t a -> a
concat = foldMap id
||| Combine into a monoid the collective results of applying a function to each
||| element of a structure.
public export
concatMap : Monoid m => Foldable t => (a -> m) -> t a -> m
concatMap = foldMap
namespace Bool.Lazy
namespace Semigroup
public export
[Any] Semigroup (Lazy Bool) where
x <+> y = force x || y
public export
[All] Semigroup (Lazy Bool) where
x <+> y = force x && y
namespace Monoid
public export
[Any] Monoid (Lazy Bool) using Semigroup.Any where
neutral = delay False
public export
[All] Monoid (Lazy Bool) using Semigroup.All where
neutral = delay True
||| The conjunction of all elements of a structure containing lazy boolean
||| values. `and` short-circuits from left to right, evaluating until either an
||| element is `False` or no elements remain.
public export
and : Foldable t => t (Lazy Bool) -> Bool
and = force . concat @{All}
||| The disjunction of all elements of a structure containing lazy boolean
||| values. `or` short-circuits from left to right, evaluating either until an
||| element is `True` or no elements remain.
public export
or : Foldable t => t (Lazy Bool) -> Bool
or = force . concat @{Any}
namespace Bool
namespace Semigroup
public export
[Any] Semigroup Bool where
x <+> y = x || delay y
public export
[All] Semigroup Bool where
x <+> y = x && delay y
namespace Monoid
public export
[Any] Monoid Bool using Bool.Semigroup.Any where
neutral = False
public export
[All] Monoid Bool using Bool.Semigroup.All where
neutral = True
||| The disjunction of the collective results of applying a predicate to all
||| elements of a structure. `any` short-circuits from left to right.
public export
any : Foldable t => (a -> Bool) -> t a -> Bool
any = foldMap @{%search} @{Any}
||| The conjunction of the collective results of applying a predicate to all
||| elements of a structure. `all` short-circuits from left to right.
public export
all : Foldable t => (a -> Bool) -> t a -> Bool
all = foldMap @{%search} @{All}
namespace Num
namespace Semigroup
public export
[Additive] Num a => Semigroup a where
(<+>) = (+)
public export
[Multiplicative] Num a => Semigroup a where
(<+>) = (*)
namespace Monoid
public export
[Additive] Num a => Monoid a using Semigroup.Additive where
neutral = 0
public export
[Multiplicative] Num a => Monoid a using Semigroup.Multiplicative where
neutral = 1
||| Add together all the elements of a structure.
public export
sum : Num a => Foldable t => t a -> a
sum = concat @{Additive}
||| Add together all the elements of a structure.
||| Same as `sum` but tail recursive.
export
sum' : Num a => Foldable t => t a -> a
sum' = sum
||| Multiply together all elements of a structure.
public export
product : Num a => Foldable t => t a -> a
product = concat @{Multiplicative}
||| Multiply together all elements of a structure.
||| Same as `product` but tail recursive.
export
product' : Num a => Foldable t => t a -> a
product' = product
||| Map each element of a structure to a computation, evaluate those
||| computations and discard the results.
public export
traverse_ : Applicative f => Foldable t => (a -> f b) -> t a -> f ()
traverse_ f = foldr ((*>) . f) (pure ())
||| Evaluate each computation in a structure and discard the results.
public export
sequence_ : Applicative f => Foldable t => t (f a) -> f ()
sequence_ = foldr (*>) (pure ())
||| Like `traverse_` but with the arguments flipped.
public export
for_ : Applicative f => Foldable t => t a -> (a -> f b) -> f ()
for_ = flip traverse_
public export
[SemigroupApplicative] Applicative f => Semigroup a => Semigroup (f a) where
x <+> y = [| x <+> y |]
public export
[MonoidApplicative] Applicative f => Monoid a => Monoid (f a) using SemigroupApplicative where
neutral = pure neutral
namespace Lazy
public export
[SemigroupAlternative] Alternative f => Semigroup (Lazy (f a)) where
x <+> y = force x <|> y
public export
[MonoidAlternative] Alternative f => Monoid (Lazy (f a)) using Lazy.SemigroupAlternative where
neutral = delay empty
public export
[SemigroupAlternative] Alternative f => Semigroup (f a) where
x <+> y = x <|> delay y
public export
[MonoidAlternative] Alternative f => Monoid (f a) using Interfaces.SemigroupAlternative where
neutral = empty
||| Fold using Alternative.
|||
||| If you have a left-biased alternative operator `<|>`, then `choice` performs
||| left-biased choice from a list of alternatives, which means that it
||| evaluates to the left-most non-`empty` alternative.
|||
||| If the list is empty, or all values in it are `empty`, then it evaluates to
||| `empty`.
|||
||| Example:
|||
||| ```
||| -- given a parser expression like:
||| expr = literal <|> keyword <|> funcall
|||
||| -- choice lets you write this as:
||| expr = choice [literal, keyword, funcall]
||| ```
|||
||| Note: In Haskell, `choice` is called `asum`.
public export
choice : Alternative f => Foldable t => t (Lazy (f a)) -> f a
choice = force . concat @{Lazy.MonoidAlternative}
||| A fused version of `choice` and `map`.
public export
choiceMap : Alternative f => Foldable t => (a -> f b) -> t a -> f b
choiceMap = foldMap @{%search} @{MonoidAlternative}
namespace Foldable
||| Composition of foldables is foldable.
public export
[Compose] (l : Foldable t) => (r : Foldable f) => Foldable (t . f) where
foldr = foldr . flip . foldr
foldl = foldl . foldl
null tf = null tf || all null tf
foldMap = foldMap . foldMap
||| `Bifoldable` identifies foldable structures with two different varieties
||| of elements (as opposed to `Foldable`, which has one variety of element).
||| Common examples are `Either` and `Pair`.
||| A minimal definition includes `bifoldr`
public export
interface Bifoldable p where
constructor MkBifoldable
bifoldr : (a -> acc -> acc) -> (b -> acc -> acc) -> acc -> p a b -> acc
bifoldl : (acc -> a -> acc) -> (acc -> b -> acc) -> acc -> p a b -> acc
bifoldl f g z t = bifoldr (flip (.) . flip f) (flip (.) . flip g) id t z
binull : p a b -> Bool
binull t = bifoldr {acc = Lazy Bool} (\ _,_ => False) (\ _,_ => False) True t
||| Analogous to `foldMap` but for `Bifoldable` structures
public export
bifoldMap : Monoid acc => Bifoldable p => (a -> acc) -> (b -> acc) -> p a b -> acc
bifoldMap f g = bifoldr ((<+>) . f) ((<+>) . g) neutral
||| Like Bifunctor's `mapFst` but for `Bifoldable` structures
public export
bifoldMapFst : Monoid acc => Bifoldable p => (a -> acc) -> p a b -> acc
bifoldMapFst f = bifoldMap f (const neutral)
public export
interface (Functor t, Foldable t) => Traversable t where
constructor MkTraversable
||| Map each element of a structure to a computation, evaluate those
||| computations and combine the results.
traverse : Applicative f => (a -> f b) -> t a -> f (t b)
||| Evaluate each computation in a structure and collect the results.
public export
sequence : Applicative f => Traversable t => t (f a) -> f (t a)
sequence = traverse id
||| Like `traverse` but with the arguments flipped.
public export
for : Applicative f => Traversable t => t a -> (a -> f b) -> f (t b)
for = flip traverse
public export
interface (Bifunctor p, Bifoldable p) => Bitraversable p where
constructor MkBitraversable
||| Map each element of a structure to a computation, evaluate those
||| computations and combine the results.
bitraverse : Applicative f => (a -> f c) -> (b -> f d) -> p a b -> f (p c d)
||| Evaluate each computation in a structure and collect the results.
public export
bisequence : Applicative f => Bitraversable p => p (f a) (f b) -> f (p a b)
bisequence = bitraverse id id
||| Like `bitraverse` but with the arguments flipped.
public export
bifor : Applicative f => Bitraversable p
=> p a b
-> (a -> f c)
-> (b -> f d)
-> f (p c d)
bifor t f g = bitraverse f g t
namespace Traversable
||| Composition of traversables is traversable.
public export
[Compose] (l : Traversable t) => (r : Traversable f) => Traversable (t . f)
using Foldable.Compose Functor.Compose where
traverse = traverse . traverse
namespace Monad
||| Composition of a traversable monad and a monad is a monad.
public export
[Compose] (l : Monad m) => (r : Monad t) => (tr : Traversable t) => Monad (m . t)
using Applicative.Compose where
a >>= f = a >>= map join . traverse f