2 {-# LANGUAGE AllowAmbiguousTypes #-}
4 {-# LANGUAGE DataKinds #-}
6 {-# LANGUAGE PatternSynonyms #-}
8 {-# LANGUAGE RankNTypes #-}
10 {-# LANGUAGE TypeFamilyDependencies #-}
12 {-# LANGUAGE UndecidableInstances #-}
14 -- | This module gathers commonly used tagless-final combinators
15 -- (the syntax part of symantics).
17 -- Note: combinators in this module conflict with usual ones from the @Prelude@
18 -- hence they are meant to be imported either explicitely or qualified.
19 module Symantic.Syntaxes.Classes where
21 import Control.Category qualified as Cat
22 import Data.Bool (Bool (..))
23 import Data.Char (Char)
24 import Data.Either (Either (..))
26 import Data.Function qualified as Fun
28 import Data.Kind (Constraint)
29 import Data.Maybe (Maybe (..), fromJust)
30 import Data.Proxy (Proxy (..))
31 import Data.Semigroup (Semigroup)
32 import Data.String (String)
33 import Data.Tuple qualified as Tuple
34 import GHC.Generics (Generic)
35 import Numeric.Natural (Natural)
37 import Symantic.Syntaxes.CurryN
38 import Symantic.Syntaxes.Derive
39 import Symantic.Syntaxes.EithersOfTuples
42 type Syntax = Semantic -> Constraint
44 -- ** Type family 'Syntaxes'
46 -- | Merge several 'Syntax'es into a single one.
48 -- Useful in 'IfSemantic' or 'All'.
49 type family Syntaxes (syns :: [Syntax]) (sem :: Semantic) :: Constraint where
50 Syntaxes '[] sem = (() :: Constraint)
51 Syntaxes (syn ': syns) sem = ((syn sem, Syntaxes syns sem) :: Constraint)
53 -- * Class 'Abstractable'
54 class Abstractable sem where
55 -- | Lambda term abstraction, in HOAS (Higher-Order Abstract Syntax) style.
56 lam :: (sem a -> sem b) -> sem (a -> b)
57 lam f = liftDerived (lam (derive Fun.. f Fun.. liftDerived))
59 FromDerived Abstractable sem =>
64 -- ** Class 'Abstractable1'
65 class Abstractable1 sem where
66 -- | Like 'lam' but whose argument must be used only once,
67 -- hence safe to beta-reduce (inline) without duplicating work.
68 lam1 :: (sem a -> sem b) -> sem (a -> b)
69 lam1 f = liftDerived (lam1 (derive Fun.. f Fun.. liftDerived))
71 FromDerived Abstractable1 sem =>
77 class Varable sem where
78 -- | Mark the use of a variable.
80 var = liftDerived1 var
81 default var :: FromDerived1 Varable sem => sem a -> sem a
83 -- ** Class 'Unabstractable'
84 class Unabstractable sem where
85 -- | Application, aka. unabstract.
86 (.@) :: sem (a -> b) -> sem a -> sem b
89 (.@) = liftDerived2 (.@)
91 FromDerived2 Unabstractable sem =>
96 -- ** Class 'Functionable'
97 class Functionable sem where
98 const :: sem (a -> b -> a)
99 flip :: sem ((a -> b -> c) -> b -> a -> c)
101 (.) :: sem ((b -> c) -> (a -> b) -> a -> c)
103 ($) :: sem ((a -> b) -> a -> b)
105 const = liftDerived const
106 flip = liftDerived flip
108 (.) = liftDerived (.)
109 ($) = liftDerived ($)
111 FromDerived Functionable sem =>
114 FromDerived Functionable sem =>
115 sem ((a -> b -> c) -> b -> a -> c)
117 FromDerived Functionable sem =>
120 FromDerived Functionable sem =>
121 sem ((b -> c) -> (a -> b) -> a -> c)
123 FromDerived Functionable sem =>
124 sem ((a -> b) -> a -> b)
126 -- * Class 'Anythingable'
127 class Anythingable sem where
128 anything :: sem a -> sem a
131 -- * Class 'Bottomable'
132 class Bottomable sem where
135 -- * Class 'Constantable'
136 class Constantable c sem where
137 constant :: c -> sem c
138 constant = liftDerived Fun.. constant
140 FromDerived (Constantable c) sem =>
144 -- * Class 'Eitherable'
145 class Eitherable sem where
146 either :: sem ((l -> a) -> (r -> a) -> Either l r -> a)
147 left :: sem (l -> Either l r)
148 right :: sem (r -> Either l r)
149 either = liftDerived either
150 left = liftDerived left
151 right = liftDerived right
153 FromDerived Eitherable sem =>
154 sem ((l -> a) -> (r -> a) -> Either l r -> a)
156 FromDerived Eitherable sem =>
157 sem (l -> Either l r)
159 FromDerived Eitherable sem =>
160 sem (r -> Either l r)
162 -- * Class 'Equalable'
163 class Equalable sem where
164 equal :: Eq a => sem (a -> a -> Bool)
165 equal = liftDerived equal
167 FromDerived Equalable sem =>
174 Unabstractable sem =>
180 (==) x y = equal .@ x .@ y
182 -- * Class 'IfThenElseable'
183 class IfThenElseable sem where
184 ifThenElse :: sem Bool -> sem a -> sem a -> sem a
185 ifThenElse = liftDerived3 ifThenElse
186 default ifThenElse ::
187 FromDerived3 IfThenElseable sem =>
193 -- * Class 'Inferable'
194 class Inferable a sem where
196 default infer :: FromDerived (Inferable a) sem => sem a
197 infer = liftDerived infer
199 unit :: Inferable () sem => sem ()
201 bool :: Inferable Bool sem => sem Bool
203 char :: Inferable Char sem => sem Char
205 int :: Inferable Int sem => sem Int
207 natural :: Inferable Natural sem => sem Natural
209 string :: Inferable String sem => sem String
212 -- * Class 'Listable'
213 class Listable sem where
214 cons :: sem (a -> [a] -> [a])
216 cons = liftDerived cons
217 nil = liftDerived nil
219 FromDerived Listable sem =>
220 sem (a -> [a] -> [a])
222 FromDerived Listable sem =>
225 -- * Class 'Maybeable'
226 class Maybeable sem where
227 nothing :: sem (Maybe a)
228 just :: sem (a -> Maybe a)
229 nothing = liftDerived nothing
230 just = liftDerived just
232 FromDerived Maybeable sem =>
235 FromDerived Maybeable sem =>
238 -- * Class 'IsoFunctor'
239 class IsoFunctor sem where
240 (<%>) :: Iso a b -> sem a -> sem b
242 (<%>) iso = liftDerived1 (iso <%>)
244 FromDerived1 IsoFunctor sem =>
250 data Iso a b = Iso {a2b :: a -> b, b2a :: b -> a}
251 instance Cat.Category Iso where
252 id = Iso Cat.id Cat.id
253 f . g = Iso (a2b f Cat.. a2b g) (b2a g Cat.. b2a f)
255 -- * Class 'ProductFunctor'
257 -- | Beware that this is an @infixr@,
258 -- not @infixl@ like 'Control.Applicative.<*>';
259 -- this is to follow what is expected by 'ADT'.
260 class ProductFunctor sem where
261 (<.>) :: sem a -> sem b -> sem (a, b)
263 (<.>) = liftDerived2 (<.>)
265 FromDerived2 ProductFunctor sem =>
269 (<.) :: sem a -> sem () -> sem a
271 ra <. rb = Iso Tuple.fst (,()) <%> (ra <.> rb)
272 default (<.) :: IsoFunctor sem => sem a -> sem () -> sem a
273 (.>) :: sem () -> sem a -> sem a
275 ra .> rb = Iso Tuple.snd ((),) <%> (ra <.> rb)
276 default (.>) :: IsoFunctor sem => sem () -> sem a -> sem a
278 -- * Class 'SumFunctor'
280 -- | Beware that this is an @infixr@,
281 -- not @infixl@ like 'Control.Applicative.<|>';
282 -- this is to follow what is expected by 'ADT'.
283 class SumFunctor sem where
284 (<+>) :: sem a -> sem b -> sem (Either a b)
286 (<+>) = liftDerived2 (<+>)
288 FromDerived2 SumFunctor sem =>
293 -- | Like @(,)@ but @infixr@.
294 -- Mostly useful for clarity when using 'SumFunctor'.
295 pattern (:!:) :: a -> b -> (a, b)
302 {-# COMPLETE (:!:) #-}
304 -- * Class 'AlternativeFunctor'
306 -- | Beware that this is an @infixr@,
307 -- not @infixl@ like 'Control.Applicative.<|>';
308 -- this is to follow what is expected by 'ADT'.
309 class AlternativeFunctor sem where
310 (<|>) :: sem a -> sem a -> sem a
312 (<|>) = liftDerived2 (<|>)
314 FromDerived2 AlternativeFunctor sem =>
319 -- * Class 'Dicurryable'
320 class Dicurryable sem where
324 (args -..-> a) -> -- construction
325 (a -> Tuples args) -> -- destruction
328 dicurry args constr destr = liftDerived1 (dicurry args constr destr)
330 FromDerived1 Dicurryable sem =>
334 (a -> Tuples args) ->
344 Tuples args ~ EoT (ADT a) =>
345 (args ~ Args (args -..-> a)) =>
349 construct f = dicurry (Proxy :: Proxy args) f eotOfadt
351 -- * Class 'Dataable'
353 -- | Enable the contruction or deconstruction
354 -- of an 'ADT' (algebraic data type).
355 class Dataable a sem where
356 dataType :: sem (EoT (ADT a)) -> sem a
364 dataType = (<%>) (Iso adtOfeot eotOfadt)
366 -- * Class 'IfSemantic'
368 -- | 'IfSemantic' enables to change the 'Syntax' for a specific 'Semantic'.
370 -- Useful when a 'Semantic' does not implement some 'Syntax'es used by other 'Semantic's.
373 (thenSyntaxes :: [Syntax])
374 (elseSyntaxes :: [Syntax])
379 (Syntaxes thenSyntaxes thenSemantic => thenSemantic a) ->
380 (Syntaxes elseSyntaxes elseSemantic => elseSemantic a) ->
385 Syntaxes thenSyntaxes thenSemantic =>
386 IfSemantic thenSyntaxes elseSyntaxes thenSemantic thenSemantic
388 ifSemantic thenSyntax _elseSyntax = thenSyntax
390 Syntaxes elseSyntaxes elseSemantic =>
391 IfSemantic thenSyntaxes elseSyntaxes thenSemantic elseSemantic
393 ifSemantic _thenSyntax elseSyntax = elseSyntax
395 -- * Class 'Monoidable'
407 -- ** Class 'Emptyable'
408 class Emptyable sem where
410 empty = liftDerived empty
412 FromDerived Emptyable sem =>
415 -- ** Class 'Semigroupable'
416 class Semigroupable sem where
417 concat :: Semigroup a => sem (a -> a -> a)
418 concat = liftDerived concat
420 FromDerived Semigroupable sem =>
424 infixr 6 `concat`, <>
427 Unabstractable sem =>
433 (<>) x y = concat .@ x .@ y
435 -- ** Class 'Optionable'
436 class Optionable sem where
437 optional :: sem a -> sem (Maybe a)
438 optional = liftDerived1 optional
440 FromDerived1 Optionable sem =>
444 -- * Class 'Repeatable'
445 class Repeatable sem where
446 many0 :: sem a -> sem [a]
447 many1 :: sem a -> sem [a]
448 many0 = liftDerived1 many0
449 many1 = liftDerived1 many1
451 FromDerived1 Repeatable sem =>
455 FromDerived1 Repeatable sem =>
459 -- | Alias to 'many0'.
460 many :: Repeatable sem => sem a -> sem [a]
463 -- | Alias to 'many1'.
464 some :: Repeatable sem => sem a -> sem [a]
467 -- * Class 'Permutable'
468 class Permutable sem where
469 -- Use @TypeFamilyDependencies@ to help type-inference infer @(sem)@.
470 type Permutation (sem :: Semantic) = (r :: Semantic) | r -> sem
471 type Permutation sem = Permutation (Derived sem)
472 permutable :: Permutation sem a -> sem a
473 perm :: sem a -> Permutation sem a
474 noPerm :: Permutation sem ()
475 permWithDefault :: a -> sem a -> Permutation sem a
481 Permutation sem (Maybe a)
482 optionalPerm = permWithDefault Nothing Fun.. (<%>) (Iso Just fromJust)
486 ProductFunctor (Permutation sem) =>
489 Permutation sem (a, b)
490 x <&> y = perm x <.> y
498 ProductFunctor (Permutation sem) =>
501 Permutation sem (Maybe a, b)
502 x <?&> y = optionalPerm x <.> y
504 {-# INLINE (<?&>) #-}
511 ProductFunctor (Permutation sem) =>
514 Permutation sem ([a], b)
515 x <*&> y = permWithDefault [] (many1 x) <.> y
517 {-# INLINE (<*&>) #-}
524 ProductFunctor (Permutation sem) =>
527 Permutation sem ([a], b)
528 x <+&> y = perm (many1 x) <.> y
530 {-# INLINE (<+&>) #-}
532 -- * Class 'Voidable'
533 class Voidable sem where
534 -- | Useful to supply @(a)@ to a @(sem)@ consuming @(a)@,
535 -- for example in the format of a printing interpreter.
536 void :: a -> sem a -> sem ()
537 void = liftDerived1 Fun.. void
539 FromDerived1 Voidable sem =>
544 -- * Class 'Substractable'
545 class Substractable sem where
546 (<->) :: sem a -> sem b -> sem a
548 (<->) = liftDerived2 (<->)
550 FromDerived2 Substractable sem =>