2 {-# LANGUAGE AllowAmbiguousTypes #-}
4 {-# LANGUAGE DataKinds #-}
6 {-# LANGUAGE PatternSynonyms #-}
8 {-# LANGUAGE RankNTypes #-}
10 {-# LANGUAGE TypeFamilyDependencies #-}
12 {-# LANGUAGE UndecidableInstances #-}
14 -- | Combinators in this module conflict with usual ones from the @Prelude@
15 -- hence they are meant to be imported either explicitely or qualified.
16 module Symantic.Syntaxes.Classes where
18 import Control.Category qualified as Cat
19 import Data.Bool (Bool (..))
20 import Data.Char (Char)
21 import Data.Either (Either (..))
23 import Data.Function qualified as Fun
25 import Data.Kind (Constraint)
26 import Data.Maybe (Maybe (..), fromJust)
27 import Data.Proxy (Proxy (..))
28 import Data.Semigroup (Semigroup)
29 import Data.String (String)
30 import Data.Tuple qualified as Tuple
31 import GHC.Generics (Generic)
32 import Numeric.Natural (Natural)
34 import Symantic.Syntaxes.CurryN
35 import Symantic.Syntaxes.Derive
36 import Symantic.Syntaxes.EithersOfTuples
39 type Syntax = Semantic -> Constraint
41 -- ** Type family 'Syntaxes'
43 -- | Merge several 'Syntax'es into a single one.
45 -- Useful in 'IfSemantic' or 'All'.
46 type family Syntaxes (syns :: [Syntax]) (sem :: Semantic) :: Constraint where
47 Syntaxes '[] sem = (() :: Constraint)
48 Syntaxes (syn ': syns) sem = ((syn sem, Syntaxes syns sem) :: Constraint)
50 -- * Class 'Abstractable'
51 class Abstractable sem where
52 -- | Lambda term abstraction, in HOAS (Higher-Order Abstract Syntax) style.
53 lam :: (sem a -> sem b) -> sem (a -> b)
54 lam f = liftDerived (lam (derive Fun.. f Fun.. liftDerived))
56 FromDerived Abstractable sem =>
61 -- ** Class 'Abstractable1'
62 class Abstractable1 sem where
63 -- | Like 'lam' but whose argument must be used only once,
64 -- hence safe to beta-reduce (inline) without duplicating work.
65 lam1 :: (sem a -> sem b) -> sem (a -> b)
66 lam1 f = liftDerived (lam1 (derive Fun.. f Fun.. liftDerived))
68 FromDerived Abstractable1 sem =>
74 class Varable sem where
75 -- | Mark the use of a variable.
77 var = liftDerived1 var
78 default var :: FromDerived1 Varable sem => sem a -> sem a
80 -- ** Class 'Unabstractable'
81 class Unabstractable sem where
82 -- | Application, aka. unabstract.
83 (.@) :: sem (a -> b) -> sem a -> sem b
86 (.@) = liftDerived2 (.@)
88 FromDerived2 Unabstractable sem =>
93 -- ** Class 'Functionable'
94 class Functionable sem where
95 const :: sem (a -> b -> a)
96 flip :: sem ((a -> b -> c) -> b -> a -> c)
98 (.) :: sem ((b -> c) -> (a -> b) -> a -> c)
100 ($) :: sem ((a -> b) -> a -> b)
102 const = liftDerived const
103 flip = liftDerived flip
105 (.) = liftDerived (.)
106 ($) = liftDerived ($)
108 FromDerived Functionable sem =>
111 FromDerived Functionable sem =>
112 sem ((a -> b -> c) -> b -> a -> c)
114 FromDerived Functionable sem =>
117 FromDerived Functionable sem =>
118 sem ((b -> c) -> (a -> b) -> a -> c)
120 FromDerived Functionable sem =>
121 sem ((a -> b) -> a -> b)
123 -- * Class 'Anythingable'
124 class Anythingable sem where
125 anything :: sem a -> sem a
128 -- * Class 'Bottomable'
129 class Bottomable sem where
132 -- * Class 'Constantable'
133 class Constantable c sem where
134 constant :: c -> sem c
135 constant = liftDerived Fun.. constant
137 FromDerived (Constantable c) sem =>
141 -- * Class 'Eitherable'
142 class Eitherable sem where
143 either :: sem ((l -> a) -> (r -> a) -> Either l r -> a)
144 left :: sem (l -> Either l r)
145 right :: sem (r -> Either l r)
146 either = liftDerived either
147 left = liftDerived left
148 right = liftDerived right
150 FromDerived Eitherable sem =>
151 sem ((l -> a) -> (r -> a) -> Either l r -> a)
153 FromDerived Eitherable sem =>
154 sem (l -> Either l r)
156 FromDerived Eitherable sem =>
157 sem (r -> Either l r)
159 -- * Class 'Equalable'
160 class Equalable sem where
161 equal :: Eq a => sem (a -> a -> Bool)
162 equal = liftDerived equal
164 FromDerived Equalable sem =>
171 Unabstractable sem =>
177 (==) x y = equal .@ x .@ y
179 -- * Class 'IfThenElseable'
180 class IfThenElseable sem where
181 ifThenElse :: sem Bool -> sem a -> sem a -> sem a
182 ifThenElse = liftDerived3 ifThenElse
183 default ifThenElse ::
184 FromDerived3 IfThenElseable sem =>
190 -- * Class 'Inferable'
191 class Inferable a sem where
193 default infer :: FromDerived (Inferable a) sem => sem a
194 infer = liftDerived infer
196 unit :: Inferable () sem => sem ()
198 bool :: Inferable Bool sem => sem Bool
200 char :: Inferable Char sem => sem Char
202 int :: Inferable Int sem => sem Int
204 natural :: Inferable Natural sem => sem Natural
206 string :: Inferable String sem => sem String
209 -- * Class 'Listable'
210 class Listable sem where
211 cons :: sem (a -> [a] -> [a])
213 cons = liftDerived cons
214 nil = liftDerived nil
216 FromDerived Listable sem =>
217 sem (a -> [a] -> [a])
219 FromDerived Listable sem =>
222 -- * Class 'Maybeable'
223 class Maybeable sem where
224 nothing :: sem (Maybe a)
225 just :: sem (a -> Maybe a)
226 nothing = liftDerived nothing
227 just = liftDerived just
229 FromDerived Maybeable sem =>
232 FromDerived Maybeable sem =>
235 -- * Class 'IsoFunctor'
236 class IsoFunctor sem where
237 (<%>) :: Iso a b -> sem a -> sem b
239 (<%>) iso = liftDerived1 (iso <%>)
241 FromDerived1 IsoFunctor sem =>
247 data Iso a b = Iso {a2b :: a -> b, b2a :: b -> a}
248 instance Cat.Category Iso where
249 id = Iso Cat.id Cat.id
250 f . g = Iso (a2b f Cat.. a2b g) (b2a g Cat.. b2a f)
252 -- * Class 'ProductFunctor'
254 -- | Beware that this is an @infixr@,
255 -- not @infixl@ like 'Control.Applicative.<*>';
256 -- this is to follow what is expected by 'ADT'.
257 class ProductFunctor sem where
258 (<.>) :: sem a -> sem b -> sem (a, b)
260 (<.>) = liftDerived2 (<.>)
262 FromDerived2 ProductFunctor sem =>
266 (<.) :: sem a -> sem () -> sem a
268 ra <. rb = Iso Tuple.fst (,()) <%> (ra <.> rb)
269 default (<.) :: IsoFunctor sem => sem a -> sem () -> sem a
270 (.>) :: sem () -> sem a -> sem a
272 ra .> rb = Iso Tuple.snd ((),) <%> (ra <.> rb)
273 default (.>) :: IsoFunctor sem => sem () -> sem a -> sem a
275 -- * Class 'SumFunctor'
277 -- | Beware that this is an @infixr@,
278 -- not @infixl@ like 'Control.Applicative.<|>';
279 -- this is to follow what is expected by 'ADT'.
280 class SumFunctor sem where
281 (<+>) :: sem a -> sem b -> sem (Either a b)
283 (<+>) = liftDerived2 (<+>)
285 FromDerived2 SumFunctor sem =>
290 -- | Like @(,)@ but @infixr@.
291 -- Mostly useful for clarity when using 'SumFunctor'.
292 pattern (:!:) :: a -> b -> (a, b)
299 {-# COMPLETE (:!:) #-}
301 -- * Class 'AlternativeFunctor'
303 -- | Beware that this is an @infixr@,
304 -- not @infixl@ like 'Control.Applicative.<|>';
305 -- this is to follow what is expected by 'ADT'.
306 class AlternativeFunctor sem where
307 (<|>) :: sem a -> sem a -> sem a
309 (<|>) = liftDerived2 (<|>)
311 FromDerived2 AlternativeFunctor sem =>
316 -- * Class 'Dicurryable'
317 class Dicurryable sem where
321 (args -..-> a) -> -- construction
322 (a -> Tuples args) -> -- destruction
325 dicurry args constr destr = liftDerived1 (dicurry args constr destr)
327 FromDerived1 Dicurryable sem =>
331 (a -> Tuples args) ->
341 Tuples args ~ EoT (ADT a) =>
342 (args ~ Args (args -..-> a)) =>
346 construct f = dicurry (Proxy :: Proxy args) f eotOfadt
348 -- * Class 'Dataable'
350 -- | Enable the contruction or deconstruction
351 -- of an 'ADT' (algebraic data type).
352 class Dataable a sem where
353 dataType :: sem (EoT (ADT a)) -> sem a
361 dataType = (<%>) (Iso adtOfeot eotOfadt)
363 -- * Class 'IfSemantic'
365 -- | 'IfSemantic' enables to change the 'Syntax' for a specific 'Semantic'.
367 -- Useful when a 'Semantic' does not implement some 'Syntax'es used by other 'Semantic's.
370 (thenSyntaxes :: [Syntax])
371 (elseSyntaxes :: [Syntax])
376 (Syntaxes thenSyntaxes thenSemantic => thenSemantic a) ->
377 (Syntaxes elseSyntaxes elseSemantic => elseSemantic a) ->
382 Syntaxes thenSyntaxes thenSemantic =>
383 IfSemantic thenSyntaxes elseSyntaxes thenSemantic thenSemantic
385 ifSemantic thenSyntax _elseSyntax = thenSyntax
387 Syntaxes elseSyntaxes elseSemantic =>
388 IfSemantic thenSyntaxes elseSyntaxes thenSemantic elseSemantic
390 ifSemantic _thenSyntax elseSyntax = elseSyntax
392 -- * Class 'Monoidable'
404 -- ** Class 'Emptyable'
405 class Emptyable sem where
407 empty = liftDerived empty
409 FromDerived Emptyable sem =>
412 -- ** Class 'Semigroupable'
413 class Semigroupable sem where
414 concat :: Semigroup a => sem (a -> a -> a)
415 concat = liftDerived concat
417 FromDerived Semigroupable sem =>
421 infixr 6 `concat`, <>
424 Unabstractable sem =>
430 (<>) x y = concat .@ x .@ y
432 -- ** Class 'Optionable'
433 class Optionable sem where
434 optional :: sem a -> sem (Maybe a)
435 optional = liftDerived1 optional
437 FromDerived1 Optionable sem =>
441 -- * Class 'Repeatable'
442 class Repeatable sem where
443 many0 :: sem a -> sem [a]
444 many1 :: sem a -> sem [a]
445 many0 = liftDerived1 many0
446 many1 = liftDerived1 many1
448 FromDerived1 Repeatable sem =>
452 FromDerived1 Repeatable sem =>
456 -- | Alias to 'many0'.
457 many :: Repeatable sem => sem a -> sem [a]
460 -- | Alias to 'many1'.
461 some :: Repeatable sem => sem a -> sem [a]
464 -- * Class 'Permutable'
465 class Permutable sem where
466 -- Use @TypeFamilyDependencies@ to help type-inference infer @(sem)@.
467 type Permutation (sem :: Semantic) = (r :: Semantic) | r -> sem
468 type Permutation sem = Permutation (Derived sem)
469 permutable :: Permutation sem a -> sem a
470 perm :: sem a -> Permutation sem a
471 noPerm :: Permutation sem ()
472 permWithDefault :: a -> sem a -> Permutation sem a
478 Permutation sem (Maybe a)
479 optionalPerm = permWithDefault Nothing Fun.. (<%>) (Iso Just fromJust)
483 ProductFunctor (Permutation sem) =>
486 Permutation sem (a, b)
487 x <&> y = perm x <.> y
495 ProductFunctor (Permutation sem) =>
498 Permutation sem (Maybe a, b)
499 x <?&> y = optionalPerm x <.> y
501 {-# INLINE (<?&>) #-}
508 ProductFunctor (Permutation sem) =>
511 Permutation sem ([a], b)
512 x <*&> y = permWithDefault [] (many1 x) <.> y
514 {-# INLINE (<*&>) #-}
521 ProductFunctor (Permutation sem) =>
524 Permutation sem ([a], b)
525 x <+&> y = perm (many1 x) <.> y
527 {-# INLINE (<+&>) #-}
529 -- * Class 'Voidable'
530 class Voidable sem where
531 -- | Useful to supply @(a)@ to a @(sem)@ consuming @(a)@,
532 -- for example in the format of a printing interpreter.
533 void :: a -> sem a -> sem ()
534 void = liftDerived1 Fun.. void
536 FromDerived1 Voidable sem =>
541 -- * Class 'Substractable'
542 class Substractable sem where
543 (<->) :: sem a -> sem b -> sem a
545 (<->) = liftDerived2 (<->)
547 FromDerived2 Substractable sem =>