Reputation: 371
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
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)
.
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
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