Doug McIlroy wrote:
A fragment of an attempt to make pairs serve as complex numbers, using ghc/hugs extensions:
instance Num a => Num (a,a) where (x,y) * (u,v) = (x*u-y*v, x*v+y*u)
Unfortunately, type inference isn't strong enough to cope with
(1,1)*(1,1)
It is not quite difficult to tell the type checker that if pairs of numbers are numbers, the two components of the pair must have the same type. We say so literally. We first should make the instance more general to permit any pair, of the type (a,b) to match. We next impose the constraint that the types a and b must be in the class Num; furthermore, the types a and b must be the same. Here is the complete solution that should work on GHC 6.4, 6.6, 6.8, and probably of earlier and later versions.
{-# LANGUAGE MultiParamTypeClasses, FunctionalDependencies, UndecidableInstances, FlexibleInstances #-}
module D where
instance (Num a, Num b, TypeCast b a, TypeCast a b) => Num (a,b) where (x,y) * (u,v) = (typeCast x * typeCast u - typeCast y * typeCast v, typeCast x * typeCast v + typeCast y * typeCast u) (x,y) + (u,v) = (typeCast x + typeCast u, typeCast y + typeCast v) (x,y) - (u,v) = (typeCast x - typeCast u, typeCast y - typeCast v) fromInteger x = (fromInteger x, 0)
test1 = (1,1) * (2,2) -- (0.0,4.0) test2 = (1.1,1) * (2,2) -- (0.20000000000000018,4.2) test3 = test1 * test2 -- (-16.8,0.8000000000000007) test4 = (test1 + test3) * (test1 - test3) -- (-297.6,26.880000000000024) test4' = -16 - test3 * test3 -- (-297.6,26.880000000000024)
class TypeCast a b | a -> b, b->a where typeCast :: a -> b class TypeCast' t a b | t a -> b, t b -> a where typeCast' :: t->a->b class TypeCast'' t a b | t a -> b, t b -> a where typeCast'' :: t->a->b instance TypeCast' () a b => TypeCast a b where typeCast x = typeCast' () x instance TypeCast'' t a b => TypeCast' t a b where typeCast' = typeCast'' instance TypeCast'' () a a where typeCast'' _ x = x
As one can see, we added typeCast before every variable, indicating that an application of the equality constraint is needed. We let the GHC figure out what should be `cast' to what. The recent versions of GHC have a nifty equality constraint, so the code can be written simply
{-# LANGUAGE TypeFamilies #-}
{-# OPTIONS -fglasgow-exts #-} {-# OPTIONS -fallow-undecidable-instances #-}
module D where
instance (Num a, Num b, a ~ b) => Num (a,b) where (x,y) * (u,v) = (x*u-y*v, x*v+y*u)
test1 = (1,1) * (2,2)
It does typecheck in GHC 6.8.2; alas, running the code produces
ghc-6.8.2: panic! (the 'impossible' happened) (GHC version 6.8.2 for i386-unknown-freebsd): nameModule $dNum{v aJiF}
I guess one needs to upgrade to GHC 6.10. The solution using TypeCast, however inelegant, works on GHC 6.8 and earlier compilers.
In ghc 6.10.1 the ~ constraint is working:
{-# LANGUAGE TypeFamilies #-}
{-# OPTIONS -fglasgow-exts #-} {-# OPTIONS -fallow-undecidable-instances #-}
module D where
instance (Num a, Num b, a ~ b) => Num (a,b) where (x,y) * (u,v) = (x*u-y*v, x*v+y*u)
test1 = (1,1) * (2,2)
test2 = (1,1.0)*(2,2)
With ghci: *D> test1 test1 (0,4) *D> test2 test2 (0.0,4.0) *D> :t test1 :t test1 test1 :: (Integer, Integer) *D> :t test2 :t test2 test2 :: (Double, Double) -- Chris
On Tue, Jan 27, 2009 at 4:51 AM, <oleg@okmij.org> wrote:
Doug McIlroy wrote:
A fragment of an attempt to make pairs serve as complex numbers, using ghc/hugs extensions:
instance Num a => Num (a,a) where (x,y) * (u,v) = (x*u-y*v, x*v+y*u) The recent versions of GHC have a nifty equality constraint, so the code can be written simply
I'm confused on why instance Num a => Num (a, a) where is not equivalent to instance (Num a, Num b, a ~ b) => Num (a, b) where I don't know the details of the type inference algorithm. What am I missing to understand why they are not the same? Cheers, Corey O'Connor
Corey O'Connor wrote:
On Tue, Jan 27, 2009 at 4:51 AM, <oleg@okmij.org> wrote:
Doug McIlroy wrote:
A fragment of an attempt to make pairs serve as complex numbers, using ghc/hugs extensions:
instance Num a => Num (a,a) where (x,y) * (u,v) = (x*u-y*v, x*v+y*u) The recent versions of GHC have a nifty equality constraint, so the code can be written simply
I'm confused on why instance Num a => Num (a, a) where
is not equivalent to instance (Num a, Num b, a ~ b) => Num (a, b) where
I don't know the details of the type inference algorithm. What am I missing to understand why they are not the same?
Type inference doesn't backtrack, and it starts by giving different expressions different free type variables. So it doesn't initially "know" that the two numeric literals in the tuple have the same type, and so the instance ... => Num (a, a) doesn't apply - after all, there might be some different instance where the two tuple elements are of different types. In contrast Num (a, b) will always apply, and only later do we discover that we've forced a = b by selecting it. But that's fine as there's no possibility of another Num instance for tuples (ignoring overlapping instances for now). Ganesh ============================================================================== Please access the attached hyperlink for an important electronic communications disclaimer: http://www.credit-suisse.com/legal/en/disclaimer_email_ib.html ==============================================================================
On Tue, 2009-01-27 at 10:05 -0800, Corey O'Connor wrote:
On Tue, Jan 27, 2009 at 4:51 AM, <oleg@okmij.org> wrote:
Doug McIlroy wrote:
A fragment of an attempt to make pairs serve as complex numbers, using ghc/hugs extensions:
instance Num a => Num (a,a) where (x,y) * (u,v) = (x*u-y*v, x*v+y*u) The recent versions of GHC have a nifty equality constraint, so the code can be written simply
I'm confused on why instance Num a => Num (a, a) where
The head of this instance is Num (a, a), while
is not equivalent to instance (Num a, Num b, a ~ b) => Num (a, b) where
the head of this instance is Num (a, b) So if GHC doesn't already have an equation a ~ b in scope, it'll skip the first instance during instance search, but find the second one (incidentally introducing the equation a ~ b as above). So GHC is more aggressive about using the second instance. (Or, to put it another way: instance selection ignores the context on the instance. Or, to put it another way: the context on the instance is part of the *output* of instance selection, not the input). jcc
participants (5)
-
ChrisK -
Corey O'Connor -
Jonathan Cast -
oleg@okmij.org -
Sittampalam, Ganesh