{-# LANGUAGE CPP #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE PolyKinds #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE Safe #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE UndecidableInstances #-}
module Data.InvertibleGrammar.Generic
( with
, match
, Coproduct (..)
) where
import Prelude hiding ((.), id)
import Control.Applicative
import Control.Category ((.))
import Data.Functor.Identity
import Data.InvertibleGrammar.Base
import Data.Monoid (First(..))
import Data.Profunctor (Choice(..))
import Data.Profunctor.Unsafe
import Data.Tagged
import Data.Text (pack)
#if !MIN_VERSION_base(4,11,0)
import Data.Semigroup ((<>))
#endif
import GHC.Generics
with
:: forall a b s t c d f p.
( Generic a
, MkPrismList (Rep a)
, MkStackPrism f
, Rep a ~ M1 D d (M1 C c f)
, StackPrismLhs f t ~ b
, Constructor c
) =>
(Grammar p b (a :- t) -> Grammar p s (a :- t))
-> Grammar p s (a :- t)
with g =
let PrismList (P prism) = mkRevPrismList
name = conName (undefined :: m c f e)
in g (PartialIso
(fwd prism)
(maybe (Left $ expected ("constructor " <> pack name)) Right . bkwd prism))
match
:: ( Generic a
, MkPrismList (Rep a)
, Match (Rep a) bs t
, bs ~ Coll (Rep a) t
) =>
Coproduct p s bs a t
-> Grammar p s (a :- t)
match = fst . match' mkRevPrismList
data Coproduct p s bs a t where
With
:: (Grammar p b (a :- t) -> Grammar p s (a :- t))
-> Coproduct p s bs a t
-> Coproduct p s (b ': bs) a t
End :: Coproduct p s '[] a t
type family (:++) (as :: [k]) (bs :: [k]) :: [k] where
(:++) (a ': as) bs = a ': (as :++ bs)
(:++) '[] bs = bs
type family Coll (f :: * -> *) (t :: *) :: [*] where
Coll (f :+: g) t = Coll f t :++ Coll g t
Coll (M1 D c f) t = Coll f t
Coll (M1 C c f) t = '[StackPrismLhs f t]
type family Trav (t :: * -> *) (l :: [*]) :: [*] where
Trav (f :+: g) lst = Trav g (Trav f lst)
Trav (M1 D c f) lst = Trav f lst
Trav (M1 C c f) (l ': ls) = ls
class Match (f :: * -> *) bs t where
match' :: PrismList f a
-> Coproduct p s bs a t
-> ( Grammar p s (a :- t)
, Coproduct p s (Trav f bs) a t
)
instance (Match f bs t, Trav f bs ~ '[]) => Match (M1 D c f) bs t where
match' (PrismList p) = match' p
instance
( Match f bs t
, Match g (Trav f bs) t
) => Match (f :+: g) bs t where
match' (p :& q) lst =
let (gp, rest) = match' p lst
(qp, rest') = match' q rest
in (gp <> qp, rest')
instance (StackPrismLhs f t ~ b, Constructor c) => Match (M1 C c f) (b ': bs) t where
match' (P prism) (With g rest) =
let name = conName (undefined :: m c f e)
p = fwd prism
q = maybe (Left $ expected ("constructor " <> pack name)) Right . bkwd prism
in (g $ PartialIso p q, rest)
mkRevPrismList :: (Generic a, MkPrismList (Rep a)) => StackPrisms a
mkRevPrismList = mkPrismList' to (Just . from)
type StackPrism a b = forall p f. (Choice p, Applicative f) => p a (f a) -> p b (f b)
stackPrism :: (a -> b) -> (b -> Maybe a) -> StackPrism a b
stackPrism f g = dimap (\b -> maybe (Left b) Right (g b)) (either pure (fmap f)) . right'
fwd :: StackPrism a b -> a -> b
fwd l = runIdentity #. unTagged #. l .# Tagged .# Identity
bkwd :: StackPrism a b -> b -> Maybe a
bkwd l = getFirst #. getConst #. l (Const #. First #. Just)
type StackPrisms a = PrismList (Rep a) a
data family PrismList (f :: * -> *) (a :: *)
class MkPrismList (f :: * -> *) where
mkPrismList' :: (f p -> a) -> (a -> Maybe (f q)) -> PrismList f a
data instance PrismList (M1 D c f) a = PrismList (PrismList f a)
instance MkPrismList f => MkPrismList (M1 D c f) where
mkPrismList' f' g' = PrismList (mkPrismList' (f' . M1) (fmap unM1 . g'))
infixr :&
data instance PrismList (f :+: g) a = PrismList f a :& PrismList g a
instance (MkPrismList f, MkPrismList g) => MkPrismList (f :+: g) where
mkPrismList' f' g' = f f' g' :& g f' g'
where
f :: forall a p q. ((f :+: g) p -> a) -> (a -> Maybe ((f :+: g) q)) -> PrismList f a
f _f' _g' = mkPrismList' (\fp -> _f' (L1 fp)) (matchL _g')
g :: forall a p q. ((f :+: g) p -> a) -> (a -> Maybe ((f :+: g) q)) -> PrismList g a
g _f' _g' = mkPrismList' (\gp -> _f' (R1 gp)) (matchR _g')
matchL :: (a -> Maybe ((f :+: g) q)) -> a -> Maybe (f q)
matchL _g' a = case _g' a of
Just (L1 f'') -> Just f''
_ -> Nothing
matchR :: (a -> Maybe ((f :+: g) q)) -> a -> Maybe (g q)
matchR _g' a = case _g' a of
Just (R1 g'') -> Just g''
_ -> Nothing
data instance PrismList (M1 C c f) a = P (forall t. StackPrism (StackPrismLhs f t) (a :- t))
instance MkStackPrism f => MkPrismList (M1 C c f) where
mkPrismList' f' g' = P (stackPrism (f f') (g g'))
where
f :: forall a p t. (M1 C c f p -> a) -> StackPrismLhs f t -> a :- t
f _f' lhs = mapHead (_f' . M1) (mkR lhs)
g :: forall a p t. (a -> Maybe (M1 C c f p)) -> (a :- t) -> Maybe (StackPrismLhs f t)
g _g' (a :- t) = fmap (mkL . (:- t) . unM1) (_g' a)
type family StackPrismLhs (f :: * -> *) (t :: *) :: *
class MkStackPrism (f :: * -> *) where
mkR :: forall p t. StackPrismLhs f t -> (f p :- t)
mkL :: forall p t. (f p :- t) -> StackPrismLhs f t
type instance StackPrismLhs U1 t = t
instance MkStackPrism U1 where
mkR t = U1 :- t
mkL (U1 :- t) = t
type instance StackPrismLhs (K1 i a) t = a :- t
instance MkStackPrism (K1 i a) where
mkR (h :- t) = K1 h :- t
mkL (K1 h :- t) = h :- t
type instance StackPrismLhs (M1 i c f) t = StackPrismLhs f t
instance MkStackPrism f => MkStackPrism (M1 i c f) where
mkR = mapHead M1 . mkR
mkL = mkL . mapHead unM1
type instance StackPrismLhs (f :*: g) t = StackPrismLhs g (StackPrismLhs f t)
instance (MkStackPrism f, MkStackPrism g) => MkStackPrism (f :*: g) where
mkR t = (hg :*: hf) :- tg
where
hf :- tf = mkR t
hg :- tg = mkR tf
mkL ((hf :*: hg) :- t) = mkL (hg :- mkL (hf :- t))
mapHead :: (a -> b) -> (a :- t) -> (b :- t)
mapHead f (h :- t) = f h :- t