Ticket #1696 (closed bug: fixed)

Opened 6 years ago

Last modified 3 years ago

Confusing type signature

Reported by: guest Owned by:
Priority: low Milestone: _|_
Component: Compiler (Type checker) Version: 6.6.1
Keywords: Cc:
Operating System: Unknown/Multiple Architecture: Unknown/Multiple
Type of failure: None/Unknown Difficulty: Unknown
Test Case: Blocked By:
Blocking: Related Tickets:

Description (last modified by simonpj) (diff)

I was working with some buggy numerical code of mine, and I was having problems with some types involving exponentiation. My working hypothesis was that the problem involved using (^) with a numerical type I had defined - I had checked (^)'s type through :t and saw:

 (^) :: forall a b. (Integral b, Num a) => a -> b -> a

I immediately thought that I needed another type class declaration for my new type, and went haring off on that tangent for a long time. Eventually someone on #haskell pointed out to me that the *base* could be Num, but the power to which it was being raised had to be Integral and that my problems stemmed from going foo(1/3), and that what I needed was more along the lines of foo**(1/3).

My confusion stemmed from the variables - the forall declaration goes, in order, a-b, and the curried signature itself goes a-b as well, but the classes goes b-a! This apparently is for no particular reason, and so I think it'd be good if the signatures :t displayed could be a little more consistent and go a-b as well, so it'd be instead:

 (^) :: forall a b. (Num a, Integral b) => a -> b -> a

A small thing, perhaps, but it did trip me up.

Change History

Changed 6 years ago by simonpj

  • description modified (diff)

Was the type of (^) declared by you with a type signature, or inferred?

Simon

Changed 6 years ago by guest

The ^ was the normal Prelude one. Looks the formatting got messed up (what a confusing markup. I like MediaWiki?'s better). I meant more along the lines of:

"My working hypothesis was that the problem involved ^ with one of the arguments being a numerical type I had defined (positive reals, essentially) - I had checked ^'s type through :t and saw:"

Changed 6 years ago by simonpj

  • priority changed from normal to low
  • component changed from Compiler to Compiler (Type checker)
  • description modified (diff)
  • milestone set to _|_

Ah now I see. When you say

:t (^)

GHC infers the type of the (rather small) expression (^). In doing so, it instantiates the function and re-generalises it. That jumbles up the constraints.

Random thoughts:

  • We could have a special case for :t of a single identifier, which simply prints the type of the identifier. That would be a bit of a hack, but would eliminate any jumbling in a common case.
  • When GHCi displays a type for the user, it drops the foralls, and combines constraints (actually this is a recent fix). It could do more: it could re-order the constraints to look nicer.
  • Alternatively, when generalising, GHC could sort the constraints (and perhaps the type variables too) into a nicer order. That would entail extra work that would usually go unseen; but perhaps it'd be worth it.

I don't this this is very high priority, but I'm jotting down these notes so that we don't lose the thought. Thanks for raising it.

Simon

Changed 5 years ago by simonmar

  • architecture changed from Unknown to Unknown/Multiple

Changed 5 years ago by simonmar

  • os changed from Unknown to Unknown/Multiple

Changed 3 years ago by nominolo

  • status changed from new to closed
  • failure set to None/Unknown
  • resolution set to fixed
  • patch set to 0

Seems to be fixed in 6.12.1.

Prelude> :t (^)
(^) :: (Num a, Integral b) => a -> b -> a
Note: See TracTickets for help on using tickets.