Control.Selective.Multi

selective-0.7.0.1: Selective applicative functors

Copyright | (c) Andrey Mokhov 2018-2024
License | MIT (see the file LICENSE)
Maintainer | andrey.mokhov@gmail.com
Stability | experimental
Safe Haskell | Safe-Inferred
Language | Haskell2010

Control.Selective.Multi

Contents

- Generalised sum types
- Selective functors
- Applicative functors
- Monads
- Generalised products and various combinators

Description

This is a library for selective applicative functors, or just selective functors for short, an abstraction between applicative functors and monads, introduced in this paper: https://dl.acm.org/doi/10.1145/3341694.

This module defines multi-way selective functors, which are more efficient when selecting from a large number of options. They also fully subsume the `Applicative` type class because they allow to express the notion of independet effects.

This definition is inspired by the following construction by Daniel Peebles, with the main difference being the added `Enumerable` constraint: https://gist.github.com/copumpkin/d5bdbc7afda54ff04049b6bdbcffb67e

Synopsis

- data Sigma (t :: Type -> Type) where
  - Sigma :: forall (t :: Type -> Type) x. t x -> x -> Sigma t
- inject :: t x -> x -> Sigma t
- data Zero a
- data One a b where
  - One :: forall a. One a a
- data Two a b c where
  - A :: forall a b. Two a b a
  - B :: forall a b. Two a b b
- data Many a b where
  - Many :: forall a. a -> Many a ()
- many :: a -> Sigma (Many a)
- matchPure :: Sigma t -> (forall x. t x -> x -> a) -> a
- eitherToSigma :: Either a b -> Sigma (Two a b)
- sigmaToEither :: Sigma (Two a b) -> Either a b
- data Some (t :: Type -> Type) where
  - Some :: forall (t :: Type -> Type) a. t a -> Some t
- class Enumerable (t :: Type -> Type) where
  - enumerate :: [Some t]
- class Applicative f => Selective (f :: Type -> Type) where
  - match :: Enumerable t => f (Sigma t) -> (forall x. t x -> f (x -> a)) -> f a
- newtype Over m a = Over {
  - getOver :: m
  }
- newtype Under m a = Under {
  - getUnder :: m
  }
- select :: Selective f => f (Either a b) -> f (a -> b) -> f b
- branch :: Selective f => f (Either a b) -> f (a -> c) -> f (b -> c) -> f c
- apS :: Selective f => f a -> f (a -> b) -> f b
- bindS :: (Enum a, Selective f) => f a -> (a -> f b) -> f b
- class Functor f => ApplicativeS (f :: Type -> Type) where
  - pureA :: a -> f a
  - matchOne :: t ~ One x => f (Sigma t) -> (forall x1. t x1 -> f (x1 -> a)) -> f a
- ap :: ApplicativeS f => f a -> f (a -> b) -> f b
- matchA :: (Applicative f, t ~ One x) => f (Sigma t) -> (forall x1. t x1 -> f (x1 -> a)) -> f a
- class Applicative f => MonadS (f :: Type -> Type) where
  - matchUnconstrained :: f (Sigma t) -> (forall x. t x -> f (x -> a)) -> f a
- bind :: MonadS f => f a -> (a -> f b) -> f b
- matchM :: Monad f => f (Sigma t) -> (forall x. t x -> f (x -> a)) -> f a
- type (~>) (t :: Type -> Type) (u :: Type -> Type) = forall x. t x -> u x
- type Pi (t :: Type -> Type) = t ~> Identity
- project :: t a -> Pi t -> a
- identity :: t x -> t x
- compose :: forall (u :: Type -> Type) (v :: Type -> Type) (t :: Type -> Type). (u ~> v) -> (t ~> u) -> t ~> v
- apply :: forall (t :: Type -> Type) (u :: Type -> Type). (t ~> u) -> Sigma t -> Sigma u
- toSigma :: a -> Sigma (One a)
- fromSigma :: Sigma (One a) -> a
- toPi :: a -> Pi (One a)
- fromPi :: Pi (One a) -> a
- pairToPi :: (a, b) -> Pi (Two a b)
- piToPair :: Pi (Two a b) -> (a, b)
- newtype Case (f :: Type -> Type) a x = Case {
  - handleCase :: f (x -> a)
  }
- matchCases :: forall f (t :: Type -> Type) a. Functor f => Sigma t -> (t ~> Case f a) -> f a

# Generalised sum types

data Sigma (t :: Type -> Type) where #

A generalised sum type where `t` stands for the type of constructor "tags". Each tag has a type parameter `x` which determines the type of the payload. A `Sigma` `t` value therefore contains a payload whose type is not visible externally but is revealed when pattern-matching on the tag.

See `Two`, `eitherToSigma` and `sigmaToEither` for an example.

Constructors

Sigma :: forall (t :: Type -> Type) x. t x -> x -> Sigma t |

inject :: t x -> x -> Sigma t #

An injection into a generalised sum. An alias for `Sigma`.

data Zero a #

A data type defining no tags. Similar to `Void` but parameterised.

#### Instances

Instances details

Enumerable Zero # |
Instance details Defined in Control.Selective.Multi Methods enumerate :: [Some Zero] #

data One a b where #

A data type with a single tag. This data type is commonly known as `Refl`, see Data.Type.Equality.

Constructors

One :: forall a. One a a |

#### Instances

Instances details

Enumerable (One a) # |
Instance details Defined in Control.Selective.Multi Methods enumerate :: [Some (One a)] #

data Two a b c where #

A data type with two tags `A` and `B` that allows us to encode the good old `Either` as `Sigma` `Two`, where the tags `A` and `B` correspond to `Left` and `Right`, respectively. See `eitherToSigma` and `sigmaToEither` that witness the isomorphism between `Either` `a b` and `Sigma` `(``Two` `a b)`.

Constructors

A :: forall a b. Two a b a |
B :: forall a b. Two a b b |

#### Instances

Instances details

Enumerable (Two a b) # |
Instance details Defined in Control.Selective.Multi Methods enumerate :: [Some (Two a b)] #

data Many a b where #

A potentially uncountable collection of tags for the same unit `()` payload.

Constructors

Many :: forall a. a -> Many a () |

#### Instances

Instances details

Enum a => Enumerable (Many a) # |
Instance details Defined in Control.Selective.Multi Methods enumerate :: [Some (Many a)] #

many :: a -> Sigma (Many a) #

matchPure :: Sigma t -> (forall x. t x -> x -> a) -> a #

Generalised pattern matching on a Sigma type using a Pi type to describe how to handle each case.

This is a specialisation of `matchCases` for `f = Identity`. We could also have also given it the following type:

    matchPure :: Sigma t -> (t ~> Case Identity a) -> a

We chose to simplify it by inlining `~>`, `Case` and `Identity`.

eitherToSigma :: Either a b -> Sigma (Two a b) #

Encode `Either` into a generalised sum type.

sigmaToEither :: Sigma (Two a b) -> Either a b #

Decode `Either` from a generalised sum type.

# Selective functors

data Some (t :: Type -> Type) where #

Hide the type of the payload a tag.

There is a whole library dedicated to this nice little GADT: http://hackage.haskell.org/package/some.

Constructors

Some :: forall (t :: Type -> Type) a. t a -> Some t |

class Enumerable (t :: Type -> Type) where #

A class of tags that can be enumerated.

A valid instance must list every tag in the resulting list exactly once.

Methods

enumerate :: [Some t] #

#### Instances

Instances details

Enumerable Zero # |
Instance details Defined in Control.Selective.Multi Methods enumerate :: [Some Zero] #
Enum a => Enumerable (Many a) # |
Instance details Defined in Control.Selective.Multi Methods enumerate :: [Some (Many a)] #
Enumerable (One a) # |
Instance details Defined in Control.Selective.Multi Methods enumerate :: [Some (One a)] #
Enumerable (Two a b) # |
Instance details Defined in Control.Selective.Multi Methods enumerate :: [Some (Two a b)] #

class Applicative f => Selective (f :: Type -> Type) where #

Multi-way selective functors. Given a computation that produces a value of a sum type, we can `match` it to the corresponding computation in a given product type.

For greater similarity with `matchCases`, we could have given the following type to `match`:

    match :: f (Sigma t) -> (t ~> Case f a) -> f a

We chose to simplify it by inlining `~>` and `Case`.

Methods

match :: Enumerable t => f (Sigma t) -> (forall x. t x -> f (x -> a)) -> f a #

#### Instances

Instances details

Monoid m => Selective (Over m) # |
Instance details Defined in Control.Selective.Multi Methods match :: Enumerable t => Over m (Sigma t) -> (forall x. t x -> Over m (x -> a)) -> Over m a #
Monoid m => Selective (Under m) # |
Instance details Defined in Control.Selective.Multi Methods match :: Enumerable t => Under m (Sigma t) -> (forall x. t x -> Under m (x -> a)) -> Under m a #

newtype Over m a #

Static analysis of selective functors with over-approximation.

Constructors

Over |
Fields getOver :: m

#### Instances

Instances details

Monoid m => Applicative (Over m) # |
Instance details Defined in Control.Selective.Multi Methods pure :: a -> Over m a # (<*>) :: Over m (a -> b) -> Over m a -> Over m b # liftA2 :: (a -> b -> c) -> Over m a -> Over m b -> Over m c # (*>) :: Over m a -> Over m b -> Over m b # (<*) :: Over m a -> Over m b -> Over m a #
Functor (Over m) # |
Instance details Defined in Control.Selective.Multi Methods fmap :: (a -> b) -> Over m a -> Over m b # (<$) :: a -> Over m b -> Over m a #
Monoid m => Selective (Over m) # |
Instance details Defined in Control.Selective.Multi Methods match :: Enumerable t => Over m (Sigma t) -> (forall x. t x -> Over m (x -> a)) -> Over m a #
Show m => Show (Over m a) # |
Instance details Defined in Control.Selective.Multi Methods showsPrec :: Int -> Over m a -> ShowS # show :: Over m a -> String # showList :: [Over m a] -> ShowS #
Eq m => Eq (Over m a) # |
Instance details Defined in Control.Selective.Multi Methods (==) :: Over m a -> Over m a -> Bool # (/=) :: Over m a -> Over m a -> Bool #
Ord m => Ord (Over m a) # |
Instance details Defined in Control.Selective.Multi Methods compare :: Over m a -> Over m a -> Ordering # (<) :: Over m a -> Over m a -> Bool # (<=) :: Over m a -> Over m a -> Bool # (>) :: Over m a -> Over m a -> Bool # (>=) :: Over m a -> Over m a -> Bool # max :: Over m a -> Over m a -> Over m a # min :: Over m a -> Over m a -> Over m a #

newtype Under m a #

Static analysis of selective functors with under-approximation.

Constructors

Under |
Fields getUnder :: m

#### Instances

Instances details

Monoid m => Applicative (Under m) # |
Instance details Defined in Control.Selective.Multi Methods pure :: a -> Under m a # (<*>) :: Under m (a -> b) -> Under m a -> Under m b # liftA2 :: (a -> b -> c) -> Under m a -> Under m b -> Under m c # (*>) :: Under m a -> Under m b -> Under m b # (<*) :: Under m a -> Under m b -> Under m a #
Functor (Under m) # |
Instance details Defined in Control.Selective.Multi Methods fmap :: (a -> b) -> Under m a -> Under m b # (<$) :: a -> Under m b -> Under m a #
Monoid m => Selective (Under m) # |
Instance details Defined in Control.Selective.Multi Methods match :: Enumerable t => Under m (Sigma t) -> (forall x. t x -> Under m (x -> a)) -> Under m a #
Show m => Show (Under m a) # |
Instance details Defined in Control.Selective.Multi Methods showsPrec :: Int -> Under m a -> ShowS # show :: Under m a -> String # showList :: [Under m a] -> ShowS #
Eq m => Eq (Under m a) # |
Instance details Defined in Control.Selective.Multi Methods (==) :: Under m a -> Under m a -> Bool # (/=) :: Under m a -> Under m a -> Bool #
Ord m => Ord (Under m a) # |
Instance details Defined in Control.Selective.Multi Methods compare :: Under m a -> Under m a -> Ordering # (<) :: Under m a -> Under m a -> Bool # (<=) :: Under m a -> Under m a -> Bool # (>) :: Under m a -> Under m a -> Bool # (>=) :: Under m a -> Under m a -> Bool # max :: Under m a -> Under m a -> Under m a # min :: Under m a -> Under m a -> Under m a #

select :: Selective f => f (Either a b) -> f (a -> b) -> f b #

The basic "if-then" selection primitive from Control.Selective.

branch :: Selective f => f (Either a b) -> f (a -> c) -> f (b -> c) -> f c #

Choose a matching effect with `Either`.

apS :: Selective f => f a -> f (a -> b) -> f b #

Recover the application operator `<*>` from `match`.

bindS :: (Enum a, Selective f) => f a -> (a -> f b) -> f b #

A restricted version of monadic bind.

# Applicative functors

class Functor f => ApplicativeS (f :: Type -> Type) where #

An alternative definition of applicative functors, as witnessed by `ap` and `matchOne`. This class is almost like `Selective` but has a strict constraint on `t`.

Methods

pureA :: a -> f a #

matchOne :: t ~ One x => f (Sigma t) -> (forall x1. t x1 -> f (x1 -> a)) -> f a #

ap :: ApplicativeS f => f a -> f (a -> b) -> f b #

Recover the application operator `<*>` from `matchOne`.

matchA :: (Applicative f, t ~ One x) => f (Sigma t) -> (forall x1. t x1 -> f (x1 -> a)) -> f a #

Every `Applicative` is also an `ApplicativeS`.

# Monads

class Applicative f => MonadS (f :: Type -> Type) where #

An alternative definition of monads, as witnessed by `bind` and `matchM`. This class is almost like `Selective` but has no the constraint on `t`.

Methods

matchUnconstrained :: f (Sigma t) -> (forall x. t x -> f (x -> a)) -> f a #

bind :: MonadS f => f a -> (a -> f b) -> f b #

Monadic bind.

matchM :: Monad f => f (Sigma t) -> (forall x. t x -> f (x -> a)) -> f a #

Every monad is a multi-way selective functor.

# Generalised products and various combinators

type (~>) (t :: Type -> Type) (u :: Type -> Type) = forall x. t x -> u x infixl 4 #

A generalised product type (Pi), which holds an appropriately tagged payload `u x` for every possible tag `t x`.

Note that this looks different than the standard formulation of Pi types. Maybe it's just all wrong!

See `Two`, `pairToPi` and `piToPair` for an example.

type Pi (t :: Type -> Type) = t ~> Identity #

A product type where the payload has the type specified with the tag.

project :: t a -> Pi t -> a #

A projection from a generalised product.

identity :: t x -> t x #

A trivial product type that stores nothing and simply returns the given tag as the result.

compose :: forall (u :: Type -> Type) (v :: Type -> Type) (t :: Type -> Type). (u ~> v) -> (t ~> u) -> t ~> v #

As it turns out, one can compose such generalised products. Why not: given a tag, get the payload of the first product and then pass it as input to the second. This feels too trivial to be useful but is still somewhat cute.

apply :: forall (t :: Type -> Type) (u :: Type -> Type). (t ~> u) -> Sigma t -> Sigma u #

Update a generalised sum given a generalised product that takes care of all possible cases.

toSigma :: a -> Sigma (One a) #

Encode a value into a generalised sum type that has a single tag `One`.

fromSigma :: Sigma (One a) -> a #

Decode a value from a generalised sum type that has a single tag `One`.

toPi :: a -> Pi (One a) #

Encode a value into a generalised product type that has a single tag `One`.

fromPi :: Pi (One a) -> a #

Decode a value from a generalised product type that has a single tag `One`.

pairToPi :: (a, b) -> Pi (Two a b) #

Encode `(a, b)` into a generalised product type.

piToPair :: Pi (Two a b) -> (a, b) #

Decode `(a, b)` from a generalised product type.

newtype Case (f :: Type -> Type) a x #

Handler of a single case in a generalised pattern matching `matchCases`.

Constructors

Case |
Fields handleCase :: f (x -> a)

matchCases :: forall f (t :: Type -> Type) a. Functor f => Sigma t -> (t ~> Case f a) -> f a #

Generalised pattern matching on a Sigma type using a Pi type to describe how to handle each case.