type signatures with existentially quantified data constructors
Hi all, I'm trying to write an interpreter for a language embedded in Haskell, that "inherits" Haskell types and values. So I create a type Expr a, to represent expressions of type a, and an evaluator that takes an Expr a and produces an a. (To keep things simple, let's assume there are no variables.) I start with data Expr a = Haskell a | If (Expr Bool) (Expr a) (Expr a) eval :: Expr a -> a eval (Haskell x) = x eval (If x y z) = if eval x then eval y else eval z Next, applications. To correctly type an application expression, I need to introduce an existentially quantified type variable: data Expr a = ... | forall b . Appl (Expr (b->a)) (Expr a) eval (Appl x y) = (eval x) (eval y) Works fine. Things get tricky when I try to add pairs. In order to type a pair expression, I find I need to create a Pair class and introduce a class constraint: class Pair a b c | a -> b c, b c -> a instance Pair (b,c) b c data Expr a = ... | forall b c . Pair a b c => MkPair (Expr b) (Expr c) This seems to express the right type relationships. But now I find I can't write eval for MkPair: eval (MkPair x y) = (eval x, eval y) GHC produces the error message Cannot unify the type-signature variable `a' with the type `(t, t1)' Expected type: a Inferred type: (t, t1) in the definition of function `eval': (eval x, eval y) I have tried various ways of adding type annotations, but I can't seem to make it work. The problem seems to be the combination of class constraint with existential variables. How can I tell the compiler that it should in fact unify a with (t,t1) here? Or am I misunderstanding something, and there is a reason it can't? Perhaps there's a way of typing MkPair without introducing the Pair class? Or is there a completely different approach to my problem? All suggestions welcome. --Avi
How about: =================================================================== data Expr a = Haskell a | If (Expr Bool) (Expr a) (Expr a) | forall b . Appl (Expr (b->a)) (Expr b) | forall b c . MkPair (Expr b) (Expr c) (b -> c -> a) eval :: Expr a -> a eval (Haskell x) = x eval (If x y z) = if eval x then eval y else eval z eval (Appl x y) = (eval x) (eval y) eval (MkPair x y f) = f (eval x) (eval y) mkpair x y = MkPair x y (\x y -> (x, y)) =================================================================== Then, you can say: Main> eval (mkpair (Haskell 3.12) (Haskell True)) (3.12,True) -Levent. On Thursday 16 August 2001 05:11 am, Avi Pfeffer wrote:
Hi all,
I'm trying to write an interpreter for a language embedded in Haskell, that "inherits" Haskell types and values. So I create a type Expr a, to represent expressions of type a, and an evaluator that takes an Expr a and produces an a. (To keep things simple, let's assume there are no variables.) I start with
data Expr a = Haskell a
| If (Expr Bool) (Expr a) (Expr a)
eval :: Expr a -> a eval (Haskell x) = x eval (If x y z) = if eval x then eval y else eval z
Next, applications. To correctly type an application expression, I need to introduce an existentially quantified type variable:
data Expr a = ... | forall b . Appl (Expr (b->a)) (Expr a)
eval (Appl x y) = (eval x) (eval y)
Works fine. Things get tricky when I try to add pairs. In order to type a pair expression, I find I need to create a Pair class and introduce a class constraint:
class Pair a b c | a -> b c, b c -> a
instance Pair (b,c) b c
data Expr a = ... | forall b c . Pair a b c => MkPair (Expr b) (Expr c)
This seems to express the right type relationships. But now I find I can't write eval for MkPair:
eval (MkPair x y) = (eval x, eval y)
GHC produces the error message
Cannot unify the type-signature variable `a' with the type `(t, t1)' Expected type: a Inferred type: (t, t1) in the definition of function `eval': (eval x, eval y)
I have tried various ways of adding type annotations, but I can't seem to make it work. The problem seems to be the combination of class constraint with existential variables. How can I tell the compiler that it should in fact unify a with (t,t1) here? Or am I misunderstanding something, and there is a reason it can't? Perhaps there's a way of typing MkPair without introducing the Pair class? Or is there a completely different approach to my problem? All suggestions welcome.
--Avi
_______________________________________________ Haskell mailing list Haskell@haskell.org http://www.haskell.org/mailman/listinfo/haskell
Levent Erkok wrote: | data Expr a = Haskell a | | If (Expr Bool) (Expr a) (Expr a) | | forall b . Appl (Expr (b->a)) (Expr b) | | forall b c . MkPair (Expr b) (Expr c) (b -> c -> a) This is a beautiful solution! The third argument to MkPair can be seen as "proof" that the expression constructed really is a pair. This trick reminds me bit of a solution I came up with in the following situation. When dealing with different kinds of indexing structures (lists, arrays, etc.), it is often useful to have a type class of the following kind: class Indexing c where (!) :: c a -> Int -> a It is possible to make the following instances: instance Indexing [] where (!) = (!!) instance Indexing Array where (!) = (Array.!) However, there are also other structures in which one would like to index, such as bit-arrays, which have the following signature: data BitArray -- abstract (!) :: BitArray -> Int -> Bool ... It is then possible to make the following type: data BitArray' a = MkBitArray BitArray (a -> Bool) (Bool -> a) The first argument is the real bit array, the second and third are the "proof" that the type parameter 'a' is really a boolean (or isomorphic to it). (Often it is useful to have the conversion both ways). Now one can make the instance: instance Indexing BitArray' where MkBitArray arr back forth ! i = forth (arr BitArray.! i) This trick is akin to implementing for example Sets (which require equality to be defined on their arguments) as follows: data Set a = MkSet [a] (a -> a -> Bool) empty :: Eq a => Set a empty = MkSet [] (==) union :: Set a -> Set a -> Set a -- no type constraints!! MkSet xs1 eq `union` MkSet xs2 _ = nubBy eq (xs1 ++ xs2) ... Note that none of these tricks require type system features that lie outside Haskell98! I used these tricks heavily when converting TkGofer (that used multiple-parameter type classes and overlapping instances) to pure Haskell98. (The result is called Yahu, and will be soon available for downloading. :-) /Koen. -- Koen Claessen http://www.cs.chalmers.se/~koen phone:+46-31-772 5424 mailto:koen@cs.chalmers.se ----------------------------------------------------- Chalmers University of Technology, Gothenburg, Sweden
On Thu, Aug 16, 2001 at 01:11:25AM -0400, Avi Pfeffer wrote:
Works fine. Things get tricky when I try to add pairs. In order to type a pair expression, I find I need to create a Pair class and introduce a class constraint:
class Pair a b c | a -> b c, b c -> a
instance Pair (b,c) b c
I tried making this: class Pair a b c | a -> b c, b c -> a where mkPair :: b -> c -> a breakPair :: a -> (b, c) instance Pair (b,c) b c where mkPair = (,) breakPair = id so that then you can do...
data Expr a = ... | forall b c . Pair a b c => MkPair (Expr b) (Expr c)
This seems to express the right type relationships. But now I find I can't write eval for MkPair:
eval (MkPair x y) = (eval x, eval y)
eval (MkPair x y) = mkPair (eval x) (eval y) But this didn't work either. I agree that it should. As an alternative, you could make Expr into a class rather than a data constructor, like this: ---- class Expr a b | a -> b where eval :: a -> b data Haskell a = Haskell a instance Expr (Haskell a) a where eval (Haskell x) = x data If a = forall x y z . (Expr x Bool, Expr y a, Expr z a) => If x y z instance Expr (If a) a where eval (If x y z) = if eval x then eval y else eval z data Appl a = forall x y b . (Expr x (b -> a), Expr y b) => Appl x y instance Expr (Appl a) a where eval (Appl x y) = eval x (eval y) data MkPair a b = forall x y . (Expr x a, Expr y b) => MkPair x y instance Expr (MkPair a b) (a,b) where eval (MkPair a b) = (eval a, eval b) ---- Best, Dylan Thurston
participants (4)
-
Avi Pfeffer -
Dylan Thurston -
Koen Claessen -
Levent Erkok