A type Prism' s a = Prism s s a a
(hackage) can be thought of as a relation between some structure s
and its member a
, such that you can always produce the structure from the member (a -> s
), but can only optionally retrieve the member from the structure (s -> Maybe a
).
This model is helpful in relating a sum type to one of its constructors ... as well as (more relevant here) in route encoding and decoding. If s
is the encoded route URL, and a
is the route type, then we have a -> s
representing the encoding function (always succeeds), and s -> Maybe a
representing the decoding function (can fail).
Now, on to these pairs of functions, I want to add a "context" argument that is to be used in the encoding and decoding process (imagine that the decoding process needs to "look up" some database before successfully producing a relevant route). Basically:
encode :: ctx -> a -> s
decode :: ctx -> s -> Maybe a
Is there a type that models these conversions? It looks very much like a Prism'
but with an extra ctx
argument.
As a next step, I'd like to define a functor for this prism such that it can transform all three types: ctx
, s
, a
. I currently have a class like this, but it appears I might be missing an existing library that I could use to simplify all of this:
class PartialIsoFunctor (f :: Type -> Type -> Type -> Type) where
-- x, y are the context
-- a, b are the structure `s`
-- c, d are the route types `a`
pimap ::
Prism' b a -> -- Note: contravariant prism
Prism' c d ->
(y -> x) -> -- Note: this is contravariant
f x a c ->
f y b d
The idea here is that there is a RouteEncoder ctx r
type (see also) representing a value that knows how to encode/decode routes. And I want to be able to transform these route encoders on the r
, ctx
, and the URL string (internally a FilePath
, actually) it encodes to/ decodes from.
Notes:
- I use
optics-core
rather thanlens
.
EDIT: Here's my current approach:
type RouteEncoder ctx s route = Prism' (ctx, s) (ctx, route)
And the functor that transforms it:
mapRouteEncoder ::
Prism' b a ->
Prism' c d ->
(y -> x) ->
RouteEncoder x a c ->
RouteEncoder y b d
mapRouteEncoder = undefined
The encoding/decoding functions:
-- The use of `snd` here suggests that the use of tuples in
-- RouteEncoder is a bit of a hack
encodeRoute :: RouteEncoder ctx r -> ctx -> r -> FilePath
encodeRoute enc ctx r = snd $ review enc (ctx, r)
decodeRoute :: RouteEncoder ctx r -> ctx -> FilePath -> Maybe r
decodeRoute enc m s = snd <$> preview enc (ctx, s)
How can this be simplified? Note that RouteEncoder
's are created ahead, and composed. But the actual encoding/decoding happens later, passing the 'ctx' that varies over time.