@@ -1,3 +1,4 @@
1{-# LANGUAGE ApplicativeDo #-}
2{-# LANGUAGE ApplicativeDo #-}
2{-# LANGUAGE ConstraintKinds #-}
3{-# LANGUAGE ConstraintKinds #-}
3{-# LANGUAGE EmptyCase #-}
4{-# LANGUAGE EmptyCase #-}
@@ -5,7 +6,6 @@
5{-# LANGUAGE FlexibleContexts #-}
6{-# LANGUAGE FlexibleContexts #-}
6{-# LANGUAGE FlexibleInstances #-}
7{-# LANGUAGE FlexibleInstances #-}
7{-# LANGUAGE GADTs #-}
8{-# LANGUAGE GADTs #-}
8−{-# LANGUAGE KindSignatures #-}
9{-# LANGUAGE LambdaCase #-}
9{-# LANGUAGE LambdaCase #-}
10{-# LANGUAGE MultiParamTypeClasses #-}
10{-# LANGUAGE MultiParamTypeClasses #-}
11{-# LANGUAGE OverloadedStrings #-}
11{-# LANGUAGE OverloadedStrings #-}
@@ -61,6 +61,7 @@ module Obelisk.Route
61 , justEncoder
61 , justEncoder
62 , nothingEncoder
62 , nothingEncoder
63 , isoEncoder
63 , isoEncoder
64 , wrappedEncoder
65 , wrappedEncoder
65 , unwrappedEncoder
66 , unwrappedEncoder
66 , listToNonEmptyEncoder
67 , listToNonEmptyEncoder
@@ -69,6 +70,7 @@ module Obelisk.Route
69 , toListMapEncoder
70 , toListMapEncoder
70 , shadowEncoder
71 , shadowEncoder
71 , prismEncoder
72 , prismEncoder
74 , obeliskRouteEncoder
76 , obeliskRouteEncoder
@@ -133,20 +135,24 @@ import Control.Lens
137 , Wrapped (..)
140 , Wrapped (..)
139import Control.Monad.Except
142import Control.Monad.Except
140−import Control.Monad.Writer (execWriter, tell)
141import qualified Control.Monad.State.Strict as State
143import qualified Control.Monad.State.Strict as State
142import Control.Monad.Trans (lift)
144import Control.Monad.Trans (lift)
145+import Control.Monad.Writer (execWriter, tell)
146+import Data.Aeson (FromJSON, ToJSON)
147+import qualified Data.Aeson as Aeson
143import qualified Data.ByteString as BS
148import qualified Data.ByteString as BS
144import qualified Data.ByteString.Lazy as BSL
149import qualified Data.ByteString.Lazy as BSL
145−import Data.Dependent.Sum (DSum (..))
146import Data.Dependent.Map (DMap)
150import Data.Dependent.Map (DMap)
147import qualified Data.Dependent.Map as DMap
151import qualified Data.Dependent.Map as DMap
152+import Data.Dependent.Sum (DSum (..))
148import Data.Either.Validation (Validation (..))
153import Data.Either.Validation (Validation (..))
149import Data.Foldable
154import Data.Foldable
155+import Data.Functor (($>))
150import Data.Functor.Sum
156import Data.Functor.Sum
151import Data.GADT.Compare
157import Data.GADT.Compare
152import Data.GADT.Compare.TH
158import Data.GADT.Compare.TH
@@ -159,19 +165,18 @@ import Data.Monoid ((<>))
159import Data.Set (Set)
165import Data.Set (Set)
160import qualified Data.Set as Set
166import qualified Data.Set as Set
161import Data.Some (Some(Some))
167import Data.Some (Some(Some))
168+import Data.Tabulation
162import Data.Text (Text)
169import Data.Text (Text)
163−import Data.Text.Lens (IsText, packed, unpacked)
164import qualified Data.Text as T
170import qualified Data.Text as T
165import qualified Data.Text.Encoding as T
171import qualified Data.Text.Encoding as T
172+import Data.Text.Lens (IsText, packed, unpacked)
173+import Data.Type.Equality
166import Data.Universe
174import Data.Universe
167import Data.Universe.Some
175import Data.Universe.Some
168import Network.HTTP.Types.URI
176import Network.HTTP.Types.URI
169import qualified Numeric.Lens
177import qualified Numeric.Lens
170import Obelisk.Route.TH
178import Obelisk.Route.TH
171import Text.Read (readMaybe)
179import Text.Read (readMaybe)
172−import Data.Tabulation
173−import qualified Data.Aeson as Aeson
174−import Data.Aeson (FromJSON, ToJSON)
176-- Design goals:
181-- Design goals:
177-- No start-up time on the frontend (not yet met)
182-- No start-up time on the frontend (not yet met)
@@ -221,7 +226,19 @@ hoistR f (x :=> Identity y) = f x :/ y
221infixr 5 :.
226infixr 5 :.
222type (:.) = (,)
227type (:.) = (,)
229+#ifdef __GLASGOW_HASKELL__
230+#if __GLASGOW_HASKELL__ >= 810
224{-# COMPLETE (:.) #-}
231{-# COMPLETE (:.) #-}
234+ [ "Use of this pattern in GHC < 8.10 will result in spurious non-exhaustive warnings at every use site."
235+ , "We cannot provide a COMPLETE pragma to silence these due to a GHC bug: https://gitlab.haskell.org/ghc/ghc/issues/17729."
236+ , "The bug hides incompleteness warnings for all two tuples when the COMPLETE pattern is in scope."
237+ , "Instead, you should use (,) directly until you can switch to GHC >= 8.10, where the COMPLETE pragma is reinstated."
225pattern (:.) :: a -> b -> a :. b
242pattern (:.) :: a -> b -> a :. b
226pattern a :. b = (a, b)
243pattern a :. b = (a, b)
@@ -287,13 +304,13 @@ decode e x = runIdentity (tryDecode e x)
287-- | Once an 'Encoder' has been checked, so that its check monad has become 'Identity', even if the same is not true of the
304-- | Once an 'Encoder' has been checked, so that its check monad has become 'Identity', even if the same is not true of the
288-- parse monad, we may still attempt to decode with it in its parse monad.
305-- parse monad, we may still attempt to decode with it in its parse monad.
289tryDecode :: Encoder Identity parse decoded encoded -> encoded -> parse decoded
306tryDecode :: Encoder Identity parse decoded encoded -> encoded -> parse decoded
290−tryDecode (Encoder (Identity impl)) x = _encoderImpl_decode impl x
307+tryDecode (Encoder (Identity impl)) = _encoderImpl_decode impl
292-- | Similar to 'decode', once an encoder has been checked so that its check monad is Identity, it
309-- | Similar to 'decode', once an encoder has been checked so that its check monad is Identity, it
293-- can be used to actually encode by using this. Note that while there's no constraint on the parse monad here,
310-- can be used to actually encode by using this. Note that while there's no constraint on the parse monad here,
294-- one should usually be applying decode and encode to the same 'Encoder'
311-- one should usually be applying decode and encode to the same 'Encoder'
295encode :: Encoder Identity parse decoded encoded -> decoded -> encoded
312encode :: Encoder Identity parse decoded encoded -> decoded -> encoded
296−encode (Encoder (Identity impl)) x = _encoderImpl_encode impl x
313+encode (Encoder (Identity impl)) = _encoderImpl_encode impl
298-- | This is a primitive used to build encoders which can't fail to check. It should not be used unless one is
315-- | This is a primitive used to build encoders which can't fail to check. It should not be used unless one is
299-- reasonably certain that the law given for 'EncoderImpl' above holds.
316-- reasonably certain that the law given for 'EncoderImpl' above holds.
@@ -340,7 +357,7 @@ instance Monad parse => Bifunctor (,) (EncoderImpl parse) (EncoderImpl parse) (E
342instance (Monad parse, Applicative check) => Braided (Encoder check parse) (,) where
359instance (Monad parse, Applicative check) => Braided (Encoder check parse) (,) where
343− braid = isoEncoder (iso swap swap)
360+ braid = viewEncoder (iso swap swap)
346instance (Applicative check, Monad parse) => PFunctor (,) (Encoder check parse) (Encoder check parse) where
363instance (Applicative check, Monad parse) => PFunctor (,) (Encoder check parse) (Encoder check parse) where
@@ -376,11 +393,11 @@ instance (Monad parse, Applicative check) => Bifunctor Either (Encoder check par
376 bimap f g = Encoder $ liftA2 bimap (unEncoder f) (unEncoder g)
393 bimap f g = Encoder $ liftA2 bimap (unEncoder f) (unEncoder g)
378instance (Applicative check, Monad parse) => Associative (Encoder check parse) Either where
395instance (Applicative check, Monad parse) => Associative (Encoder check parse) Either where
379− associate = isoEncoder (iso (associate @(->) @Either) disassociate)
396+ associate = viewEncoder (iso (associate @(->) @Either) disassociate)
380− disassociate = isoEncoder (iso disassociate associate)
397+ disassociate = viewEncoder (iso disassociate associate)
382instance (Monad parse, Applicative check) => Braided (Encoder check parse) Either where
399instance (Monad parse, Applicative check) => Braided (Encoder check parse) Either where
383− braid = isoEncoder (iso swap swap)
400+ braid = viewEncoder (iso swap swap)
@@ -434,17 +451,17 @@ instance (Applicative check, Monad parse) => Monoidal (Encoder check parse) (,)
434--------------------------------------------------------------------------------
451--------------------------------------------------------------------------------
436-- | Given a valid 'Iso' from lens, construct an 'Encoder'
453-- | Given a valid 'Iso' from lens, construct an 'Encoder'
437−isoEncoder :: (Applicative check, Applicative parse) => Iso' a b -> Encoder check parse a b
454+viewEncoder :: (Applicative check, Applicative parse) => Iso' a b -> Encoder check parse a b
438−isoEncoder f = unsafeMkEncoder $ EncoderImpl
455+viewEncoder f = unsafeMkEncoder $ EncoderImpl
439 { _encoderImpl_encode = view f
456 { _encoderImpl_encode = view f
440 , _encoderImpl_decode = pure . view (from f)
457 , _encoderImpl_decode = pure . view (from f)
443wrappedEncoder :: (Wrapped a, Applicative check, Applicative parse) => Encoder check parse (Unwrapped a) a
460wrappedEncoder :: (Wrapped a, Applicative check, Applicative parse) => Encoder check parse (Unwrapped a) a
444−wrappedEncoder = isoEncoder $ from _Wrapped'
461+wrappedEncoder = viewEncoder $ from _Wrapped'
446unwrappedEncoder :: (Wrapped a, Applicative check, Applicative parse) => Encoder check parse a (Unwrapped a)
463unwrappedEncoder :: (Wrapped a, Applicative check, Applicative parse) => Encoder check parse a (Unwrapped a)
447−unwrappedEncoder = isoEncoder _Wrapped'
464+unwrappedEncoder = viewEncoder $ _Wrapped'
449maybeToEitherEncoder :: (Applicative check, Applicative parse) => Encoder check parse (Maybe a) (Either () a)
466maybeToEitherEncoder :: (Applicative check, Applicative parse) => Encoder check parse (Maybe a) (Either () a)
450maybeToEitherEncoder = unsafeMkEncoder $ EncoderImpl
467maybeToEitherEncoder = unsafeMkEncoder $ EncoderImpl
@@ -469,11 +486,11 @@ maybeEncoder f g = shadowEncoder f g . maybeToEitherEncoder
470-- | Encode a value by simply applying 'Just'
487-- | Encode a value by simply applying 'Just'
471justEncoder :: (Applicative check, MonadError Text parse) => Encoder check parse a (Maybe a)
488justEncoder :: (Applicative check, MonadError Text parse) => Encoder check parse a (Maybe a)
472−justEncoder = prismEncoder _Just
489+justEncoder = reviewEncoder _Just
474-- | Encode () to 'Nothing'.
491-- | Encode () to 'Nothing'.
475nothingEncoder :: (Applicative check, MonadError Text parse) => Encoder check parse () (Maybe a)
492nothingEncoder :: (Applicative check, MonadError Text parse) => Encoder check parse () (Maybe a)
476−nothingEncoder = prismEncoder _Nothing
493+nothingEncoder = reviewEncoder _Nothing
478someConstEncoder :: (Applicative check, Applicative parse) => Encoder check parse (Some (Const a)) a
495someConstEncoder :: (Applicative check, Applicative parse) => Encoder check parse (Some (Const a)) a
479someConstEncoder = unsafeMkEncoder $ EncoderImpl
496someConstEncoder = unsafeMkEncoder $ EncoderImpl
@@ -538,11 +555,11 @@ pathComponentEncoder f = Encoder $ do
538 unEncoder (pathComponentEncoderImpl (enum1Encoder (extractPathSegment . f)) f')
555 unEncoder (pathComponentEncoderImpl (enum1Encoder (extractPathSegment . f)) f')
540pathComponentEncoderImpl :: forall check parse p. (Monad check, Monad parse)
557pathComponentEncoderImpl :: forall check parse p. (Monad check, Monad parse)
541− => (Encoder check parse (Some p) (Maybe Text))
558+ => Encoder check parse (Some p) (Maybe Text)
542 -> (forall a. p a -> Encoder Identity parse a PageName)
559 -> (forall a. p a -> Encoder Identity parse a PageName)
543 -> Encoder check parse (R p) PageName
560 -> Encoder check parse (R p) PageName
544−pathComponentEncoderImpl this rest =
561+pathComponentEncoderImpl =
545− chainEncoder (lensEncoder (\(_, b) a -> (a, b)) Prelude.fst consEncoder) this rest
562+ chainEncoder (lensEncoder (\(_, b) a -> (a, b)) Prelude.fst consEncoder)
547--NOTE: Naming convention in this module is to always talk about things in the *encoding* direction, never in the *decoding* direction
564--NOTE: Naming convention in this module is to always talk about things in the *encoding* direction, never in the *decoding* direction
@@ -798,7 +815,7 @@ rPrism
798 :: forall f f'
815 :: forall f f'
799 . (forall a. Prism' (f a) (f' a))
816 . (forall a. Prism' (f a) (f' a))
800 -> Prism' (R f) (R f')
817 -> Prism' (R f) (R f')
801−rPrism p = dSumPrism p
818+rPrism = dSumPrism
803dSumPrism'
820dSumPrism'
804 :: forall f g a
821 :: forall f g a
@@ -810,7 +827,7 @@ dSumGEqPrism
812 -> Prism' (DSum f g) (g a)
829 -> Prism' (DSum f g) (g a)
813−dSumGEqPrism variant = dSumPrism' $ prism' (\Refl -> variant) (\x -> geq variant x)
830+dSumGEqPrism variant = dSumPrism' $ prism' (\Refl -> variant) (geq variant)
815-- | Given a 'tag :: f a', make a prism for 'R f'. This generalizes the usual
832-- | Given a 'tag :: f a', make a prism for 'R f'. This generalizes the usual
816-- prisms for a sum type (the ones that 'mkPrisms' would make), just as 'R'
833-- prisms for a sum type (the ones that 'mkPrisms' would make), just as 'R'
@@ -827,12 +844,19 @@ _R variant = dSumGEqPrism variant . iso runIdentity Identity
828-- | An encoder that only works on the items available via the prism. An error will be thrown in the parse monad
845-- | An encoder that only works on the items available via the prism. An error will be thrown in the parse monad
829-- if the prism doesn't match.
846-- if the prism doesn't match.
848+-- Note that a 'Prism' from @a@ to @b@ will produce an 'Encoder' from @b@ to @a@
849+-- (i.e. 'reviewEncoder' is a contravariant functor from the category of prisms to the category of encoders),
850+-- just like 'review' produces a function @b -> a@. This is because 'Prism's extract values, in a way that might
851+-- fail, in their forward direction and inject values, in a way that cannot fail, in their reverse direction;
852+-- whereas 'Encoder's encode, which cannot fail, in their forward direction, and decode, which can fail, in their
853+-- reverse direction. In short @reviewEncoder (f . g) = reviewEncoder g . reviewEncoder f@.
830−prismEncoder :: (Applicative check, MonadError Text parse) => Prism' b a -> Encoder check parse a b
854+reviewEncoder :: (Applicative check, MonadError Text parse) => Prism' b a -> Encoder check parse a b
831−prismEncoder p = unsafeMkEncoder $ EncoderImpl
855+reviewEncoder p = unsafeMkEncoder $ EncoderImpl
832− { _encoderImpl_encode = (^. re p)
856+ { _encoderImpl_encode = review p
833 , _encoderImpl_decode = \r -> case r ^? p of
857 , _encoderImpl_decode = \r -> case r ^? p of
834 Just a -> pure a
858 Just a -> pure a
835− Nothing -> throwError "prismEncoder: value is not present in the prism"
859+ Nothing -> throwError "reviewEncoder: value is not present in the prism"
838-- | A URL path and query string, in which trailing slashes don't matter in the path
862-- | A URL path and query string, in which trailing slashes don't matter in the path
@@ -970,9 +994,9 @@ obeliskRouteSegment r appRouteSegment = case r of
970-- be combined with other such segment encoders before 'pathComponentEncoder' turns it into a proper 'Encoder'.
994-- be combined with other such segment encoders before 'pathComponentEncoder' turns it into a proper 'Encoder'.
971resourceRouteSegment :: (MonadError Text check, MonadError Text parse) => ResourceRoute a -> SegmentResult check parse a
995resourceRouteSegment :: (MonadError Text check, MonadError Text parse) => ResourceRoute a -> SegmentResult check parse a
972resourceRouteSegment = \case
996resourceRouteSegment = \case
973− ResourceRoute_Static -> PathSegment "static" $ pathOnlyEncoderIgnoringQuery
997+ ResourceRoute_Static -> PathSegment "static" pathOnlyEncoderIgnoringQuery
974− ResourceRoute_Ghcjs -> PathSegment "ghcjs" $ pathOnlyEncoder
998+ ResourceRoute_Ghcjs -> PathSegment "ghcjs" pathOnlyEncoder
975− ResourceRoute_JSaddleWarp -> PathSegment "jsaddle" $ jsaddleWarpRouteEncoder
999+ ResourceRoute_JSaddleWarp -> PathSegment "jsaddle" jsaddleWarpRouteEncoder
976 ResourceRoute_Version -> PathSegment "version" $ unitEncoder mempty
1000 ResourceRoute_Version -> PathSegment "version" $ unitEncoder mempty
978data JSaddleWarpRoute :: * -> * where
1002data JSaddleWarpRoute :: * -> * where
@@ -984,7 +1008,7 @@ jsaddleWarpRouteEncoder :: (MonadError Text check, MonadError Text parse) => Enc
984jsaddleWarpRouteEncoder = pathComponentEncoder $ \case
1008jsaddleWarpRouteEncoder = pathComponentEncoder $ \case
985 JSaddleWarpRoute_JavaScript -> PathSegment "jsaddle.js" $ unitEncoder mempty
1009 JSaddleWarpRoute_JavaScript -> PathSegment "jsaddle.js" $ unitEncoder mempty
986 JSaddleWarpRoute_WebSocket -> PathEnd $ unitEncoder mempty
1010 JSaddleWarpRoute_WebSocket -> PathEnd $ unitEncoder mempty
987− JSaddleWarpRoute_Sync -> PathSegment "sync" $ pathOnlyEncoder
1011+ JSaddleWarpRoute_Sync -> PathSegment "sync" pathOnlyEncoder
989instance GShow appRoute => GShow (ObeliskRoute appRoute) where
1013instance GShow appRoute => GShow (ObeliskRoute appRoute) where
990 gshowsPrec prec = \case
1014 gshowsPrec prec = \case
@@ -1015,8 +1039,8 @@ someSumEncoder = Encoder $ pure $ EncoderImpl
1016data Void1 :: * -> * where {}
1040data Void1 :: * -> * where {}
1018−instance Universe (Some Void1) where
1042+instance UniverseSome Void1 where
1019− universe = []
1043+ universeSome = []
1021void1Encoder :: (Applicative check, MonadError Text parse) => Encoder check parse (Some Void1) a
1045void1Encoder :: (Applicative check, MonadError Text parse) => Encoder check parse (Some Void1) a
1022void1Encoder = Encoder $ pure $ EncoderImpl
1046void1Encoder = Encoder $ pure $ EncoderImpl
@@ -1028,17 +1052,6 @@ void1Encoder = Encoder $ pure $ EncoderImpl
1028instance GShow Void1 where
1052instance GShow Void1 where
1029 gshowsPrec _ = \case {}
1053 gshowsPrec _ = \case {}
1031−concat <$> mapM deriveRouteComponent
1033− , ''JSaddleWarpRoute
1037−makePrisms ''ObeliskRoute
1038−makePrisms ''FullRoute
1040−deriveGCompare ''Void1
1042-- | Given a backend route and a checked route encoder, render the route (path
1055-- | Given a backend route and a checked route encoder, render the route (path
1043-- and query string). See 'checkEncoder' for how to produce a checked encoder.
1056-- and query string). See 'checkEncoder' for how to produce a checked encoder.
1044renderBackendRoute
1057renderBackendRoute
@@ -1071,11 +1084,11 @@ readShowEncoder :: (MonadError Text parse, Read a, Show a, Applicative check) =>
1071readShowEncoder = singlePathSegmentEncoder . unsafeTshowEncoder
1084readShowEncoder = singlePathSegmentEncoder . unsafeTshowEncoder
1073integralEncoder :: (MonadError Text parse, Applicative check, Integral a) => Encoder check parse a Integer
1086integralEncoder :: (MonadError Text parse, Applicative check, Integral a) => Encoder check parse a Integer
1074−integralEncoder = prismEncoder (Numeric.Lens.integral)
1087+integralEncoder = reviewEncoder Numeric.Lens.integral
1076pathSegmentEncoder :: (MonadError Text parse, Applicative check, Cons as as a a) =>
1089pathSegmentEncoder :: (MonadError Text parse, Applicative check, Cons as as a a) =>
1077 Encoder check parse (a, (as, b)) (as, b)
1090 Encoder check parse (a, (as, b)) (as, b)
1078−pathSegmentEncoder = first (prismEncoder _Cons) . disassociate
1091+pathSegmentEncoder = first (reviewEncoder _Cons) . disassociate
1080newtype Decoder check parse b a = Decoder { toEncoder :: Encoder check parse a b }
1093newtype Decoder check parse b a = Decoder { toEncoder :: Encoder check parse a b }
@@ -1146,7 +1159,7 @@ pathFieldEncoder fieldEncoder = unsafeEncoder $ do
1146 fieldEncoderPure f = toEncoder (DMap.findWithDefault (error "bad") f fieldEncoderPureMap)
1159 fieldEncoderPure f = toEncoder (DMap.findWithDefault (error "bad") f fieldEncoderPureMap)
1147 pure $ EncoderImpl
1160 pure $ EncoderImpl
1148 { _encoderImpl_encode = \(x, rest) -> execWriter $ do
1161 { _encoderImpl_encode = \(x, rest) -> execWriter $ do
1149− _ <- traverseWithField (\f x_i -> tell (pure $ encode (fieldEncoderPure f) x_i) *> pure x_i) x
1162+ _ <- traverseWithField (\f x_i -> tell (pure $ encode (fieldEncoderPure f) x_i) $> x_i) x
1150 tell rest
1163 tell rest
1151 , _encoderImpl_decode = State.runStateT $ tabulateFieldsA $ \f -> State.get >>= \case
1164 , _encoderImpl_decode = State.runStateT $ tabulateFieldsA $ \f -> State.get >>= \case
1152 [] -> throwError $ T.pack "not enough path components"
1165 [] -> throwError $ T.pack "not enough path components"
@@ -1182,3 +1195,34 @@ byteStringsToPageName p q =
1182 in decode pageNameEncoder' (T.unpack (T.decodeUtf8 p), T.unpack (T.decodeUtf8 q))
1195 in decode pageNameEncoder' (T.unpack (T.decodeUtf8 p), T.unpack (T.decodeUtf8 q))
1184--TODO: decodeURIComponent as appropriate
1197--TODO: decodeURIComponent as appropriate
1200+{-# DEPRECATED isoEncoder "Instead of 'isoEncoder f', use 'viewEncoder f'" #-}
1201+-- | Given a valid 'Iso' from lens, construct an 'Encoder'
1202+isoEncoder :: (Applicative check, Applicative parse) => Iso' a b -> Encoder check parse a b
1203+isoEncoder = viewEncoder
1205+{-# DEPRECATED prismEncoder "Instead of 'prismEncoder f', use 'reviewEncoder f'" #-}
1206+-- | An encoder that only works on the items available via the prism. An error will be thrown in the parse monad
1207+-- if the prism doesn't match.
1209+-- Note that a 'Prism' from @a@ to @b@ will produce an 'Encoder' from @b@ to @a@
1210+-- (i.e. 'prismEncoder' is a contravariant functor from the category of prisms to the category of encoders),
1211+-- just like 'review' produces a function @b -> a@. This is because 'Prism's extract values, in a way that might
1212+-- fail, in their forward direction and inject values, in a way that cannot fail, in their reverse direction;
1213+-- whereas 'Encoder's encode, which cannot fail, in their forward direction, and decode, which can fail, in their
1214+-- reverse direction. In short @prismEncoder (f . g) = prismEncoder g . prismEncoder f@.
1215+prismEncoder :: (Applicative check, MonadError Text parse) => Prism' b a -> Encoder check parse a b
1216+prismEncoder = reviewEncoder
1219+concat <$> mapM deriveRouteComponent
1221+ , ''JSaddleWarpRoute
1225+makePrisms ''ObeliskRoute
1226+makePrisms ''FullRoute
1228+deriveGCompare ''Void1