This utility module provides DynT, a dynamically-typed value that restricts the injected value to be a Functor. It can be considered an improved Data.Dynamic.Dynamic.
Below we will refer the Dynamic provided by base as Base.Dynamic:
import qualified Data.Dynamic as Base
import Data.DynTLet's say we have a list of Base.Dynamic, where each value wrapped inside must be actually of type [a]:
let dyns :: [Base.Dynamic]
dyns = [ Base.toDyn [True, False]
, Base.toDyn "abcdefghijklmnopqrstuvwxyz"
, Base.toDyn ([] :: [Int])
, Base.toDyn (["abc", "def"] ^? ix 0)
]We can unwrap a Base.Dynamic with Base.fromDynamic or Base.fromDyn:
λ> Base.fromDynamic (dyns !! 0) :: Maybe [Bool]
Just [True,False]However, it is also completely possible to accidentally write:
λ> Base.fromDynamic (dyns !! 2) :: Maybe (Vector Int)
NothingOops!
The problem is that the interface of Data.Dynamic:
Base.toDyn :: Typeable a => a -> Base.Dynamic
Base.fromDynamic :: Typeable a => Base.Dynamic -> Maybe adoesn't really restrict what a could be. Ideally, we would like to a restricted version of Data.Dynamic that captures the "static" part of the structure inside a dynamically-typed value, and the conversion functions should respect said structure.
By the way, did you notice that dyns !! 3 is actually a Maybe String instead of a String? The type checker didn't catch that either.
DynT transforms a Functor so that it maps over a dynamically-typed value.
data DynT :: (* -> *) -> * where
DynT :: f Any -> TypeRep -> DynT f
toDynT :: (Functor f, Typeable a) => f a -> DynT f
fromDynT :: (Functor f, Typeable a) => DynT f -> Maybe (f a)
dynTypeRep :: DynT f -> TypeRepNow, the example above won't compile:
let dyns :: [DynT []]
dyns = [ toDynT [True, False]
, toDynT "abcdefghijklmnopqrstuvwxyz"
, toDynT ([] :: [Int])
, toDynT $ ["abc", "def"] ^? ix 0
]
Couldn't match type ‘Maybe’ with ‘[]’
Expected type: DynT []
Actual type: DynT Maybe
In the expression: toDynT $ ["abc", "def"] ^? ix 0Removing the offending element, and we may now convert a DynT [] back with fromDynT:
λ> fromDynT (dyns !! 0) :: Maybe [Bool]
Just [True,False]Attempting to convert a DynT [] to something that is not a list results in a type error:
λ> fromDynT (dyns !! 2) :: Maybe (Vector Int)
Couldn't match type ‘[]’ with ‘Vector’
Expected type: [DynT Vector]
Actual type: [DynT []]type Dyn = DynT Identity
toDyn :: Typeable a => a -> Dyn
fromDyn :: Typeable a => Dyn -> Maybe aDyn is functionally equivalent to Data.Dynamic.Dynamic. Note that it isn't a drop-in replacement however.
Base.fromDynamic = fromDyn
Base.fromDyn dyn def = fromMaybe def $ fromDyn dynrunDynT :: Functor f => DynT f -> f DynConvert a DynT f to a plain Dyn wrapped under f.
wrapDyn :: (Alternative m, Monad m) => m Dyn -> TypeRep -> DynT m
wrapDynFail :: Monad m => m Dyn -> TypeRep -> DynT mConvert an existing Dyn under a m to DynT m. Unfortunately, it isn't possible to access the TypeRep under m Dyn without m being Copointed, so users must supply the correct TypeRep manually. These two functions differ in how they report the error when the supplied TypeRep does not match the actual TypeRep inside Dyn. wrapDyn returns an empty, and wrapDynFail reports the mismatch with fail.
An example could be passing a DynT f to a function of type DynT f -> f Dyn where you would like to wrap the result back to DynT f again:
let f :: DynT f -> f Dyn
foo :: (Alternative f, Monad f) => DynT f -> DynT f
foo d = wrapDyn (f d) (dynTypeRep d)