haskell - Lowering functions to an embedded language -
how can lower haskell function embedded language in typesafe manner possible. in particular, let's assume have value type like
data type t num :: type int bool :: type bool data ty = tnum | tbool deriving eq data tagged t = tagged (type t) t deriving typeable data dynamic = forall t . typeable t => dynamic (tagged t) deriving typeable forget :: typeable t => tagged t -> dynamic forget = dynamic remember :: typeable b => dynamic -> maybe b remember (dynamic c) = cast c
and want convert function (issucc :: int -> int -> bool)
product of dynamic form , type information, this
data splitfun = sf { dynamic :: [dynamic] -> dynamic , inputtypes :: [ty] , outputtype :: ty }
such apply
function
(\(a:b:_) -> issucc b) == apply (makedynamicfn issucc)
modulo possible exceptions thrown if dynamic types don't match. or, more explicitly, i'd find makedynamicfn :: funtype -> splitfun
. isn't proper haskell type , there's unlikely way pull types issucc
itself, might more like
anint . anint . retbool $ issucc :: splitfun
where anint
, retbool
have printf
-style types.
is such thing possible? there way simulate it?
to implement function of type funtype -> splitfun
, we'll use standard type class machinery deconstruct function types.
now, implementing function directly turns out hard. inputtypes
, outputtype
recursive case, have apply function; can apply function inside dynamic
field, gives no way fill other fields. instead, we'll split task two: 1 function give ty
information, other construct [dynamic] -> dynamic
function.
data proxy = proxy class split r dynfun :: r -> [dynamic] -> dynamic tyinfo :: proxy r -> ([ty], ty) split :: r -> splitfun split f = let (i, o) = tyinfo (proxy :: proxy r) in sf (dynfun f) o
now, tyinfo
doesn't need function, use proxy
pass type information without needing use undefined
on place. note need scopedtypevariables
able refer type variable r
instance declaration. clever use of astypeof
might work.
we have 2 base cases: bool
, int
:
instance split int dynfun _ = forget (tagged num i) tyinfo _ = ([], tnum) instance split bool dynfun b _ = forget (tagged bool b) tyinfo _ = ([], tbool)
there no input types , since have value, not need ask more dynamic
values , return dynamic
of particular value.
next, have 2 recursive cases: bool -> r
, int -> r
instance (split r) => split (int -> r) dynfun f (d:ds) = case remember d :: maybe (tagged int) of (tagged _ i) -> dynfun (f i) ds nothing -> error "dynfun: wrong dynamic type" dynfun f [] = error "dynfun: not enough arguments" tyinfo _ = case tyinfo (proxy :: proxy r) of (i, o) -> (tnum:i, o) instance (split r) => split (bool -> r) dynfun f (d:ds) = case remember d :: maybe (tagged bool) of (tagged _ b) -> dynfun (f b) ds nothing -> error "dynfun: wrong dynamic type" dynfun f [] = error "dynfun: not enough arguments" tyinfo _ = case tyinfo (proxy :: proxy r) of (i, o) -> (tbool:i, o)
these 2 need flexibleinstances
. dynfun
examines first dynamic
argument , if it's okay, can safely apply function f
, continue there. make dynfun :: r -> [dynamic] -> maybe dynamic
, that's trivial change.
now, there's duplication going on. introduce class, such as:
class concrete r getty :: proxy r -> ty gettype :: proxy r -> type r
and write:
instance (typeable r, concrete r) => split r dynfun r _ = forget (tagged (gettype (proxy :: proxy r)) r) tyinfo _ = ([], getty (proxy :: proxy r)) instance (typeable r, concrete r, split s) => split (r -> s) dynfun f (d:ds) = case remember d :: maybe (tagged r) of (tagged _ v) -> dynfun (f v) ds -- ... tyinfo _ = case tyinfo (proxy :: proxy s) of (i, o) -> (getty (proxy :: proxy r):i, o)
but needs both overlappinginstances
, undecidableinstances
.
Comments
Post a Comment