Haskell [x] and x notation - as-pattern example - list

I am reading Learn you a Haskell for great good and on page 40 - as-patterns.
I have changed the example slightly to be:
firstLetter :: String -> String
firstLetter "" = "Empty string, oops"
firstLetter all#(x:xs) = "The first letter of " ++ all ++ " is " ++ [x] ++ " otherbit " ++ xs
Then can use like this:
*Main> firstLetter "Qwerty"
"The first letter of Qwerty is Q otherbit werty"
But I was confused about the difference between [x] and x and why I have to use [x] in the above example.
For example if I change to
firstLetter :: String -> String
firstLetter "" = "Empty string, oops"
firstLetter all#(x:xs) = "The first letter of " ++ all ++ " is " ++ x ++ " otherbit " ++ xs
I get error:
Couldn't match expected type `[Char]' with actual type `Char'
In the first argument of `(++)', namely `x'
In the second argument of `(++)', namely `x ++ " otherbit " ++ xs'
In the second argument of `(++)', namely
`" is " ++ x ++ " otherbit " ++ xs'
I can use xs to print "werty" but have to use [x] to print "Q". Why is that?
What does [x] mean?
In the (x:xs), : just delimits each element, so x is the first element. Why can I not print by using x?
Also xs is of what type? list of values? So does this mean x is an element and xs must be of type list?

++ is for concatenating lists:
(++) :: [a] -> [a] -> [a]
[x] is a list, x is not a list.
firstLetter (x:xs) is an example of pattern matching.
(:) :: a -> [a] -> [a]
This operator adds element before list.
Therefore type of x is element and type of xs is list of elements.
It is common in Haskell to add suffix 's' to lists' names.

String is defined as type String = [Char].
"Qwerty" is shorthand for ['Q', 'w', 'e', 'r', 't', 'y']; this in turn is shorthand for 'Q' : 'w' : 'e' : 'r' : 't' : 'y' : [].
So when you match x : xs against "Qwerty", you get x = 'Q' and xs = "werty".
x : xs
('Q') : ('w' : 'e' : 'r' : 't' : 'y' : [])
Note: x = 'Q', NOT x = "Q". 'Q' is a Char and "Q" is a String (i.e. a [Char]). But if you have 'Q' and you want "Q", you can write ['Q'] because "Q" is just shorthand for ['Q'].
So the short answer is that you have to do it to make the types match up. [x] is a list of length one, its single element being x.
In this:
firstLetter all#(x:xs)
= "The first letter of " ++ all ++ " is " ++ x ++ " otherbit " ++ xs
You're building up the string to be printed using ++. That has type
(++) :: [a] -> [a] -> [a]
i.e. ++ takes two lists, and gives you back a list.
But x is not a list. So you get a type error.
Instead you use
"The first letter of " ++ all ++ " is " ++ [x] ++ " otherbit " ++ xs
Now all the parameters to ++ are lists, and all the types match up.
But you could instead use
"The first letter of " ++ all ++ " is " ++ x : " otherbit " ++ xs
because the type of : is given by
(:) :: a -> [a] -> [a]

[x] means "a list containing only the element x".
As a String in Haskell is a list of characters, if x is the character 'Q' then [x] is the string "Q".
The ++ operator expects to receive two strings as arguments, so you can't give it only a character.
That's what the compiler is telling you: Couldn't match expected type `[Char]' with actual type `Char' means that the compiler was expecting an argument of type [Char] (which is a list of character, the same as String) but you are passing it a Char.

One could also note that in many programming languages the concatenation operator would either be overloaded so that there are several implementations of it depending on the types on both sides, or the arguments would be cast to strings. This is not the case in Haskell.

Related

How to show integers in a String through recursion

I am new to Haskell and I am running into some problems with my recursive solution. I would appreciate any and all help!
My first goal was to create a function that would check the order of the string of "A", "B" and "C" and to output an integer array mapping each of these to a number via recursion.
My second goal was to create a function that would take two integer arrays (one of them would be the integer array created from check_order (consisting of 1, 2, and 3) and one would be a random integer array of length 3) and return a string such that 1 would be replaced by the first element of the random integer array, 2 would be replaced by the second element of the random integer array, etc.
Here is my code:
-- Takes any String consisting of namely A, B, and C and returns an Integer
-- with 1, 2, and 3 that corresponds to each particular character.
check_order :: String -> [Int]
check_order "" = []
check_order (x:xs)
| x `elem` "A" = 1 : check_order xs
| x `elem` "B" = 2 : check_order xs
| otherwise = 3 : check_order xs
-- Takes the integer array generated from check_order and an arbitrary
-- integer array of length 3 and returns a String correlating the check_order
-- array and the arbitrary integer array
number_correction :: Integral n => [n] -> [n] -> String
number_correction [] _ = ""
number_correction (x:xs) num_array
| x == 1 = show (num_array !! 0) ++ " " ++ number_correction xs num_array
| x == 2 = show (num_array !! 1) ++ " " ++ number_correction xs num_array
| otherwise = show (num_array !! 2) ++ " " ++ number_correction xs num_array
main = do
let test = "ABCBABBAC";
let num_array = [6, 1, 8];
print(number_correction(check_order(test), num_array));
--This print statement should print out "6 1 8 1 6 1 1 6 8"
Here are the error logs:
[1 of 1] Compiling Main ( main.hs, main.o )
main.hs:12:16: error:• Could not deduce(Show n) arising from a use of ‘show’
from the context: Integral n
bound by the type signature for:number_correction :: Integral n => [n] -> [n] -> String
at main.hs:9:1-55
Possible fix:add (Show n) to the context of
the type signature for:
number_correction :: Integral n => [n] -> [n] -> String
• In the firstargument of ‘(++)’, namely ‘show (num_array !! 0)’
In the expression:
show (num_array !! 0) ++ " " ++ number_correction xs num_array
In anequation for ‘number_correction’:
number_correction (x : xs) num_array
| x == 1
=show (num_array !! 0) ++ " " ++ number_correction xs num_array
| x == 2
= show (num_array !! 1) ++ " " ++ number_correction xs num_array
| otherwise
= show (num_array !! 2) ++ " " ++ number_correction xs num_array
main.hs:19:31: error:
• Couldn't match expected type ‘[n0]’
with actual type ‘([Int], [Integer])’
• In the first argument of ‘number_correction’, namely
‘(check_order (test), num_array)’
In the first argument of ‘print’, namely
‘(number_correction (check_order (test), num_array))’
In a stmt of a'do' block:
print (number_correction (check_order (test), num_array))
(Int n) => [n] -> [n] -> String doesn't make any sense. Int is a concrete type, so what is Int n supposed to mean? It's like writing
foo = True 37
– you're trying to use something as a function that's not a function.
What you probably had in mind is the admittedly very similar-looking
number_correction :: Integral n => [n] -> [n] -> String
Unlike Int, Integral is a type class. Unlike in Java, types and classes in Haskell are completely different beasts. In particular, Integral is a function on the type level. You can see this in GHCi:
Prelude> :k Int
Int :: Type
Prelude> :k Integral
Integral :: Type -> Constraint
(traditionally it would show * and * -> Constraint, with * being the old symbol denoting the kind of types).
So, Integral n does make sense: you apply the Type -> Constraint to a variable n of kind Type, and thus get a constraint. Constraints are what can appear on the LHS of the => arrow.
You could also make a constraint with Int. That requires the (common enough) -XTypeFamilies extension, then you can write
number_correction :: (n ~ Int) => [n] -> [n] -> String
In this case you basically use the “class of all types which are equal to Int”, which is a bit silly. The Integral class contains the type Int, but also other types such as Integer and Int32.
If you really want to allow only Int, you should simply write
number_correction :: [Int] -> [Int] -> String

Haskell: Print Int from list one by one

I've got a short question regarding something I want to do in Haskell.
What I basically aim to achieve is to make a list of integers from 1 to a specific value y. Like [1..y], and the print this list with spaces between each number
Let's say i have [1..8]
My desired output is ("_" represents spaces):
_1_2_3_4_5_6_7_8
I have played a little bit with different things but without any luck
This is basically what Iv' got so far
printLst :: [Int] -> String
printLst (x:xs) = " " ++ putStr (show x) >> printLst xs
I've been searching around the web to find any solution to this, but I have not found anything that helps me do this.
Help is greatly appreciated
First, define a function which converts an Int to a String, then prepends a space to the result.
\x -> ' ' : show x
Now map this over your list:
> map (\x -> ' ' : show x) [1..8]
[" 1"," 2"," 3"," 4"," 5"," 6"," 7"," 8"]
Now we just need to concatenate all the strings into one:
> concat (map (\x -> ' ' : show x) [1..8])
" 1 2 3 4 5 6 7 8"
This can be simplied using the concatMap function:
> concatMap (\x -> ' ':show x) [1..8]
" 1 2 3 4 5 6 7 8"
which forms the basis for the Monad instance for lists:
> [1..8] >>= (\x -> ' ' : show x)
" 1 2 3 4 5 6 7 8"
or even more briefly, using function composition
> [1..8] >>= (' ' :) . show
" 1 2 3 4 5 6 7 8"
Once you have the final string, now you can worry about printing it.
> putStrLn $ [1..8] >>= (' ' :) . show
1 2 3 4 5 6 7 8
Well you are confusing things here, first of all:
putStr :: String -> IO ()
And you are returning a String, so no need to use that. Also, you have no patter for [] and singleton list, you can add them to get a better output like this:
printLst :: [Int] -> String
printLst [] = ""
printLst [x] = (show x)
printLst (x:xs) = (show x) ++ " " ++ printLst xs
If you want to use IO () function, use it in the main function:
main = do
putStrLn (printLst [1..8])
This is a list processing problem. For an empty list, we can return the empty string, for a non-empty list, we can first yield a space, followed by the show of that item, and then recurse on the rest of that list, like:
prefixSpace :: Show a => [a] -> String
prefixSpace [] = ""
prefixSpace (x:xs) = ' ' : show x ++ prefixSpace xs
Or as a "fold" pattern:
prefixSpace :: Show a => [a] -> String
prefixSpace = foldr (\x -> ((' ' : show x) ++)) ""
This will not print the string. For that you need putStrLn :: String -> IO (), but as the signature indicates, if you putStrLn some_string, you work with an IO ().

Haskell split string on last occurence

Is there any way I can split String in Haskell on the last occurrence of given character into 2 lists?
For example I want to split list "a b c d e" on space into ("a b c d", "e").
Thank you for answers.
I'm not sure why the solutions suggested are so complicated. Only one two traversals are needed:
splitLast :: Eq a => a -> [a] -> Either [a] ([a],[a])
splitLast c' = foldr go (Left [])
where
go c (Right (f,b)) = Right (c:f,b)
go c (Left s) | c' == c = Right ([],s)
| otherwise = Left (c:s)
Note this is total and clearly signifies its failure. When a split is not possible (because the character specified wasn't in the string) it returns a Left with the original list. Otherwise, it returns a Right with the two components.
ghci> splitLast ' ' "hello beautiful world"
Right ("hello beautiful","world")
ghci> splitLast ' ' "nospaceshere!"
Left "nospaceshere!"
Its not beautiful, but it works:
import Data.List
f :: Char -> String -> (String, String)
f char str = let n = findIndex (==char) (reverse str) in
case n of
Nothing -> (str, [])
Just n -> splitAt (length str - n -1) str
I mean f 'e' "a b c d e" = ("a b c d ", "e"), but I myself wouldn't crop that trailing space.
I would go with more pattern matching.
import Data.List
splitLast = contract . words
where contract [] = ("", "")
contract [x] = (x, "")
contract [x,y] = (x, y)
contract (x:y:rest) = contract $ intercalate " " [x,y] : rest
For long lists, we just join the first two strings with a space and try the shorter list again. Once the length is reduced to 2, we just return the pair of strings.
(x, "") seemed like a reasonable choice for strings with no whitespace, but I suppose you could return ("", x) instead.
It's not clear that ("", "") is the best choice for empty strings, but it seems like a reasonable alternative to raising an error or changing the return type to something like Maybe (String, String).
I can propose the following solution:
splitLast list elem = (reverse $ snd reversedSplit, reverse $ fst reversedSplit)
where
reversedSplit = span (/= elem) $ reverse list
probably not the fastest one (two needless reverses) but I like it's simplicity.
If you insist on removing the space we're splitting on, you can go for:
import qualified Data.List as List
splitLast list elem = splitAt (last $ List.elemIndices elem list) list
however, this version assumes that there will be at least one element matching the pattern. If you don't like this assumption, the code gets slightly longer (but no double-reversals here):
import qualified Data.List as List
splitLast list elem = splitAt index list where
index = if null indices then 0 else last indices
indices = List.elemIndices elem list
Of course, choice of splitting at the beginning is arbitrary and probably splitting at the end would be more intuitive for you - then you can simply replace 0 with length list
My idea is to split at every occurrence and then separate the initial parts from the last part.
Pointed:
import Control.Arrow -- (&&&)
import Data.List -- intercalate
import Data.List.Split -- splitOn
breakOnLast :: Eq a => a -> [a] -> ([a], [a])
breakOnLast x = (intercalate x . init &&& last) . splitOn x
Point-free:
liftA2 (.) ((&&& last) . (. init) . intercalate) splitOn
(.) <$> ((&&&) <$> ((.) <$> pure init <*> intercalate) <*> pure last) <*> splitOn

Type error in generator , haskell list using tuple

Currently working with Haskell on a function that takes a String in parameters and return a list of (Char, Int) The function occur works with multiple type and is used in the function called word.
occur::Eq a=>a->[a]->Int
occur n [] = 0
occur n (x:xs) = if n == x
then 1 + occur n xs
else occur n xs
word::String->[(String,Int)]
word xs = [(x,y) | x<-head xs, y<-(occur x xs)]
Get me this error
ERROR "file.hs":31 - Type error in generator
*** Term : head xs
*** Type : Char
*** Does not match : [a]
What am I doing wrong ? How can I make this code run properly , type-wise ?
The problem is you say that xs has type String, so head xs has type Char, and then you try to iterate over a single Char, which can't be done. The a <- b syntax only works when b is a list. You have the same problem in that y <- occur x xs is trying to iterate over a single Int, not a list of Int. You also had a problem in your type signature, the first type in the tuple should be Char, not String. You can fix it with:
word :: String -> [(Char, Int)]
word xs = [(x, occur x xs) | x <- xs]
Here we loop over the entire string xs, and for each character x in xs we compute occur x xs.
I would actually recommend using a slightly stronger constraint than just Eq. If you generalize word (that I've renamed to occurrences) and constrain it with Ord, you can use group and sort, which allow you to keep from iterating over the list repeatedly for each character and avoid the O(n^2) complexity. You can also simplify the definition pretty significantly:
import Control.Arrow
import Data.List
occurrences :: Ord a => [a] -> [(a, Int)]
occurrences = map (head &&& length) . group . sort
What this does is first sort your list, then group by identical elements. So "Hello, world" turns into
> sort "Hello, world"
" ,Hdellloorw"
> group $ sort "Hello, world"
[" ", ",", "H", "d", "e", "lll", "oo", "r", "w"]
Then we use the arrow operator &&& which takes two functions, applies a single input to both, then return the results as a tuple. So head &&& length is the same as saying
\x -> (head x, length x)
and we map this over our sorted, grouped list:
> map (head &&& length) $ group $ sort "Hello, world"
[(' ',1),(',',1),('H',1),('d',1),('e',1),('l',3),('o',2),('r',1),('w',1)]
This eliminates repeats, you aren't having to scan the list over and over counting the number of elements, and it can be defined in a single line in the pointfree style, which is nice. However, it does not preserve order. If you need to preserve order, I would then use sortBy and the handy function comparing from Data.Ord (but we lose a nice point free form):
import Control.Arrow
import Data.List
import Data.Ord (comparing)
occurrences :: Ord a => [a] -> [(a, Int)]
occurrences = map (head &&& length) . group . sort
occurrences' :: Ord a => [a] -> [(a, Int)]
occurrences' xs = sortBy (comparing ((`elemIndex` xs) . fst)) $ occurrences xs
You can almost read this as plain English. This sorts by comparing the index in xs of the first element of the tuples in occurrences xs. Even though elemIndex returns a value of type Maybe Int, we can still compare those directly (Nothing is "less than" any Just value). It simply looks up the first index of each letter in the original string and sorts by that index. That way
> occurrences' "Hello, world"
returns
[('H',1),('e',1),('l',3),('o',2),(',',1),(' ',1),('w',1),('r',1),('d',1)]
with all the letters in the original order, up to repetition.

Applying putStr to each item of a list

I'm trying to print out a list of integers in this format
[1]
[2]
[3]
So my function takes a list of integers and returns an IO () like so:
import System.IO
printVector :: [Integer] -> IO ()
printVector (x:xs) = putStr("[" ++ show(x) ++ "]" ++ "\n") : printVector xs
But ghc gives me the error:
lin-test.hs:5:22:
Couldn't match expected type `IO ()' with actual type `[a0]'
In the expression:
putStr ("[" ++ show (x) ++ "]" ++ "") : printVector xs
In an equation for `printVector':
printVector (x : xs)
= putStr ("[" ++ show (x) ++ "]" ++ "") : printVector xs
Failed, modules loaded: none.
Now it is my understanding that the function would run through the list, taking the first item 'x' first, and then with : printVector xs it would recursively call the rest of the list applying the same putStr function to each item in the list.
But I think my problem is where I do : printVector xs?
Can anyone point out what i'm doing wrong here?
You need to map over the list but, as these are IO actions, you also need to execute them (as map will return a list of IO action without executing them, see following example).
sequence $ map (putStrLn . show) [1,2,3,4]
There is a function doing that already, it's mapM. So the example can be simplify as :
mapM (putStrLn . show) [1,2,3,4]
Another thing you can do is using mapM_ which uses sequence_ instead and will ignore the result of executing the IO action to each of the elements. Therefore, the return type will be IO () instead of IO [()] (previous example).
mapM_ (putStrLn . show) [1,2,3,4]