{-# language AllowAmbiguousTypes #-} {-# language ConstraintKinds #-} {-# language DataKinds #-} {-# language DeriveFunctor #-} {-# language FlexibleContexts #-} {-# language GADTs #-} {-# language RankNTypes #-} {-# language ScopedTypeVariables #-} {-# language TypeApplications #-} {-# language TypeOperators #-} module Language.Haskell.To.Elm.DataShape where import Generics.SOP type DataShape a = [(String, ConstructorShape a)] data ConstructorShape a = ConstructorShape [a] | RecordConstructorShape [(String, a)] deriving Functor nullary :: ConstructorShape a -> Bool nullary (ConstructorShape []) = True nullary _ = False data Dict constraint where Dict :: constraint => Dict constraint newtype ConstraintFun constraint a = ConstraintFun (forall t. Dict (constraint t) -> a) dataShape :: forall typ constraint a . (All2 constraint (Code typ), HasDatatypeInfo typ) => ConstraintFun constraint a -> DataShape a dataShape f = constructorShapes @(Code typ) @constraint f $ constructorInfo $ datatypeInfo $ Proxy @typ constructorShapes :: forall constrs constraint a . All2 constraint constrs => ConstraintFun constraint a -> NP ConstructorInfo constrs -> [(String, ConstructorShape a)] constructorShapes f infos = case infos of Nil -> [] info :* infos' -> constructorShape f info : constructorShapes f infos' constructorShape :: forall constr constraint a . All constraint constr => ConstraintFun constraint a -> ConstructorInfo constr -> (String, ConstructorShape a) constructorShape f info = case info of Constructor cname -> (cname, ConstructorShape $ constructorFieldShape f $ shape @_ @constr) Infix {} -> error "Infix constructors are not supported" Record cname fs -> (cname, RecordConstructorShape $ recordFieldShape f fs) constructorFieldShape :: All constraint fields => ConstraintFun constraint a -> Shape fields -> [a] constructorFieldShape f shape_ = case shape_ of ShapeNil -> [] s@(ShapeCons _) -> go f s where go :: forall field fields constraint a . (constraint field, All constraint fields) => ConstraintFun constraint a -> Shape (field ': fields) -> [a] go f'@(ConstraintFun fun) (ShapeCons s') = fun @field Dict : constructorFieldShape f' s' recordFieldShape :: forall fields constraint a . All constraint fields => ConstraintFun constraint a -> NP FieldInfo fields -> [(String, a)] recordFieldShape f infos = case infos of Nil -> [] info :* infos' -> go f info : recordFieldShape f infos' where go :: forall field . constraint field => ConstraintFun constraint a -> FieldInfo field -> (String, a) go (ConstraintFun fun) (FieldInfo fname) = (fname, fun @field Dict)