1 {-# LANGUAGE AllowAmbiguousTypes #-}
2 {-# LANGUAGE ConstraintKinds #-}
3 {-# LANGUAGE DataKinds #-}
4 {-# LANGUAGE UndecidableInstances #-}
6 -- | Tuples-of-Functions
7 module Symantic.Syntaxes.TuplesOfFunctions where
9 import Data.Bool (Bool (..))
10 import Data.Either (Either (..))
12 import Data.Function (const, id, ($), (.))
13 import Data.Kind (Type)
15 import Text.Show (Show)
17 -- * Type family '(-->)'
19 -- | Convenient alias for a Tuples of Functions transformation.
21 -- Example of a covariant semantic producing a 'Text':
24 -- data Texter a = { unTexter :: forall next. (a --> next) -> next }
25 -- runTexter :: Texter a -> (a --> Text) -> Text
26 -- runTexter = unTexter
30 -- Example of a contravariant semantic producing a 'Text':
33 -- data Texter a = { unTexter :: forall next. (Text -> next) -> a --> next }
34 -- runTexter :: Texter a -> a --> Text
35 -- runTexter sem = unTexter sem id
37 type (-->) a next = ToFIf (IsToF a) a next
41 -- ** Type family 'ToFIf'
43 -- | Return Tuples-of-Functions instead of Eithers-of-Tuples
45 -- Useful to avoid introducing a parameter dedicated for the return value,
46 -- as in https://okmij.org/ftp/typed-formatting/index.html#DSL-FIn
48 -- Useful to avoid declaring and pattern-matching
49 -- an algebraic datatype of type @(a)@,
50 -- as the corresponding function will be called directly,
51 -- given as arguments the terms that would have been
52 -- pattern-matched from a constructor
53 -- of such algebraic datatype.
54 type family ToFIf (t :: Bool) a next :: Type where
56 ToFIf 'True (a, b) next = a --> b --> next
58 ToFIf 'True (Either a b) next = (a --> next, b --> next)
59 -- Skip '()' as argument.
60 ToFIf 'True () next = next
61 -- Enable a different return value for each function.
62 ToFIf 'True (Endpoint sem a) next = ToFEndpoint sem a next
63 -- Everything else becomes a new argument.
64 ToFIf 'False a next = a -> next
66 -- ** Type family 'IsToF'
68 -- | When @('IsToF' a ~ 'True')@, iif. the argument is changed by 'ToFIf'.
69 -- This being a closed type family, it enables to avoid defining
70 -- an instance of 'ToFIf' and 'ToFable' for all types.
71 type family IsToF (a :: Type) :: Bool where
73 IsToF (Either a b) = 'True
75 IsToF (Endpoint end a) = 'True
80 -- | @('Endpoint' sem a)@ enables the function equivalent to a datatype constructor,
81 -- to return a value of type @a@.
83 -- Useful to enable functions to return different types.
84 newtype Endpoint (sem :: Type -> Type) a = Endpoint {unEndpoint :: a}
85 deriving (Eq, Ord, Show)
87 -- ** Type family 'ToFEndpoint'
88 type family ToFEndpoint (sem :: Type -> Type) a next :: Type
92 tofOffun :: (a -> next) -> a --> next
93 funOftof :: (a --> next) -> a -> next
94 default tofOffun :: (a --> next) ~ (a -> next) => (a -> next) -> a --> next
95 default funOftof :: (a --> next) ~ (a -> next) => (a --> next) -> a -> next
98 instance ToFable () where
101 instance (ToFable a, ToFable b) => ToFable (a, b) where
102 tofOffun ab2n = tofOffun (\a -> tofOffun (\b -> ab2n (a, b)))
103 funOftof k (a, b) = funOftof (funOftof k a) b
104 instance (ToFable a, ToFable b) => ToFable (Either a b) where
105 tofOffun e2n = (tofOffun (e2n . Left), tofOffun (e2n . Right))
106 funOftof (ak, bk) = \case
107 Left a -> funOftof ak a
108 Right b -> funOftof bk b
110 -- OVERLAPPABLE could be avoided by using 'ToFIf',
111 -- but that would a be a bit more verbose and require more type annotations.
112 instance {-# OVERLAPPABLE #-} IsToF a ~ 'False => ToFable a