I am trying to create a function which adds a 1 before each of the entries in a given list. I haven't quite grasped the syntax for Haskell and am wondering what is wrong with this code. For example, I would like this to return the list [1,1,1,2,1,3]
ins1 :: [a] -> [a]
ins1 [x] = [x]
ins1 (x:xs) = [1] ++ [x] ++ ins1(xs)
main = print(ins1 [1,2,3])
I get the error:
• No instance for (Num a) arising from the literal ‘1’
Possible fix:
add (Num a) to the context of
the type signature for:
ins1 :: [a] -> [a]
• In the expression: 1
In the first argument of ‘(++)’, namely ‘[1]’
In the expression: [1] ++ [x] ++ ins1 (xs)
<interactive>:3:1: error:
• Variable not in scope: main
• Perhaps you meant ‘min’ (imported from Prelude)
Well like the error says, you use ins1
, and you write [1] ++ [x] ++ ...
.
Now 1
is a numerical literal, so it can take all numerical types . Hence 1
has type Num b => b
, as a result [1]
has type Num b => [b]
.
Later you append the list with x
and recursion, hence we now know that a ~ b
( a
and b
are the same type). So we have to add a type constraint to the signature for a
:
ins1 :: [a] -> [a]
ins1 [x] = [x]
ins1 (x:xs) = [1] ++ [x] ++ ins1(xs)
This solves the compile error, but probably will not generate what you want. Since now there is no case for the empty list. Indeed, both the [x]
pattern and the (x:xs)
pattern work with lists that respectively match with lists with exactly one element, and at least one element.
Therefore I think that your first clause should actually match the empty list:
ins1 :: Num a => [a] -> [a]
ins1 (x:xs) = [1] ++ [x] ++ ins1(xs)
There is also an inefficiency in the second clause: you append to a list of one element, so we can use the " cons " data cosntructor (:)
here:
ins1 :: Num a => [a] -> [a]
ins1 [] = []
ins1 (x:xs) =
This will insert a 1
for every element in the original list, so:
Prelude> ins1 [1, 4, 2, 5]
[1,1,1,4,1,2,1,5]
If you give me a function of type [a] -> [a]
, you're saying that, for all types a
that I choose, if I give you a list of values of type a
, then you can give me back a list of elements of that type.
So if I choose a
to be Int
by giving you [2, 3, 4] :: [Int]
, then all is well: the literal 1
in the implementation of ins1
is constrained from Num t => t
by t
= Int
to Num Int => Int
. That works because there is an instance Num Int
.
However, if I choose a
to be Char
, by giving you ['a', 'b', 'c']
(= "abc"
), then t
= Char
, giving Num Char => Char
, which is an error because there's no instance Num Char
. Therefore, this is a counterexample for the type: your function doesn't work for all a
, only those a
that have an instance of Num
. So you need to express this constraint in the type signature:
ins1 :: (Num a) => [a] -> [a]
The compiler can infer this for you, and will display a warning about a missing type signature if -Wall
is enabled (or -Wmissing-signatures
specifically). This will also warn about the fact that ins1
is non-exhaustive : you don't handle the case of an empty input list. Alternatively, you can enter the definition into GHCi and ask for its type with :type ins1
or :t ins1
.
Generic functions in Haskell are parametrically polymorphic , which means that if you have an unconstrained type variable like a
, you know nothing about it, not even that it can be constructed from a number literal. So the only things a function of type [a] -> [a]
can do are copy, rearrange, or drop elements from the input, or fail to terminate (loop infinitely or raise an error)—it can't construct new elements or perform any class-specific operations on those elements, such as +
from Num
or <
from Ord
.
This may sound limiting, but in fact it's incredibly useful: the less you know about a type, the fewer options you have for misusing it. And by a neat trick called the Curry–Howard correspondence, you can examine a polymorphic function type like head :: [a] -> a
and think of it as a logical formula: does having a list of a
imply you can get an a
? No, because the list may be empty. So you know that a head
function with this type must raise an error if the input is empty, because it has no generic way to construct an a
.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.