{-# LANGUAGE UndecidableSuperClasses #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE AllowAmbiguousTypes #-}
{-# LANGUAGE InstanceSigs #-}
module Data.Schematic.Lens
( FIndex
, FElem(..)
, FImage
, FSubset(..)
, obj
, arr
, uni
, txt
, num
, opt
, bln
, textRepr
, numberRepr
, boolRepr
, arrayRepr
, objectRepr
, optionalRepr
) where
import Data.Profunctor
import Data.Schematic.Schema
import Data.Scientific
import Data.Singletons
import Data.Text
import Data.Union
import Data.Vector as V
import Data.Vinyl
import Data.Vinyl.Functor
import Data.Vinyl.TypeLevel (Nat(..))
import GHC.TypeLits (Symbol, KnownSymbol)
type family FIndex (r :: Symbol) (rs :: [(Symbol, Schema)]) :: Nat where
FIndex r ( '(r, s) ': rs) = 'Z
FIndex r ( s ': rs) = 'S (FIndex r rs)
type Flens fn f g rs i
= Functor g
=> (f '(fn, (ByField fn rs i)) -> g (f '(fn, (ByField fn rs i))))
-> Rec f rs
-> g (Rec f rs)
type FGetter fn f rs i = Rec f rs -> f '(fn, (ByField fn rs i))
class i ~ FIndex fn rs => FElem (fn :: Symbol) (rs :: [(Symbol, Schema)]) (i :: Nat) where
type ByField fn rs i :: Schema
flens :: Flens fn f g rs i
fget :: FGetter fn f rs i
fput
:: f '(fn, ByField fn rs i)
-> Rec f rs
-> Rec f rs
instance FElem fn ('(fn, r) ': rs) 'Z where
type ByField fn ('(fn, r) ': rs) 'Z = r
flens f (x :& xs) = fmap (:& xs) (f x)
{-# INLINE flens #-}
fget = getConst . flens Const
{-# INLINE fget #-}
fput y = getIdentity . flens (\_ -> Identity y)
{-# INLINE fput #-}
instance (FIndex r (s ': rs) ~ 'S i, FElem r rs i) => FElem r (s ': rs) ('S i) where
type ByField r (s ': rs) ('S i) = ByField r rs i
flens f (x :& xs) = fmap (x :&) (flens f xs)
{-# INLINE flens #-}
fget = getConst . flens Const
{-# INLINE fget #-}
fput y = getIdentity . flens (\_ -> Identity y)
{-# INLINE fput #-}
lens
:: Functor f
=> (t -> s)
-> (t -> a -> b)
-> (s -> f a)
-> t
-> f b
lens sa sbt afb s = fmap (sbt s) $ afb (sa s)
{-# INLINE lens #-}
type Iso s t a b = forall p f. (Profunctor p, Functor f) => p a (f b) -> p s (f t)
type Iso' s a = Iso s s a a
iso
:: (s -> a) -> (b -> t)
-> Iso s t a b
iso sa bt = dimap sa (fmap bt)
{-# INLINE iso #-}
type family FImage (rs :: [(Symbol, Schema)]) (ss :: [(Symbol, Schema)]) :: [Nat] where
FImage '[] ss = '[]
FImage ('(fn,s) ': rs) ss = FIndex fn ss ': FImage rs ss
class is ~ FImage rs ss
=> FSubset (rs :: [(Symbol, Schema)]) (ss :: [(Symbol, Schema)]) is where
fsubset
:: Functor g
=> (Rec f rs -> g (Rec f rs))
-> Rec f ss
-> g (Rec f ss)
fcast
:: Rec f ss
-> Rec f rs
fcast = getConst . fsubset Const
{-# INLINE fcast #-}
freplace
:: Rec f rs
-> Rec f ss
-> Rec f ss
freplace rs = getIdentity . fsubset (\_ -> Identity rs)
{-# INLINE freplace #-}
instance FSubset '[] ss '[] where
fsubset = lens (const RNil) const
instance
( ByField fn ss i ~ s
, FElem fn ss i
, FSubset rs ss is) => FSubset ( '(fn,s) ': rs) ss (i ': is) where
fsubset = lens (\ss -> fget @fn ss :& fcast ss) set
where
set :: Rec f ss -> Rec f ( '(fn,s) ': rs) -> Rec f ss
set ss (r :& rs) = fput r $ freplace rs ss
textRepr
:: (KnownSymbol fn, SingI cs)
=> Iso' (FieldRepr '(fn, ('SchemaText cs))) Text
textRepr = iso (\(FieldRepr (ReprText t)) -> t) (FieldRepr . ReprText)
numberRepr
:: (KnownSymbol fn, SingI cs)
=> Iso' (FieldRepr '(fn, ('SchemaNumber cs))) Scientific
numberRepr = iso (\(FieldRepr (ReprNumber n)) -> n) (FieldRepr . ReprNumber)
boolRepr
:: (KnownSymbol fn)
=> Iso' (FieldRepr '(fn, 'SchemaBoolean)) Bool
boolRepr = iso (\(FieldRepr (ReprBoolean b)) -> b) (FieldRepr . ReprBoolean)
arrayRepr
:: (KnownSymbol fn, SingI cs, SingI schema)
=> Iso' (FieldRepr '(fn, ('SchemaArray cs schema))) (V.Vector (JsonRepr schema))
arrayRepr = iso (\(FieldRepr (ReprArray a)) -> a) (FieldRepr . ReprArray)
objectRepr
:: (KnownSymbol fn, SingI fields)
=> Iso' (FieldRepr '(fn, ('SchemaObject fields))) (Rec FieldRepr fields)
objectRepr = iso (\(FieldRepr (ReprObject o)) -> o) (FieldRepr . ReprObject)
optionalRepr
:: (KnownSymbol fn, SingI schema)
=> Iso' (FieldRepr '(fn, ('SchemaOptional schema))) (Maybe (JsonRepr schema))
optionalRepr = iso (\(FieldRepr (ReprOptional r)) -> r) (FieldRepr . ReprOptional)
obj :: Iso' (JsonRepr ('SchemaObject fields)) (Rec FieldRepr fields)
obj = iso (\(ReprObject r) -> r) ReprObject
arr :: Iso' (JsonRepr ('SchemaArray cs schema)) (V.Vector (JsonRepr schema))
arr = iso (\(ReprArray r) -> r) ReprArray
uni :: Iso' (JsonRepr ('SchemaUnion (h ': tl))) (Union JsonRepr (h ': tl))
uni = iso (\(ReprUnion u) -> u) ReprUnion
txt :: Iso' (JsonRepr ('SchemaText cs)) Text
txt = iso (\(ReprText t) -> t) ReprText
num :: Iso' (JsonRepr ('SchemaNumber cs)) Scientific
num = iso (\(ReprNumber t) -> t) ReprNumber
bln :: Iso' (JsonRepr 'SchemaBoolean) Bool
bln = iso (\(ReprBoolean b) -> b) ReprBoolean
opt :: Iso' (JsonRepr ('SchemaOptional schema)) (Maybe (JsonRepr schema))
opt = iso (\(ReprOptional o) -> o) ReprOptional