Hugs type signature contains extra type constraints?
Asked Answered
D

2

5

Came across this while playing with Haskell and I'm stumped:

Hugs> :type (\x -> x^2)
\x -> x ^ 2 :: (Integral a, Num b) => b -> b

What is a doing in there? How am I supposed to read that? If I type the same thing into GHCi, it gives me the output I would expect:

Prelude> :type (\x -> x^2)
(\x -> x^2) :: Num a => a -> a

Is this a bug in Hugs?

Dive answered 3/11, 2011 at 16:15 Comment(4)
@CatPlusPlus: At least it's not Gofer :-) I imagine a lot of FP courses were written when GHC didn't have GHCi and was difficult (and large! 50MB!) to install on Windows. The whole point of Hugs was to get people to use Haskell quickly. Hugs also used to support a few interesting Haskell extensions, notably TREX. Gofer, Hugs' predecessor, was the first to introduce multi-parameter typeclasses, if I recall correctly.Krol
Come to think of it, Gofer was Mark P. Jones' brainchild, written to experiment with fancy typeclasses like Functor and Monad. Before that (we're talking Haskell 1.3 here) typeclasses could only be declared for kinds *. There was a lot going on, back around 1992. On the other hand, it's still a lot of fun now: GADTs, type families and constraint kinds, oh my!Krol
Apparently computer science professors use Hugs; I have heard that it makes a good learning platform due to its verbose error messages [citation needed].Dive
Is this wikipedia now or something? xkcd.com/285Sha
H
7

The Integral constraint comes from the exponent of 2. Remember that in Haskell, integer literals are actually polymorphic values of type Num a => a. The compiler then infers that since it's being used as an exponent to (^) :: (Num a, Integral b) => a -> b -> a, it must be of the more constrained type Integral a => a.

To save you from having to disambiguate numeric literals all over your code, Haskell uses type defaulting to pick a reasonable concrete type for any unconstrained numeric types. In this case, that will be Integer. The difference seems to be that :type in Hugs reports the inferred type before this happens, while GHCi reports the type after type defaulting has been applied.

If you specify a concrete type for the exponent yourself, the extra constraint disappears.

Hugs> :type (\x -> x^(2 :: Integer))
\x -> x ^ 2 :: Num a => a -> a
Haftarah answered 3/11, 2011 at 17:19 Comment(2)
A bit strange though, as (Integral a, Num b) => b -> b would not be a valid type to give something explicitly, since the a is unused.Vouch
Very interesting. I would have expected that the program would reject it as ambiguous if it didn't type default.Sha
M
3

This is sort of speculation, but this might have to do with the type of (^). :t (^) in ghci returns (^) :: (Num a, Integral b) => a -> b -> a. I'm guessing hugs sees that the second argument of ^ needs to be an Integral, and even though that argument is just a constant 2, it includes the constraint in the type signature.

Mallissa answered 3/11, 2011 at 16:49 Comment(1)
Yes, a deviation in hugs' printing of types from ghci's. It should probably default the 2 to Integer and only emit the base's type.Disembogue

© 2022 - 2024 — McMap. All rights reserved.