{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE AllowAmbiguousTypes #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE KindSignatures #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE DeriveFunctor #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE DeriveFoldable #-}
{-# LANGUAGE DeriveTraversable #-}
module Data.Dynamic.Resolve where
import Data.Dynamic
import Type.Reflection
import GHC.Base (Type, join, Alternative(..))
import Control.Monad.Fail (MonadFail)
import Data.Foldable (foldrM, foldl')
import Data.List.NonEmpty (NonEmpty(..))
import qualified Data.List.NonEmpty as NE
import Data.String (IsString(..))
import Data.Functor.Identity
import Data.Maybe (catMaybes, fromMaybe)
import Data.Foldable (asum)
import GHC.Generics (Generic)
import Data.Dynamic.Resolve.Util
class Parameter a where
values :: a -> NonEmpty Dynamic
instance Parameter (NonEmpty Dynamic) where
values = id
instance Parameter Dynamic where
values d = d:|[]
data Tree a = Leaf a
| (Tree a) :*: (Tree a)
deriving (Show, Read, Eq, Functor, Traversable, Foldable, Generic, Typeable)
instance Semigroup (Tree a) where
l <> r = l :*: r
instance Applicative Tree where
pure = Leaf
(Leaf f) <*> rh = f <$> rh
(l :*: r) <*> rh = (l <*> rh) :*: (r <*> rh)
instance Monad Tree where
(Leaf a) >>= f = f a
(l :*: r) >>= f = (l >>= f) :*: (r >>= f)
drawTree :: (Show a) => Tree a -> String
drawTree = drawTree' drawIndentr 0
where
drawIndentr n
| n <= 0 = ""
| otherwise = "| " ++ drawIndentr (n - 1)
drawIndentl n
| n <= 0 = ""
| otherwise = " " ++ drawIndentl (n - 1)
drawTree' _ _ (Leaf a) = " " ++ show a ++ "\n"
drawTree' indentF indentN (l :*: r)
= "+--"
++ drawTree' drawIndentr (indentN + 1) r
++ indentF indentN
++ "|\n"
++ indentF indentN
++ "+--"
++ drawTree' drawIndentl (indentN + 1) l
foldi :: (a -> b -> b)
-> (b -> b -> b)
-> b
-> Tree a
-> b
foldi f bf s (Leaf a) = f a s
foldi f bf s (l :*: r) = bf (foldi f bf s l) (foldi f bf s r)
foldi1 :: (a -> a -> a) -> Tree a -> a
foldi1 f (Leaf a) = a
foldi1 f (l :*: r) = f (foldi1 f l) (foldi1 f r)
fromListL :: NonEmpty a -> Tree a
fromListL (a:|as) = foldl' (\acc x -> acc :*: (Leaf x)) (Leaf a) as
data Ap a = Ap { result :: NonEmpty Dynamic
, applicationTree :: Tree a
} deriving (Show, Generic, Typeable)
data ApResult (env :: * -> *) a = Success (Ap a)
| Failure (Ap a, Ap a)
deriving (Show, Generic, Typeable)
type ApResultPure a = ApResult Identity a
papply :: forall env a. (Parameter a, Monad env, Typeable env)
=> ApResult env a
-> ApResult env a
-> ApResult env a
papply f@(Failure _) _ = f
papply _ f@(Failure _) = f
papply (Success la@(Ap l lt)) (Success ra@(Ap r rt)) =
case applicationResult of
[] -> Failure (la, ra)
vs -> Success $ Ap (NE.fromList vs) (lt :*: rt)
where
catMaybes' = catMaybes . NE.toList
applicationResult = catMaybes' $ dynApplyFmapAp @env <$> (values l) <*> (values r)
applyTree :: forall env a. (Parameter a, Monad env, Typeable env)
=> Tree a
-> ApResult env a
applyTree as = foldi1 papply (marshal <$> as)
where
marshal a = Success $ Ap (values a) (Leaf a)
pureApplyTree :: (Parameter a) => Tree a -> ApResultPure a
pureApplyTree = applyTree
applyList :: forall env a. (Parameter a, Monad env, Typeable env)
=> a
-> [a]
-> ApResult env a
applyList f params = applyTree $ fromListL (f:|params)
pureApplyList :: (Parameter a)
=> a
-> [a]
-> ApResultPure a
pureApplyList = applyList
reifyTree :: forall env result a. (Parameter a, Monad env, Typeable env, Typeable result)
=> Tree a
-> Either (ApResult env a) (NonEmpty (env result))
reifyTree t = case apResult of
f@(Failure _) -> Left f
s@(Success (Ap res _)) -> case (catMaybes . NE.toList) (reify res) of
[] -> Left s
(r:rs) -> Right (r:|rs)
where
apResult = applyTree t
reify r = fromDynamic <$> dynPureJoinId @env <$> r
pureReifyTree :: forall result a. (Parameter a, Typeable result)
=> Tree a
-> Either (ApResultPure a) (NonEmpty result)
pureReifyTree t = case reifyTree t of
Left err -> Left err
Right vs -> Right $ runIdentity <$> vs
reifyList :: forall env result a. (Parameter a, Monad env, Typeable env, Typeable result)
=> a
-> [a]
-> Either (ApResult env a) (NonEmpty (env result))
reifyList f params = reifyTree $ fromListL (f:|params)
pureReifyList :: forall result a. (Parameter a, Typeable result)
=> a
-> [a]
-> Either (ApResultPure a) (NonEmpty result)
pureReifyList f params = pureReifyTree $ fromListL (f:|params)