
I was playing around with type constructors and I wrote this: data Foo a b = Foo1 [a] | Foo2 (a -> b) t3 = Foo1 [1, 2, 3] I wanted to see what ghci thought the type of t3 was. Essentially, it's data that doesn't use all of the type variables. So this ran fine, and *Main> :t t3 t3 :: Foo Integer b Wow! The data exists but it doesn't have a "complete type" so to speak. This worked, too: f3 (Foo1 xs) = length xs *Main> f3 t3 3 This is surprising to a conventional programmer. But does this naturally relate to other features of Haskell. Perhaps laziness? (I.e. data of type Foo doesn't always need a type b so it just doesn't have one until it needs one.) Thanks, Mike

2009/08/01 Michael P Mossey
This is surprising to a conventional programmer. But does this naturally relate to other features of Haskell. Perhaps laziness? (I.e. data of type Foo doesn't always need a type b so it just doesn't have one until it needs one.)
Laziness is a runtime thing; type constraints are compile time. So "...doesn't have one until it needs one." isn't right -- `t3` will *never* have a type for `b` in the program snippet above. If you add more code (create a different context) then you might constrain `b` more and lead to a more definite type. The type is always a static type, though -- types don't get more specific as the program runs. -- Jason Dusek

Am Samstag 01 August 2009 19:44:49 schrieb Michael P Mossey:
I was playing around with type constructors and I wrote this:
data Foo a b = Foo1 [a]
| Foo2 (a -> b)
t3 = Foo1 [1, 2, 3]
I wanted to see what ghci thought the type of t3 was. Essentially, it's data that doesn't use all of the type variables. So this ran fine, and
*Main> :t t3 t3 :: Foo Integer b
Actually, the type of t3 could be t3 :: Num a => Foo a b but without a type signature the monomorphism restriction applies and a is defaulted to Integer. *MFoo> :t Foo2 even Foo2 even :: (Integral a) => Foo a Bool
Wow! The data exists but it doesn't have a "complete type" so to speak.
It does, it has a polymorphic type, or one could say it has many types: *MFoo> :t [t3,Foo2 even] [t3,Foo2 even] :: [Foo Integer Bool] *MFoo> :t [t3,Foo2 id] [t3,Foo2 id] :: [Foo Integer Integer] *MFoo> :t (t3 :: Foo Integer Bool) (t3 :: Foo Integer Bool) :: Foo Integer Bool *MFoo> :t (t3 :: Foo Integer Char) (t3 :: Foo Integer Char) :: Foo Integer Char *MFoo> :t (t3 `asTypeOf` (Foo2 even)) (t3 `asTypeOf` (Foo2 even)) :: Foo Integer Bool *MFoo> :t (t3 `asTypeOf` (Foo2 id)) (t3 `asTypeOf` (Foo2 id)) :: Foo Integer Integer
This worked, too:
f3 (Foo1 xs) = length xs
*Main> f3 t3 3
*MFoo> :t (\(Foo1 xs) -> length xs) (\(Foo1 xs) -> length xs) :: Foo t1 t -> Int
This is surprising to a conventional programmer. But does this naturally relate to other features of Haskell. Perhaps laziness? (I.e. data of type Foo doesn't always need a type b so it just doesn't have one until it needs one.)
Polymorphism and type inference, not laziness.
Thanks, Mike
participants (3)
-
Daniel Fischer
-
Jason Dusek
-
Michael P Mossey