{-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TypeApplications #-} {-# LANGUAGE AllowAmbiguousTypes #-} {-# LANGUAGE GADTs #-} module Data.Dynamic.Resolve.Util where import Data.Dynamic import Type.Reflection import GHC.Base (Type) import Control.Monad (join) import Data.Foldable (foldrM) -- * Utilities for working with Dynamics -- | Cons a 'Dynamic' value to a 'Dynamic' list of values of the same type dynCons :: Dynamic -- ^ value to cons -> Dynamic -- ^ list to cons to -> Maybe Dynamic dynCons (Dynamic ta v) (Dynamic (App tl ta') vs) | Just HRefl <- typeRep @[] `eqTypeRep` tl , Just HRefl <- ta `eqTypeRep` ta' = pure $ Dynamic (App tl ta) $ (v:vs) dynCons (Dynamic tl _) (Dynamic tr _) = Nothing -- | Apply 'pure' to a value inside a 'Dynamic' -- Note that the type of 'Applicative' you want to -- return must be manually specified with visible type application dynPure :: forall env. (Applicative env, Typeable env) => Dynamic -> Dynamic dynPure (Dynamic ta v) = (Dynamic (App (typeRep @env) ta) (pure v)) -- | Apply 'join' to a value inside a 'Dynamic' -- Note that the type of 'Monad' you want to -- return must be manually specified with visible type application dynJoin :: forall env. (Monad env, Typeable env) => Dynamic -> Maybe Dynamic dynJoin (Dynamic (App tf (App tf' ta)) v) | Just HRefl <- typeRep @env `eqTypeRep` tf , Just HRefl <- tf `eqTypeRep` tf' = pure $ Dynamic (App tf ta) (join v) dynJoin (Dynamic ta v) = Nothing -- | Normalizes a 'Dynamic' value to an unnested monadic -- value in the env monad via applications of 'join', 'pure', -- or simply 'id' as needed. -- Note that the type of 'Monad' you want to -- return must be manually specified with visible type application dynPureJoinId :: forall env. (Monad env, Typeable env) => Dynamic -> Dynamic dynPureJoinId (Dynamic (App tf (App tf' ta)) v) | Just HRefl <- typeRep @env `eqTypeRep` tf , Just HRefl <- tf `eqTypeRep` tf' = dynPureJoinId @env $ Dynamic (App tf ta) (join v) dynPureJoinId d@(Dynamic (App tf ta) v) = d dynPureJoinId d = dynPure @env d -- | Returns an empty list (wrapped in 'Dynamic') of the same type as the -- value inside the given 'Dynamic' dynEmptyList :: Dynamic -> Dynamic dynEmptyList (Dynamic ta v) = Dynamic (App (typeRep @[]) ta) (tail [v]) -- | Turns a list of 'Dynamic' values into a 'Dynamic' list of values; -- that is to say it embeds the list of items inside a single 'Dynamic'. -- Fails if all values are not of the same type or an empty list is -- provided. dynMerge :: [Dynamic] -> Maybe Dynamic dynMerge [] = Nothing dynMerge (d:ds) = foldrM (dynCons) (dynEmptyList d) (d:ds) -- | Turns a list of 'Dynamic' values into a 'Dynamic' list of values; -- that is to say it embeds the list of items inside a single 'Dynamic'. -- Fails if all values are not of the same type or an empty list is -- provided. Takes a 'Monad' type -- variable via visible type application to allow mixing wrapped and -- unwrapped values—use 'dynMerge' if this is undesirable dynMergeM :: forall f. (Monad f, Typeable f) => [Dynamic] -> Maybe Dynamic dynMergeM [] = Nothing dynMergeM ds = foldrM (dynCons) first normalized >>= dynSequence where tl = typeRep @[] tf = typeRep @f normalized@(n:_) = dynPureJoinId @f <$> ds first = dynEmptyList n dynSequence (Dynamic (App tl' (App tf' ta)) v) | Just HRefl <- typeRep @Type `eqTypeRep` typeRepKind ta , Just HRefl <- tl `eqTypeRep` tl' , Just HRefl <- tf `eqTypeRep` tf' = pure $ Dynamic (App tf' (App tl' ta)) $ sequence v dynSequence _ = Nothing -- | 'fmap' lifted to work with a 'Dynamic' function and value. Fails if -- 'fmap' would fail with the actual types of the function and value. -- Requires the desired 'Functor' to be specified with visible type -- application dynFmap :: forall f. (Functor f, Typeable f) => Dynamic -- ^ function -> Dynamic -- ^ value -> Maybe Dynamic dynFmap (Dynamic (Fun ta tr) f) (Dynamic (App tf ta') x) | Just HRefl <- typeRep @Type `eqTypeRep` typeRepKind ta , Just HRefl <- typeRep @Type `eqTypeRep` typeRepKind tr , Just HRefl <- ta `eqTypeRep` ta' , Just HRefl <- typeRep @f `eqTypeRep` tf = Just $ Dynamic (App tf tr) (f <$> x) dynFmap _ _ = Nothing -- | '<*>' lifted to work with a 'Dynamic' function and value. Fails if -- '<*>' would fail with the actual types of the function and value. -- Requires the desired 'Applicative' to be specified with visible type -- application dynAp :: forall f. (Applicative f, Typeable f) => Dynamic -- ^ function -> Dynamic -- ^ value -> Maybe Dynamic dynAp (Dynamic (App tf (Fun ta tr)) f) (Dynamic (App tf' ta') x) | Just HRefl <- typeRep @Type `eqTypeRep` typeRepKind ta , Just HRefl <- typeRep @Type `eqTypeRep` typeRepKind tr , Just HRefl <- ta `eqTypeRep` ta' , Just HRefl <- typeRep @f `eqTypeRep` tf , Just HRefl <- tf `eqTypeRep` tf' = Just $ Dynamic (App tf tr) (f <*> x) -- | Applies a 'Dynamic' function to a 'Dynamic' value, utilizing -- 'fmap', 'pure', or '<*>' as needed if the function, the argument, or -- both are wrapped in an 'Applicative'. -- Requires the desired 'Applicative' to be specified with visible type -- application dynApplyFmapAp :: forall f. (Applicative f, Typeable f) => Dynamic -- ^ function -> Dynamic -- ^ value -> Maybe Dynamic dynApplyFmapAp (Dynamic (App tf (Fun ta tr)) f) (Dynamic (App tf' ta') x) | Just HRefl <- typeRep @Type `eqTypeRep` typeRepKind ta , Just HRefl <- typeRep @Type `eqTypeRep` typeRepKind tr , Just HRefl <- ta `eqTypeRep` ta' , Just HRefl <- typeRep @f `eqTypeRep` tf , Just HRefl <- tf `eqTypeRep` tf' = Just $ Dynamic (App tf tr) (f <*> x) dynApplyFmapAp (Dynamic (App tf (Fun ta tr)) f) (Dynamic ta' x) | Just HRefl <- typeRep @Type `eqTypeRep` typeRepKind ta , Just HRefl <- typeRep @Type `eqTypeRep` typeRepKind tr , Just HRefl <- ta `eqTypeRep` ta' , Just HRefl <- typeRep @f `eqTypeRep` tf = Just $ Dynamic (App tf tr) (f <*> (pure x)) dynApplyFmapAp (Dynamic (Fun ta tr) f) (Dynamic (App tf ta') x) | Just HRefl <- typeRep @Type `eqTypeRep` typeRepKind ta , Just HRefl <- typeRep @Type `eqTypeRep` typeRepKind tr , Just HRefl <- ta `eqTypeRep` ta' , Just HRefl <- typeRep @f `eqTypeRep` tf = Just $ Dynamic (App tf tr) (f <$> x) dynApplyFmapAp f x = dynApply f x