What is the difference between ++ and : in haskell? - haskell

I don't get this--
Prelude> "hi"++"there"
"hithere"
Prelude> "hi":"there"
<interactive>:12:6:
Couldn't match expected type `[Char]' with actual type `Char'
Expected type: [[Char]]
Actual type: [Char]
In the second argument of `(:)', namely `"there"'
In the expression: "hi" : "there"
Prelude>
Why doesn't that also return "hithere"?

The types. Try this in GCHi:
Prelude> :t (:)
(:) :: a -> [a] -> [a]
Prelude. :t (++)
(++) :: [a] -> [a] -> [a]

I get it now. The first operator needs to be an element, not a list.
So if i did 'h':"ithere" it would return "hithere"

The operator : is one of the constructors for lists. So "hello" is 'h':'e':'l':'l':'o':[]. You can imagine lists being defined like: (not real haskell syntax)
data List a = (:) a (List a) | []
: constructs a list by taking an element and a list. That's why the type is a->[a]->[a].
++ which concatenates lists is defined by using : as a primitive:
(++) :: [a] -> [a] -> [a]
(++) [] ys = ys
(++) (x:xs) ys = x : xs ++ ys

Related

Haskell add to list in list

i have the following code
groupEq :: Eq a => [a] -> [[a]]
groupEq list = foldl (\acc x -> if (isType acc x) then ((last acc) ++ [x]) else acc++[[x]]) [] list
isType :: Eq a => [[a]] -> a -> Bool
isType list item
| (length list) == 0 = False
| head (last list) == item = True
| otherwise = False
Now, i am having difficulties understanding why it would not compile.
The problem is with the ((last acc) ++ [x]) part of it. I understand it as it takes the last element of accumulator, which would be [[a]] at this point and tries to add an element to it.
The idea what i want to achieve is this:
-- groupEq [1,2,2,3,3,3,4,1,1] ==> [[1], [2,2], [3,3,3], [4], [1,1]]
Full error is
Couldn't match type ‘a’ with ‘[a]’
‘a’ is a rigid type variable bound by
the type signature for groupEq :: Eq a => [a] -> [[a]]
at exam_revisited.hs:3:12
Expected type: [[[a]]]
Actual type: [[a]]
Relevant bindings include
x :: a (bound at exam_revisited.hs:4:28)
acc :: [[a]] (bound at exam_revisited.hs:4:24)
list :: [a] (bound at exam_revisited.hs:4:9)
groupEq :: [a] -> [[a]] (bound at exam_revisited.hs:4:1)
In the first argument of ‘last’, namely ‘acc’
In the first argument of ‘(++)’, namely ‘(last acc)’
What am i missing here?
groupEq is declared to return [[a]], but ((last acc) ++ [x]) is of type [a].
A quick and dirty solution is to change this expression into
init acc ++ [last acc ++ [x]].

rigid type variable error in Haskell

Why this gives rigid type variable error:
data MyTree a = Leaf [a]
| Branch (String, a) [MyTree a]
deriving (Show)
list :: MyTree a -> [a]
list (Leaf []) = []
list (Leaf m) = m
list (Branch _ (x:xs)) = list x ++ map (list) xs
-------------------------------------------------------------
Couldn't match type `a' with `[a]'
`a' is a rigid type variable bound by
the type signature for list :: MyTree a -> [a]
at test.hs:6:15
Expected type: MyTree a -> a
Actual type: MyTree a -> [a]
In the first argument of `map', namely `(list)'
In the second argument of `(++)', namely `map (list) xs'
In the expression: list x ++ map (list) xs
The part of the error that actually tells you what is happening is:
Expected type: MyTree a -> a
Actual type: MyTree a -> [a]
In the first argument of `map', namely `(list)'
So the type of the function you give to map is wrong. But why is it so? map has type:
map :: (a -> b) -> [a] -> [b]
list is MyTree a -> [a], and therefore:
map list :: (MyTree a -> [a]) -> [MyTree a] -> [[a]]
That means map list xs will have type [[a]]. You are using it like this:
list x ++ map list xs -- omitting unnecessary parentheses.
(++) is list concatenation; it expects two lists of the same type. list x, however, is [a] instead of [[a]], which leads to the type error. Since [a] is the type of an element of [[a]], you might try using (:), instead of (++), to prepend list x to the rest of your list-of-lists.
list (Branch _ (x:xs)) = list x : map list xs
That, however, is redundant: you are applying the same list function to x and the elements of xs. That means you can simplify it to:
list (Branch _ xs) = map list xs
We are still not done, as map list xs has type [[a]], and you want [a]. That is easy to solve, though: just use concatMap, which maps the function and flattens the resulting list-of-lists. The full definition would then become:
list :: MyTree a -> [a]
list (Leaf m) = m
list (Branch _ xs) = concatMap list xs
I have removed the redundant (Leaf []) case. Note that your function did not cover the (
Branch _ []) case; that's not a problem now that we are not matching (x:xs) only.
The type of map (list) xs is [[a]] and you want something of type [a]. There is a function concat that we can use: concat (map list xs), but we can write it more idiomatically with concatMap: concatMap list xs

Haskell using foldl like recursion

I'm trying to get a function working that uses foldl
to go through a list of tuples and create a string from it.
I'm trying to create a similar function that already works using recursion.
Here is the code that I'm trying to compile:
citeBook :: (String, String, Integer) -> String
citeBook (name, titl, date) = (titl ++ " (" ++ name ++ ", " ++ show date ++ ")\n")
--Recursion function
-- function must be called with putStr in order for newlines to work
bibliography_rec :: [(String, String, Integer)] -> String
bibliography_rec [] = ""
bibliography_rec xs = (citeBook(head xs) ++ bibliography_rec (tail xs))
--foldl function
bibliography_fold :: [(String, String, Integer)] -> String
bibliography_fold [] = ""
bibliography_fold (x:xs) = foldl (++) citeBook(x) xs --ERROR HERE
So in the very last line of the provided code, I am trying to have foldl
use (++) as the operator in order to combine the strings in the list.
I'm using citeBook(x) as my base case, since x will be the first tuple
taken from the list. Note that citeBook(x) returns a string. Then continue
folding with the list xs.
Here are the errors I'm getting. I think my parameter types for foldl aren't
matching up with what is expected, but everything seems okay to me..
hw1.hs:28:34:
Couldn't match type `[a0]'
with `(String, String, Integer) -> String'
Expected type: ((String, String, Integer) -> String)
-> [a0] -> (String, String, Integer) -> String
Actual type: [a0] -> [a0] -> [a0]
In the first argument of `foldl', namely `(++)'
In the expression: foldl (++) citeBook (x) xs
In an equation for `bibliography_fold':
bibliography_fold (x : xs) = foldl (++) citeBook (x) xs
hw1.hs:28:48:
Couldn't match expected type `[[a0]]'
with actual type `(String, String, Integer)'
In the third argument of `foldl', namely `(x)'
In the expression: foldl (++) citeBook (x) xs
In an equation for `bibliography_fold':
bibliography_fold (x : xs) = foldl (++) citeBook (x) xs
hw1.hs:28:51:
Couldn't match expected type `(String, String, Integer)'
with actual type `[(String, String, Integer)]'
In the fourth argument of `foldl', namely `xs'
In the expression: foldl (++) citeBook (x) xs
In an equation for `bibliography_fold':
bibliography_fold (x : xs) = foldl (++) citeBook (x) xs
I appreciate any and all feedback. Thanks!
You gave foldl the (++) function which has type String -> String -> String. However the collection you're folding over, xs, has type [(String, String, Integer)], not type [String].
You could change bibliography_fold to
bibliography_fold :: [(String, String, Integer)] -> String
bibliography_fold [] = ""
bibliography_fold (x:xs) = foldl (++) (citeBook x) (map citeBook xs)
or just to
bibliography_fold :: [(String, String, Integer)] -> String
bibliography_fold xs = foldl (++) "" (map citeBook xs)
but I'm a relative noob at Haskell myself so take my coding style with a grain of salt.
Also, you need to write (citeBook x) and not citeBook(x), or the compiler will assume that citeBook and (x) are both arguments to foldl (correct me if I'm wrong). This helps explain why the error message you got is so strange-looking.
You've already gotten your answer, so I'll provide another way of using a fold to solve this problem:
bibliography_fold :: [(String, String, Integer)] -> String
bibliography_fold = foldr ((++) . citeBook) ""
There's no maps, no special cases, and it can be written in point free style. I'd encourage you to deconstruct this expression in GHCi and inspect each component using :info to explore how it actually works. Look at the types of foldr, ++, citeBook, (++) . citeBook, see if you can figure out why this works. You may want to look up the source code for foldr as well.

How can ++ and : be used interchangably in Haskell?

I read that the cons operator (:) and the ++ operator can be used interchangeably in Haskell strings. How can this be done? I'm not sure I completely understand what it means.
Edit: here's what I think it should be:
x:xs = [x]++xs
and
[]++list = list
(x:xs)++list = x:(xs++list)
(:) is a constructor for the list type. It's a special case of a rule that we can have infix constructors as long as they start with a colon:
Prelude> :info []
data [] a = [] | a : [a] -- Defined in `GHC.Types'
(++) is a function trivially implemented in terms of (:):
Prelude> :info (++)
(++) :: [a] -> [a] -> [a] -- Defined in `GHC.Base'
infixr 5 ++
As you can see the types are different:
Prelude> :t (:)
(:) :: a -> [a] -> [a]
Prelude> :t (++)
(++) :: [a] -> [a] -> [a]
Above I've used hoogle (and implementations linked in haddock docs) and GHCi with the :t and :info commands, and now you can too!
x:xs = [x]++xs
and
[]++list = list
(x:xs)++list = x:(xs++list)

type error when compiling

I don't understand why the following code won't compile:
append :: [a] -> [a] -> [a]
append xs ys = foldr (:) ys xs
traverse :: a -> [a] -> [[a]]
traverse x [] = [[x]]
traverse x (y:ys) = append [(x:y:ys)] (map (y:) (traverse x ys))
comb :: [a] -> [[a]]
comb [] = [[]]
comb (x:[]) = [[x]]
comb (x:y:[]) = [[x,y],[y,x]]
comb (x:xs) = map (traverse x) (comb xs)
It produces the following error:
pr27.hs:13:20:
Couldn't match type `a' with `[a]'
`a' is a rigid type variable bound by
the type signature for comb :: [a] -> [[a]] at pr27.hs:10:1
Expected type: [a] -> [a]
Actual type: [a] -> [[a]]
In the return type of a call of `traverse'
In the first argument of `map', namely `(traverse x)'
Failed, modules loaded: none
But when I load just traverse and use it in an expression similar to the above, I get the desired result. What's going on?
Main> map (traverse 3) [[1,2],[2,1]]
[[[3,1,2],[1,3,2],[1,2,3]],[[3,2,1],[2,3,1],[2,1,3]]]
The problem is that comb has to return a value of type [[a]]. traverse returns a value of type [[a]], so mapping it over another list produces [[[a]]], which has too many levels of nesting.
Let's look at map. It has a type map :: (x -> y) -> [x] -> [y]. traverse x has a type [a] -> [[a]]. Now we need to combine the two. To do this, we replace x with [a] and y with [[a]], getting ([a] -> [[a]]) -> [[a]] -> [[[a]]]. This clearly shows the result of mapping traverse has to have at least three levels of nesting.
If you look at your example, this is what you actually get. For comb, you only want one two levels deep.
The reason your example worked in GHCi is because an expression there can have any type. Your map (traverse 3) [[1,2], [2,1]] expression is perfectly legal; however, it has the type Num a => [[[a]]], which is a list of lists of lists of numbers. (Try it: :t map (traverse 3) [[1,2], [2,3]].) However, the type of comb is [a] -> [[a]]. This means the result has to be a list of lists, not a list of lists of lists. So the issue is that map (traverse 3) is incompatible with comb, not that it is illegal by itself.

Resources