Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Strange Haskell expression with type Num ([Char] -> t) => t

While doing some exercises in GHCi I typed and got the following>

ghci> (1 "one")

<interactive>:187:1:
  No instance for (Num ([Char] -> a0)) arising from a use of ‘it’
  In a stmt of an interactive GHCi command: print it

which is an error, howeve if I ask GHCi for the type of the expression it does not give any error:

ghci> :type (1 "one")
(1 "one") :: Num ([Char] -> t) => t

What is the meaning of (1 "one")?

Why does this expression gives an error, but GHCi tells it is well typed?

What is the meaning of Num ([Char] -> t) => t?

Thanks.

like image 282
mljrg Avatar asked Jul 19 '15 17:07

mljrg


3 Answers

Haskell Report to the rescue! (Quoting section 6.4.1)

An integer literal represents the application of the function fromInteger to the appropriate value of type Integer.

fromInteger has type:

Prelude> :t fromInteger
fromInteger :: Num a => Integer -> a

So 1 is actually syntax sugar for fromInteger (1 :: Integer). Your expression, then, is:

fromInteger 1 "one"

Which could be written as:

(fromInteger 1) "one"

Now, fromInteger produces a number (that is, a value of a type which is an instance of Num, as its type tells us). In your expression, this number is applied to a [Char] (the string "one"). GHC correctly combines these two pieces of information to deduce that your expression has type:

Num ([Char] -> t) => t

That is, it would be the result (of unspecified type t) of applying a function which is also a Num to a [Char]. That is a valid type in principle. The only problem is that there is no instance of Num for [Char] -> t (that is, functions that take strings are not numbers, which is not surprising).

P.S.: As Sibi and Ørjan point out, in GHC 7.10 and later you will only see the error mentioned in the question if the FlexibleContexts GHC extension is enabled; otherwise the type checker will instead complain about having fixed types and type constructors in the class constraint (that is, Char, [] and (->)).

like image 91
duplode Avatar answered Nov 20 '22 02:11

duplode


Haskell is a very flexible language, but also a very logical one in a rather literal sense. So often, things that in most languages would just be syntax errors, Haskell will look at them and try its darnedest to make sense of them, with results that are really confusing but are really just the logical consequence of the rules of the language.

For example, if we type your example into Python, it basically tells us "what you just typed in makes zero sense":

Python 2.7.6 (default, Sep  9 2014, 15:04:36) 
[GCC 4.2.1 Compatible Apple LLVM 6.0 (clang-600.0.39)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> (1 "one")
  File "<stdin>", line 1
    (1 "one")
           ^
SyntaxError: invalid syntax

Ruby does the same thing:

irb(main):001:0> (1 "one")
SyntaxError: (irb):1: syntax error, unexpected tSTRING_BEG, expecting ')'
(1 "one")
    ^
    from /usr/bin/irb:12:in `<main>'

But Haskell doesn't give up that easily! It sees (1 "one"), and it reasons that:

  • Expressions of the form f x are function applications, where f has type like a -> b, x has type a and f x has type b.
  • So in the expression 1 "one", 1 must be a function that takes "one" (a [Char]) as its argument.

Then given Haskell's treatment of numeric literals, it translates the 1 into fromInteger 1 :: Num b => [Char] -> b. fromInteger is a method of the Num class, meaning that the user is allowed to supply their own implementations of it for any type—including [Char] -> b if you are so inclined.

So the error message means that Haskell, instead of telling you that what you typed is nonsense, tells you that you haven't taught it how to construct a number of type Num b => [Char] -> b, because that's the really strange thing that would need to be true for the expression to make sense.

like image 3
Luis Casillas Avatar answered Nov 20 '22 00:11

Luis Casillas


TL;DR: It's a garbled nonsense type that isn't worth getting worried over.

Integer literals can represent values of any type that implements the Num typeclass. So 1 or any other integer literal can be used anywhere you need a number.

doubleVal :: Double
doubleVal = 1

intVal :: Int
intVal = 1

integerVal :: Integer
integerVal = 1

This enables us to flexibly use integral literals in any numeric context.

When you just use an integer literal without any type context, ghci doesn't know what type it is.

Prelude> :type 1
1 :: Num a => a

ghci is saying "that '1' is of some type I don't know, but I do know that whatever type it is, that type implements the Num typeclass".

Every occurrence of an integer literal in Haskell source is wrapped with an implicit fromInteger function. So (1 "one") is implicitly converted to ((fromInteger (1::Integer)) "one"), and the subexpression (fromInteger (1::Integer)) has an as-yet unknown type Num a => a, again meaning it's some unknown type, but we know it provides an instance of the Num typeclass.

We can also see that it is applied like a function to "one", so we know that its type must have the form [Char] -> a0 where a0 is yet another unknown type. So a and [Char] -> a0 must be the same. Substituting that back into the Num a => a type we figured out above, we know that 1 must have type Num ([Char] -> a0) => [Char] -> a0), and the expression (1 "one") has type Num ([Char] -> a0) => a0. Read that last type as "There is some type a0 which is the result of applying a [Char] argument to a function, and that function type is an instance of the Num class.

So the expression itself has a valid type Num ([Char] -> a0) => a0.

Haskell has something called the Monomorphism restriction. One aspect of this is that all type variables in expressions have to have a specific, known type before you can evaluate them. GHC uses type defaulting rules in certain situations when it can, to accomodate the monomorphism restriction. However, GHC doesn't know of any type a0 it can plug into the type expression above that has a Num instance defined. So it has no way to deal with it, and gives you the "No Instance for Num..." message.

like image 1
NovaDenizen Avatar answered Nov 20 '22 00:11

NovaDenizen