1 {-# LANGUAGE AllowAmbiguousTypes #-}
3 {-# LANGUAGE ConstraintKinds #-}
4 {-# LANGUAGE GeneralizedNewtypeDeriving #-}
5 {-# LANGUAGE PatternSynonyms #-}
6 {-# LANGUAGE RankNTypes #-}
7 {-# LANGUAGE UndecidableInstances #-}
8 {-# OPTIONS_GHC -Wno-orphans #-}
10 module Literate.Web.Syntaxes (
11 module Literate.Web.Syntaxes,
23 import Control.Applicative (Applicative)
24 import Control.Monad (Monad)
25 import Control.Monad.Classes qualified as MC
26 import Control.Monad.Trans.Class as MT
27 import Data.Either (Either)
28 import Data.Bool (Bool (..))
29 import Data.ByteString.Lazy qualified as BSL
30 import Data.Function ((.))
31 import Data.Functor as Functor
32 import Data.Kind (Constraint, Type)
33 import Data.Typeable (Typeable)
34 import Literate.Web.Types.URL
35 import Symantic.Semantics (Reader (..))
36 import Symantic.Semantics.ToFer (
39 import Symantic.Syntaxes (
52 import Symantic.Syntaxes.Data (Data, SomeData (..))
53 import Symantic.Syntaxes.Derive
54 import System.FilePath (FilePath)
56 --deriving instance PathSegmentable sem => PathSegmentable (Reflector r sem)
57 --deriving instance Endable sem => Endable (Reflector r sem)
58 --deriving instance Capturable sem => Capturable (Reflector r sem)
60 -- * Class 'PathSegmentable'
62 -- | Syntax (final algebra) for expressing URL paths.
63 class PathSegmentable sem where
64 -- choosePathSegments :: Set [PathSegment] -> sem [PathSegment]
65 -- default choosePathSegments :: FromDerived PathSegmentable sem => Set [PathSegment] -> sem [PathSegment]
66 -- choosePathSegments = liftDerived . choosePathSegments
67 -- pathSegment :: PathSegment -> sem a -> sem a
69 pathSegment :: PathSegment -> sem ()
70 default pathSegment :: FromDerived PathSegmentable sem => PathSegment -> sem ()
71 -- default pathSegment :: IsoFunctor sem => PathSegment -> sem ()
72 -- pathSegment s = Iso (const ()) (const s) <%> pathSegments (Set.singleton s)
73 pathSegment = liftDerived . pathSegment
75 instance (PathSegmentable sem, Functor sem) => PathSegmentable (ToFer sem) where
78 { tuplesOfFunctions = (Functor.<$ eot)
79 , eithersOfTuples = eot
84 -- pathSegments :: Set PathSegment -> sem PathSegment
85 -- default pathSegments :: FromDerived PathSegmentable sem => Set PathSegment -> sem PathSegment
86 -- pathSegments = liftDerived . pathSegments
88 -- default pathSegments :: IsoFunctor sem => Set PathSegment -> sem PathSegment
89 -- pathSegments s = Iso List.head pure <%> choosePathSegments (Set.mapMonotonic pure s)
91 instance (PathSegmentable sem, IsoFunctor sem) => PathSegmentable (Reader r sem)
93 -- | Pattern-matchable encoding (initial algebra) of 'PathSegmentable'.
94 data instance Data PathSegmentable sem a where
95 PathSegment :: PathSegment -> Data PathSegmentable sem ()
97 -- PathSegments :: Set PathSegment -> Data PathSegmentable sem PathSegment
99 -- ChoosePathSegments :: Set [PathSegment] -> Data PathSegmentable sem [PathSegment]
101 -- | Initial to final algebra.
102 instance PathSegmentable sem => Derivable (Data PathSegmentable sem) where
104 PathSegment x -> pathSegment x
106 -- PathSegments x -> pathSegments x
108 -- ChoosePathSegments x -> choosePathSegments x
110 -- | Final to initial algebra.
111 instance PathSegmentable sem => PathSegmentable (SomeData sem) where
112 pathSegment = SomeData . PathSegment
114 -- pathSegments = SomeData . PathSegments
116 --choosePathSegments = SomeData . ChoosePathSegments
118 -- | Convenient alias for an @index.html@ page.
119 index :: PathSegmentable sem => sem ()
120 index = pathSegment "index.html"
122 -- | Convenient alias for prefixing with a 'pathSegment'.
124 ProductFunctor sem =>
125 PathSegmentable sem =>
129 (</>) n = (pathSegment n .>)
133 -- * Class 'Copyable'
134 class Copyable sem where
135 copy :: FilePath -> sem ()
137 -- * Class 'Encodable'
138 class Encodable fmt a where
139 encode :: a -> BSL.ByteString
141 -- * Class 'Capturable'
142 class Capturable a sem where
143 type Captured a sem :: Type
144 type Captured a sem = a
145 capturePathSegment :: PathSegment -> sem (Captured a sem)
146 default capturePathSegment ::
147 Captured a (Derived sem) ~ Captured a sem =>
148 FromDerived (Capturable a) sem =>
151 capturePathSegment = liftDerived . capturePathSegment @a
153 -- | The @('IsToF' a ~ 'False)@ constraint
154 -- disables capturing tuples or functions.
157 , IsToF (Captured a sem) ~ 'False
159 ) => Capturable a (ToFer sem) where
160 type Captured a (ToFer sem) = Captured a sem
161 capturePathSegment _n =
163 { tuplesOfFunctions = \next -> next <$> capturePathSegment @a _n
164 , eithersOfTuples = capturePathSegment @a _n
167 -- | @('CapturedExtra' a extra)@ is useful to add @(extra)@ data
168 -- to a 'Captured' value, eg. when using the 'Compiler' semantic,
169 -- to add the content of a page whose name was captured
170 -- directly available when iterating all the pages,
171 -- hence avoiding to lookup that content as would be done
172 -- when using the 'Server' semantic.
173 newtype CapturedExtra a extra = CapturedExtra (Either (a, extra) a)
175 --instance Capturable sem => Capturable (Reader r sem)
176 data instance Data (Capturable a) sem r where
177 CapturePathSegment ::
180 Data (Capturable a) sem (Captured a sem)
181 instance Capturable a sem => Derivable (Data (Capturable a) sem) where
183 CapturePathSegment n -> capturePathSegment @a n
184 instance (Capturable a sem, Typeable a) => Capturable a (SomeData sem) where
185 type Captured a (SomeData sem) = Captured a sem
186 capturePathSegment = SomeData . CapturePathSegment @a
188 -- * Class 'Fileable'
189 class Fileable sem where
190 type FileableConstraint sem :: Type -> Constraint
192 dynamic :: FileableConstraint sem a => sem a
194 -- * Class 'Responsable'
195 class Responsable a ts m result sem where
196 response :: sem (Endpoint result (Response ts m a))
198 -- ** Type 'Response'
199 newtype Response (ts :: [Type]) m a = Response {unResponse :: m a}
200 deriving (Functor, Applicative, Monad)
201 type instance MC.CanDo (Response ts m) eff = 'False
202 instance MT.MonadTrans (Response ts) where lift = Response