{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TypeOperators #-}
module Zinza.Generic (
Zinza (..),
GFieldNames, stripFieldPrefix,
GZinzaType, genericToType, genericToTypeSFP,
GZinzaValue, genericToValue, genericToValueSFP,
GZinzaFrom, genericFromValue, genericFromValueSFP,
) where
import Data.Char (isLower, toLower)
import Data.Kind (Type)
import Data.List (stripPrefix)
import Data.Proxy (Proxy (..))
import Data.Semigroup (Semigroup (..))
import GHC.Generics
import qualified Data.Map.Strict as M
import Zinza.Class
import Zinza.Errors
import Zinza.Pos
import Zinza.Type
import Zinza.Value
import Zinza.Var (Var)
stripFieldPrefix
:: forall a. (Generic a, GFieldNames (Rep a))
=> Proxy a
-> String -> String
stripFieldPrefix _ = case fieldNames (Proxy :: Proxy (Rep a)) of
[] -> id
(y:ys) -> \fn -> case stripPrefix pfx fn of
Just (x:xs) -> toLower x : xs
_ -> fn
where
(pfx, _) = span isLower $ getCommonPrefix $ foldl (\cp z -> cp <> CP z) (CP y) ys
class GFieldNames (f :: Type -> Type) where
fieldNames :: Proxy f -> [String]
instance (i ~ D, GFieldNamesSum f) => GFieldNames (M1 i c f) where
fieldNames _ = fieldNamesSum (Proxy :: Proxy f)
class GFieldNamesSum (f :: Type -> Type) where
fieldNamesSum :: Proxy f -> [String]
instance (i ~ C, GFieldNamesProd f) => GFieldNamesSum (M1 i c f ) where
fieldNamesSum _ = fieldNamesProd (Proxy :: Proxy f)
class GFieldNamesProd (f :: Type -> Type) where
fieldNamesProd :: Proxy f -> [String]
instance (GFieldNamesProd f, GFieldNamesProd g) => GFieldNamesProd (f :*: g) where
fieldNamesProd _ = fieldNamesProd (Proxy :: Proxy f) ++ fieldNamesProd (Proxy :: Proxy g)
instance (i ~ S, Selector c) => GFieldNamesProd (M1 i c f) where
fieldNamesProd _ = [selName (undefined :: M1 i c f ())]
newtype CommonPrefix = CP { getCommonPrefix :: String }
instance Data.Semigroup.Semigroup CommonPrefix where
CP a <> CP b = CP (commonPrefix a b)
commonPrefix :: Eq a => [a] -> [a] -> [a]
commonPrefix xs@[] _ = xs
commonPrefix _ ys@[] = ys
commonPrefix (x:xs) (y:ys)
| x == y = x : commonPrefix xs ys
| otherwise = []
genericToType
:: forall a. (Generic a, GZinzaType (Rep a))
=> (String -> String)
-> Proxy a -> Ty
genericToType namer _ = TyRecord $ M.fromList
[ (namer fn, (fn, ty))
| (fn, ty) <- gtoType (Proxy :: Proxy (Rep a))
]
genericToTypeSFP
:: forall a. (Generic a, GZinzaType (Rep a), GFieldNames (Rep a))
=> Proxy a -> Ty
genericToTypeSFP p = genericToType (stripFieldPrefix p) p
class GZinzaType (f :: Type -> Type) where
gtoType :: Proxy f -> [(String, Ty)]
instance (i ~ D, GZinzaTypeSum f) => GZinzaType (M1 i c f) where
gtoType _ = gtoTypeSum (Proxy :: Proxy f)
class GZinzaTypeSum (f :: Type -> Type) where
gtoTypeSum :: Proxy f -> [(String, Ty)]
instance (i ~ C, GZinzaTypeProd f) => GZinzaTypeSum (M1 i c f ) where
gtoTypeSum _ = gtoTypeProd (Proxy :: Proxy f)
class GZinzaTypeProd (f :: Type -> Type) where
gtoTypeProd :: Proxy f -> [(String, Ty)]
instance (GZinzaTypeProd f, GZinzaTypeProd g) => GZinzaTypeProd (f :*: g) where
gtoTypeProd _ = gtoTypeProd (Proxy :: Proxy f) ++ gtoTypeProd (Proxy :: Proxy g)
instance (i ~ S, Selector c, GZinzaTypeLeaf f) => GZinzaTypeProd (M1 i c f) where
gtoTypeProd _ = [(selName (undefined :: M1 i c f ()), gtoTypeLeaf (Proxy :: Proxy f))]
class GZinzaTypeLeaf (f :: Type -> Type) where
gtoTypeLeaf :: Proxy f -> Ty
instance (i ~ R, Zinza a) => GZinzaTypeLeaf (K1 i a) where
gtoTypeLeaf _ = toType (Proxy :: Proxy a)
genericToValue
:: forall a. (Generic a, GZinzaValue (Rep a))
=> (String -> String)
-> a -> Value
genericToValue namer x = VRecord $ M.fromList
[ (namer fn, e)
| (fn, e) <- gtoValue (from x)
]
genericToValueSFP
:: forall a. (Generic a, GZinzaValue (Rep a), GFieldNames (Rep a))
=> a -> Value
genericToValueSFP = genericToValue (stripFieldPrefix (Proxy :: Proxy a))
class GZinzaValue (f :: Type -> Type) where
gtoValue :: f () -> [(Var, Value)]
instance (i ~ D, GZinzaValueSum f) => GZinzaValue (M1 i c f) where
gtoValue = gtoValueSum . unM1
class GZinzaValueSum (f :: Type -> Type) where
gtoValueSum :: f () -> [(Var, Value)]
instance (i ~ C, GZinzaValueProd f) => GZinzaValueSum (M1 i c f) where
gtoValueSum = gtoValueProd . unM1
class GZinzaValueProd (f :: Type -> Type) where
gtoValueProd :: f () -> [(Var, Value)]
instance (GZinzaValueProd f, GZinzaValueProd g) => GZinzaValueProd (f :*: g) where
gtoValueProd (f :*: g) = gtoValueProd f ++ gtoValueProd g
instance (i ~ S, Selector c, GZinzaValueLeaf f) => GZinzaValueProd (M1 i c f) where
gtoValueProd (M1 x) = [(selName (undefined :: M1 i c f ()), gtoValueLeaf x)]
class GZinzaValueLeaf f where
gtoValueLeaf :: f a -> Value
instance (i ~ R, Zinza a) => GZinzaValueLeaf (K1 i a) where
gtoValueLeaf (K1 a) = toValue a
genericFromValue
:: forall a. (Generic a, GZinzaFrom (Rep a))
=> (String -> String)
-> Loc -> Value -> Either RuntimeError a
genericFromValue namer l v@(VRecord m) = do
g <- gfromValue l (valueType v) $ \n -> M.lookup (namer n) m
return (to g)
genericFromValue _ l v = throwRuntime $ NotRecord l (valueType v)
genericFromValueSFP
:: forall a. (Generic a, GZinzaFrom (Rep a), GFieldNames (Rep a))
=> Loc -> Value -> Either RuntimeError a
genericFromValueSFP = genericFromValue (stripFieldPrefix (Proxy :: Proxy a))
class GZinzaFrom (f :: Type -> Type) where
gfromValue :: Loc -> Ty -> (Var -> Maybe Value) -> Either RuntimeError (f ())
instance (i ~ D, GZinzaFromSum f) => GZinzaFrom (M1 i c f) where
gfromValue l ty = fmap M1 . gfromValueSum l ty
class GZinzaFromSum (f :: Type -> Type) where
gfromValueSum :: Loc -> Ty -> (Var -> Maybe Value) -> Either RuntimeError (f ())
instance (i ~ C, GZinzaFromProd f) => GZinzaFromSum (M1 i c f) where
gfromValueSum l ty = fmap M1 . gfromValueProd l ty
class GZinzaFromProd (f :: Type -> Type) where
gfromValueProd :: Loc -> Ty -> (Var -> Maybe Value) -> Either RuntimeError (f ())
instance (GZinzaFromProd f, GZinzaFromProd g) => GZinzaFromProd (f :*: g) where
gfromValueProd l ty v = (:*:)
<$> gfromValueProd l ty v
<*> gfromValueProd l ty v
instance (i ~ S, Selector c, GZinzaFromLeaf f) => GZinzaFromProd (M1 i c f) where
gfromValueProd l ty f = case f n of
Nothing -> throwRuntime $ FieldNotInRecord l n ty
Just v -> M1 <$> gfromValueLeaf l v
where
n = selName (undefined :: M1 i c f ())
class GZinzaFromLeaf f where
gfromValueLeaf :: Loc -> Value -> Either RuntimeError (f ())
instance (i ~ R, Zinza a) => GZinzaFromLeaf (K1 i a) where
gfromValueLeaf l = fmap K1 . fromValue l