
Hi there, I'm currently writing an interpreter that I would like to be able to use with other haskell programs. I would like to be able to pass along arbitrary types though the interpreter. I've seen hints that GADTs can do this, but I am having trouble understanding them. So far, I've learnt you can do this: data Value where VInt :: Integer -> Value ... VWrapper :: a -> Value which can let you encode arbitrary 'dynamic' types into Value. I was hoping to be able to pattern match to get the value out again e.g. doSomething :: Value -> .... doSomething (VWrapper String s) = ..... Also, anything that can help me out with GADTs in general will be much appreciated. Thanks, Kevin.

Hey Kev, The types are "thrown away" during compile time. Therefore, if you have a constructor "VWrapper :: a -> Value" nothing is known about that "a" when you scrutinize it. What you could do, however, is something like this:
data Value a where VInt :: Integer -> Value Integer ... VWrapper :: a -> Value a
And then you can write a function doSomething:
doSomething :: Value String -> String doSomething (VWrapper s) = s
HTH, -chris On 13 jul 2009, at 12:41, Kev Mahoney wrote:
Hi there,
I'm currently writing an interpreter that I would like to be able to use with other haskell programs. I would like to be able to pass along arbitrary types though the interpreter. I've seen hints that GADTs can do this, but I am having trouble understanding them.
So far, I've learnt you can do this:
data Value where VInt :: Integer -> Value ... VWrapper :: a -> Value
which can let you encode arbitrary 'dynamic' types into Value. I was hoping to be able to pattern match to get the value out again e.g.
doSomething :: Value -> .... doSomething (VWrapper String s) = .....
Also, anything that can help me out with GADTs in general will be much appreciated.
Thanks, Kevin. _______________________________________________ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe

On Mon, Jul 13, 2009 at 6:09 AM, Chris Eidhof
Hey Kev,
The types are "thrown away" during compile time. Therefore, if you have a constructor "VWrapper :: a -> Value" nothing is known about that "a" when you scrutinize it.
What you could do, however, is something like this:
data Value a where
VInt :: Integer -> Value Integer ... VWrapper :: a -> Value a
And then you can write a function doSomething:
doSomething :: Value String -> String
doSomething (VWrapper s) = s
I would like to put in a thumbs up on this approach. I'm currently experimenting with interpreters, and have found that parameterizing over the value type works quite smoothly. Specifically, this is my usual starting point for the values: data Value a = VFun (Value a -> Value a) | VPrim a Then I use a typeclass to endow the primitives with the structure they need: class ValueType a where apply :: a -> a -> a Here's a simple interpreter for terms in De Bruijn notation: data Term a = TLit a | TApp (Term a) (Term a) | TLam (Term a) | TVar Int eval :: (ValueType a) => Term a -> [Value a] -> Value a eval (TLit x) = const (VPrim x) eval (TApp x y) = let x' = eval x y' = eval y in \env -> x' env % y' env eval (TLam body) = let body' = eval body in \env -> VFun (\x -> body (x:env)) eval (TVar z) = \env -> env !! z (%) :: (ValueType a) => Value a -> Value a -> Value a VFun f % x = f x VPrim x % VFun _ = error "Apply primitive to function not supported" VPrim x % VPrim y = VLit (x `apply` y) And an example ValueType: data Prim = PInt Int | PSucc instance ValueType Prim where apply PSucc (PInt z) = PInt $! z+1 apply _ _ = error "Type error" This approach has been very nice and modular for experimenting with dynamically typed interpreters. You could support application of literals to functions with some more support from the type class, but it wasn't worth it to me (and would limit the interpretation strategies that I would be able to use). The decision about what suite of primitives to include and how they combine with each other is pushed out to the user, and the interpreter just focuses on the important things: functions. You could even write a little primitive combinator library (perhaps made more composable by switching to dictionary passing for ValueType instead of typeclass), so that users can easily specify any suite of primitives. Anyway, those were just some thoughts for you. Luke

On Mon, Jul 13, 2009 at 12:41 PM, Kev
Mahoney
So far, I've learnt you can do this:
data Value where VInt :: Integer -> Value ... VWrapper :: a -> Value
which can let you encode arbitrary 'dynamic' types into Value. I was hoping to be able to pattern match to get the value out again e.g.
As such this type is pretty useless, since you don't know anything about a, you can't do anything with it... Which is why you add typeclass constraints, so you can use this value. Data.Dynamic adds a Typeable constraint, which allows you to do safe coercing, so you can have a "typecase", if not like you tried to do it. -- Jedaï

Thanks, I hadn't noticed Data.Dynamic. It never even occurred to me
that something like this would be in the standard libraries. It looks
like it's precisely what I was looking for, after a brief scan of the
documentation.
I will report back if I bump into any problems with it
2009/7/13 Chaddaï Fouché
On Mon, Jul 13, 2009 at 12:41 PM, Kev Mahoney
wrote: So far, I've learnt you can do this:
data Value where VInt :: Integer -> Value ... VWrapper :: a -> Value
which can let you encode arbitrary 'dynamic' types into Value. I was hoping to be able to pattern match to get the value out again e.g.
As such this type is pretty useless, since you don't know anything about a, you can't do anything with it... Which is why you add typeclass constraints, so you can use this value. Data.Dynamic adds a Typeable constraint, which allows you to do safe coercing, so you can have a "typecase", if not like you tried to do it.
-- Jedaï
participants (4)
-
Chaddaï Fouché
-
Chris Eidhof
-
Kev Mahoney
-
Luke Palmer