I have a question about head
, tail
, init
and last
.
The following works in GHCi:
Prelude Data.List Data.Char> let n = [1..10] in (head n : tail n)
[1,2,3,4,5,6,7,8,9,10]
As expected, I get the whole list. So this should work for init
and last
too,
right?
Prelude Data.List Data.Char> let n = [1..10] in (init n : last n)
<interactive>:39:1:
Non type-variable argument in the constraint: Enum [[a]]
(Use FlexibleContexts to permit this)
When checking that ‘it’ has the inferred type
it :: forall a. (Enum a, Enum [[a]], Num a, Num [[a]]) => [[a]]
If I look at the type signatures for the functions, then head
and last
look the same -- they both return an element. Also init
and tail
look
the same, because they both return lists.
Prelude Data.List Data.Char> :info head
head :: [a] -> a -- Defined in ‘GHC.List’
Prelude Data.List Data.Char> :info tail
tail :: [a] -> [a] -- Defined in ‘GHC.List’
Prelude Data.List Data.Char> :info init
init :: [a] -> [a] -- Defined in ‘GHC.List’
Prelude Data.List Data.Char> :info last
last :: [a] -> a -- Defined in ‘GHC.List’
So what does Non type-variable argument in the constraint: Enum [[a]]
mean?
If I do init n
or last n
without the construction of a new list, I get
[1..9]
and 10
.
It is true that head
/last
and tail
/init
have identical types. So if you had simply swapped out last
for head
and init
for tail
, you would have had no problem:
> let n = [1..10] in last n : init n
[10,1,2,3,4,5,6,7,8,9]
But you didn't. You did both that swap and another: you changed the order of arguments to :
. It so happens that :
doesn't take two arguments of the same type:
> :t (:)
(:) :: a -> [a] -> [a]
So this last swap is not okay! In fact, if you give n
a slightly more specific type signature, ghci will give a better error:
> let n :: [Integer]; n = [1..10] in init n : last n
<interactive>:1:50:
Couldn't match type ‘Integer’ with ‘[[Integer]]’
Expected type: [[[Integer]]]
Actual type: [Integer]
In the first argument of ‘last’, namely ‘n’
In the second argument of ‘(:)’, namely ‘last n’
This error is still not 100% clear, but I think with a bit of puzzling you can see what it's complaining about: since init n :: [Integer]
, and (:) :: [Integer] -> [[Integer]] -> [[Integer]]
, it's expecting last n :: [[Integer]]
and therefore n :: [[[Integer]]]
. But you explicitly said n :: [Integer]
, a conflict.
Now, what about the error it actually gave you in your case? Well, the clue is in the type of [1..10]
:
> :t [1..10]
[1..10] :: (Enum t, Num t) => [t]
Notice that [1..10]
is polymorphic. Moreover, it is used twice in your expression, and so it can be given separate monomorphic types in the two uses. So [1..10]
is instantiated with two different types in the sequel!
Now I think you can start to see where the error you got comes in. It's trying to find a type a
for which:
Enum a
-- this is needed to do the ..
part of init [1..10]
Num a
-- this is needed to do the 1
and 10
parts of init [1..10]
Enum [[a]]
-- if init n :: a
, then to have init n : last n
be well-typed, we must have last n :: [a]
and hence the second occurrence of n
must have n :: [[a]]
; then the Enum
constraint is needed for the ..
part of last [1..10]
Num [[a]]
-- by similar reasoning, this is needed to do the 1
and 10
parts of last [1..10]
But these constraints together are hard to satisfy -- certainly there's no instance of Enum
and Num
for lists in scope in the Prelude
or Data.List
. So it complains.