packages feed

applicable-0.4.1.0: Data/Applicable.hs

{-# LANGUAGE DeriveDataTypeable #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE DeriveTraversable #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE FunctionalDependencies #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# OPTIONS_HADDOCK show-extensions #-}

-- |
-- Module: Data.Applicable
-- Description: The 'Applicable' class
-- Copyright: ⓒ 2022 Anselm Schüler
-- License: MIT
-- Maintainer: mail@anselmschueler.com
--
-- The 'Applicable' class with its operator '($*)'.
-- You will likely need the @FlexibleContexts@ extension to use this module’s instances.
module Data.Applicable
  ( Applicable (..),
    ApplyTo (..),
    FlapApply (..),
    BiFlapApply (..),
    ApplyMap (..),
    ApplyAp (..),
    ApplyBind (..),
    GroupAction (..),
    ChurchBool (..),
    ChurchNumeral (..),
    ChurchTuple (..),
  )
where

import Data.Bifunctor (Bifunctor (bimap))
import Data.Data (Data)
import Data.Ix (Ix)
import Data.List (genericIndex)
import GHC.Generics (Generic)

-- | A class for types whose values can be applied.
--   Instances are required to be uniquely determined by the applied and applied-to type.
class Applicable f a b | f a -> b where
  -- | Apply a value to another value, producing a result.
  ($*) :: f -> a -> b

instance Applicable (a -> b) a b where
  f $* x = f x

-- | A wrapper for values.
--   Can be applied to a function, applying the function to the value.
newtype ApplyTo a = AppTo {unAppTo :: a} deriving (Generic, Data, Eq, Ord, Show, Read, Ix, Functor, Foldable, Traversable)

instance Applicable (ApplyTo a) (a -> b) b where
  AppTo x $* f = f x

-- | A wrapper for functions wrapped in a 'Functor'.
--   Can be applied to a value, 'fmap'-ing the application over the 'Functor'.
--
--   This nomenclature is borrowed from @relude@.
newtype FlapApply f a b = FlApp {unFlApp :: f (a -> b)} deriving (Generic, Functor)

instance Functor f => Applicable (FlapApply f a b) a (f b) where
  FlApp fs $* x = ($ x) <$> fs

-- | A wrapper for functions wrapped in a 'Bifunctor'.
--   Can be applied to a value, 'bimap'-ing the application over both fields.
newtype BiFlapApply f a b c = BiFlApp {unBiFlApp :: f (a -> b) (a -> c)} deriving Generic

instance Bifunctor f => Applicable (BiFlapApply f a b c) a (f b c) where
  BiFlApp fs $* x = bimap ($ x) ($ x) fs

-- | A wrapper for functions.
--   Can be applied to a 'Functor', 'fmap'-ing the function over the inner values.
newtype ApplyMap a b = AppMap {unAppMap :: a -> b} deriving (Generic, Functor)

instance Functor f => Applicable (ApplyMap a b) (f a) (f b) where
  AppMap f $* xa = f <$> xa

-- | A wrapper for functions in an applicative functor.
--   Can be applied to an 'Applicative' functor, '(<*>)'-ing it on it.
newtype ApplyAp f a b = AppAp {unAppAp :: f (a -> b)} deriving (Generic, Functor)

instance Applicative f => Applicable (ApplyAp f a b) (f a) (f b) where
  AppAp f $* xa = f <*> xa

-- | A wrapper for 'Control.Arrow.Kleisli' arrows.
--   Can be applied to a 'Monad', '(>>=)'-ing it on it.
newtype ApplyBind m a b = AppBind {unAppBind :: a -> m b} deriving (Generic, Functor)

instance Monad m => Applicable (ApplyBind m a b) (m a) (m b) where
  AppBind f $* xa = xa >>= f

-- | A wrapper for 'Semigroup' members, representing the associated group action.
--   Can be applied to another member, '(<>)'-ing them.
newtype GroupAction a = GrpAct {unGrpAct :: a} deriving (Generic, Data, Eq, Ord, Show, Read, Ix, Functor, Foldable, Traversable)

instance Semigroup a => Applicable (GroupAction a) a a where
  GrpAct a $* b = a <> b

-- | A wrapper for 'Bool's.
--   When applied to a value, uses the Church encoding of Booleans.
--   The Church encoding of Booleans is a binary function
--   that returns its first argument for 'True', and its second for 'False'.
newtype ChurchBool = ChBool {unChBool :: Bool} deriving (Generic, Data, Eq, Ord, Show, Read, Ix)

instance Applicable ChurchBool a (a -> a) where
  ($*) (ChBool True) t _ = t
  ($*) (ChBool False) _ f = f

-- | Update the 'Bool' in a 'ChurchBool'.
mapChBool :: (Bool -> Bool) -> ChurchBool -> ChurchBool
mapChBool f (ChBool x) = ChBool $ f x

-- | A wrapper for natural numbers (approximated by 'Integral').
--   When applied to a value, uses the Church encoding of natural numbers.
--   Church numerals represent the number _n_ as a function that take another function and repeatedly applies it _n_ times.
newtype ChurchNumeral a = ChNum {unChNum :: a} deriving (Generic, Data, Eq, Ord, Show, Read, Ix, Functor, Foldable, Traversable)

instance Integral a => Applicable (ChurchNumeral a) (a -> a) (a -> a) where
  ($*) (ChNum n) f x = genericIndex (iterate f x) n

-- | A wrapper for tuples '(,)'.
--   When applied to a value, uses the Church encoding of tuples.
--   The Church encoding of tuples applies a function to the values inside a tuple.
newtype ChurchTuple a b = ChTup {unChTup :: (a, b)} deriving (Generic, Data, Eq, Ord, Show, Read, Ix, Functor, Foldable, Traversable, Bifunctor)

instance Applicable (ChurchTuple a b) (a -> b -> c) c where
  ChTup (x, y) $* f = f x y