BigMeister
BigMeister

Reputation: 371

Function from tuple - Haskell

a simple problem for ones who are good with Haskell! Why if I write:

let a b = (5,6)

I obtain a function:

a :: p -> (a, b)

Furthermore, b is not instantiated. I tried to understand it in vain. Thank you for help!

Upvotes: 1

Views: 187

Answers (2)

Davislor
Davislor

Reputation: 15164

An answer that goes into more detail about why Haskell comes up with the specific result it does:

Okay, let’s break it down. In Haskell, numeric constants can have more than one possible type, so 5 could be Int, Integer, Word, etc., and (5,6) might have type (Int, Integer), (Int, Int) or something else. So GHC arbitrarily denotes its type as (a,b) and will wait and see if you use the result later in a context that will let it deduce the type. If not, it will default to (Integer, Integer).

It so happens that GHC assigned the same lowercase letters for the unknown types that you used as identifiers on the left side of the equals sign, but they’re unrelated uses of the same names. You can change a b = to f _ = (5,6) if that makes it clearer what Haskell is seeing when it takes you literally.

Your declaration a b is a function named a with one argument, b. This parameter is unused, so GHC cannot deduce a type for it unless you call it, and it designates that as p.

So you declared a function that takes a p and returns a pair of numbers of unknown types a and b: p -> (a,b).

As Sivio Mayolo beat me to saying, if you want to bind variables a and b, you can write (a,b) = (5,6).

P.S.:

HTNW suggests I talk a little more about what values the types of the numbers can have. The Haskell 2010 Report says, “An integer literal represents the application of the function fromInteger to the appropriate value of type Integer.”

So, the constant 5 is equivalent to (fromInteger (5::Integer)).

Where’s fromInteger defined? As part of the typeclass Num. It has type fromInteger :: (Num a) => Integer -> a. That is, every instance of typeclass Num (including Int, Double, and so on) has its own polymorphic version of fromInteger defined for it that will convert an Integer to that type of value.

If the compiler can deduce the type of the constant, either because you provided a type annotation or because you use it in an expression that requires a specific type, it will then know which version of fromInteger to call to get a constant of that type. (In fact, it will surely optimize away the call at compile time and just use the result at runtime.) If you leave it completely up to the compiler, GHC will pick Integer and give you a warning.

If you try to bind x = 5 and then use x as any kind of Num, it will work—but if you try to use it as two different types, the compiler won’t be able to resolve it. So, if you need to use x as an Int in one line, and then you want to pass it as a Double later, you’d want to write the conversion to Double as (fromIntegral x). That way, x is an Int all the time and everyone’s happy.

An example of a conversion that’s possible, but not allowed by the type system: Unlike in C, trying to use a numeric literal as a Char will not work, as Char is not an instance of Num. It is, however, an Enum, so you can use fromEnum and toEnum to convert between Char and Int.

So, if you try this in GHCI, you get:

Prelude> 7 :: Char

<interactive>:1:1: error:
    • No instance for (Num Char) arising from the literal ‘7’
    • In the expression: 7 :: Char
      In an equation for ‘it’: it = 7 :: Char

Char is not an instance of Num, so there is no version of fromInteger that converts to Char.

Prelude> toEnum 7 :: Char
'\a'

This is effectively toEnum (fromInteger 7) :: Char. Every Enum has a toEnum function that converts from Int to that type of Enum. The :: Char at the end tells GHCI that the Enum to convert to is Char, so GHCI can deduce that it should convert 7 from Integer to Int to Char. Haskell borrowed from C the '\a' escape code for ASCII/Unicode codepoint 7. (For alert, and if you typed control-G on a terminal, it used to beep at you. It might still work in your Linux console, or at least make the window flash.)

Upvotes: 4

Silvio Mayolo
Silvio Mayolo

Reputation: 70407

let a b = (5, 6)

You are defining a function called a, which takes one argument, called b. This function ignores its argument and returns (5, 6). I assume you want a and b to be bound, respectively, to 5 and 6, in which case you want the following syntax.

let (a, b) = (5, 6)

Upvotes: 8

Related Questions