I learned before that when using Haskell's read function when reading numbers from Strings you need to specify the type of output, as:
read "2" :: Int
unless you do something like:
read "2" + 2
Haskell then knows you're trying to do addition, hence it must be a number.
However, one particular function caught my attention, because by looking at function i thought it would not compile, but it does, i don't know why.
This reverse polish notation calculator that implements addition, subtraction and multiplication:
solveRPN :: (Num a, Read a) => String -> a
solveRPN xs = head . foldl foldingFunction [] . words $ xs
where foldingFunction (x:y:ys) "*" = (x * y):ys
foldingFunction (x:y:ys) "+" = (x + y):ys
foldingFunction (x:y:ys) "-" = (y - x):ys
foldingFunction xs numberString = read numberString:xs
if you give it a string like "2 5 +" it will return 7.
last line of this code is what i can't understand. When you give this function "2 5 +" the first element in xs list will be "2" and accumulator at time is [], hence it will slip through first 3 patterns and last one will do its job, hence:
foldingFunction [] "2" = read "2":[]
So, my question is: How come read "2":[] dosen't crash? If I tried to execute this bit in console it would give parse error becasue read wouldn't know what that string should be, right? how come it's not (read "2" :: Int):[] or something?
So what you have to understand is that Haskell assigns all the types of functions at compile time, not run time. Also, that functions have only one type for all their patterns.
What this means is that it will decide that the type of the function is overall and use that decision in every case. Also, Haskell does rather heavy type inference (unlike most other languages), and so will sometimes determine the type of a function based on the type of something that might seem a bit far away from the original function call.
Let's look at your example:
solveRPN :: (Num a, Read a) => String -> a
solveRPN xs = head . foldl foldingFunction [] . words $ xs
where foldingFunction (x:y:ys) "*" = (x * y):ys
foldingFunction (x:y:ys) "+" = (x + y):ys
foldingFunction (x:y:ys) "-" = (y - x):ys
foldingFunction xs numberString = read numberString:xs
First off, the type of solveRPN is declared as String -> a.
Now, looking at the definition of solveRPN, we have the first line saying:
solveRPN xs = head . foldl foldingFunction [] . words $ xs
Now, the types of the names used there are:
xs :: String (from the type of solveRPN)
head :: [b] -> b (I'm using different variable names for each different type)
foldl :: (c -> d -> c) -> c -> [d] -> c
words :: String -> [String]
So the type of solveRPN means that we must have that type b is the same as type a, and since head is applied to the output of foldl, we must have that type c is the same as type [a]. Now since the third argument to foldl is of type [String], we know that type d is String, and now we have enough to determine the type of foldingFunction:
foldingFunction :: [a] -> String -> [a]
Let's work backwards.
head . foldl foldingFunction [] . words :: Num a => String -> a
foldl foldingFunction [] .words :: Num a => String -> [a]
foldl foldingFunction [] :: Num a => [String] -> [a]:
Since foldl ::Foldable t => (b -> a -> b) -> b -> t a -> b, we can see that t a ~ [String], so we can also see that foldingFunction :: Num a => [a] -> String -> [a].
Thus, read "2" : [] :: Num a => [a], the same type as foldingFunction [] "2".
In other words, solveRPN provides the necessary context to infer what read should return.
If I tried to execute this bit in console it would give parse error becasue read wouldn't know what that string should be, right?
(Thanks to haskell-cafe mailing list for help on this.)
You are getting a parse error at the GHCI REPL because in the absence of a type context, GHCi evaluates the expression as if it were the type () (the empty tuple type.)
For instance, this does not give an error:
GHCi, version 7.10.2: http://www.haskell.org/ghc/ :? for help
Prelude> read "()"
()
This is a consequence of the -XExtendedDefaultRules option which is implicitly in effect when using GHCi. See Type defaulting in GHCi in the GHC User Guide for more details on why GHCi has these extended defaulting rules.
To see how this option affects evaluation, you can perform the same experiment with the option disabled:
GHCi, version 7.10.2: http://www.haskell.org/ghc/ :? for help
Prelude> :set -XNoExtendedDefaultRules
Prelude> read "2"
<interactive>:3:1:
No instance for (Read a0) arising from a use of ‘it’
The type variable ‘a0’ is ambiguous
...
Now we get a No instance for ... error message. This is the message that tells you that GHC does not know which type to return.
The context (Num a, Read a) => on your solveRPN function are enough for read to figure it out. Try this in ghci:
let n = read "2" :: (Num a, Read a) => a
:t n
Related
This is the code I have for trying to implement a reverse Polish notation evaluator:
step :: [Int] -> String -> [Int]
step (x:y:ys) "*" = (x * y):ys
step (x:y:ys) "+" = (x + y):ys
step (x:y:ys) "-" = (y - x):ys
step xs numberString = read numberString:xs
rpn :: [String] -> Int
rpn = head . foldl step[] . words
and this is error I keep getting when I do :load Rpn.hs :
Rpn.hs:9:7: error:
• Couldn't match type ‘[Char]’ with ‘Char’
Expected type: [String] -> Int
Actual type: String -> Int
• In the expression: head . foldl step [] . words
In an equation for ‘rpn’: rpn = head . foldl step [] . words
|
9 | rpn = head . foldl step[] . words
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
Failed, no modules loaded.
I think the problem is with the foldl function but I am not quite sure about how to resolve it. Please, kindly guide me on what to do.
When you have a function like f = foo . bar . baz, it can be written in a pointful way as f x = foo (bar (baz x)). In your case, you'd have rpn x = head (foldl step [] (words x)). The problem is now apparent: you're calling words x, and words has type String -> [String], so x should have type String. However, rpn :: [String] -> Int, and x is the first argument to rpn, so x should have type [String]. Since x can only have one type, it can't be both String and [String], so the compiler is making you fix this discrepancy. There are two different ways to fix this, depending on what you want rpn to do:
Change the type of rpn to String -> Int, and leave its definition alone
Remove . words from the definition of rpn, and leave its type alone
I am quite new in Haskell world. I was reading the online http://learnyouahaskell.com but I could not understand a small detail about pattern-matching section. I have written those functions
myFunc' (firstLetter:_) = firstLetter -- returns firstLetter of given string
However if I do something like that
myFunc' (firstLetter:_) = "Hello" ++firstLetter
Gives me following error when I call this function
Couldn't match type ‘Char’ with ‘[Char]’
Expected type: [[Char]]
Actual type: [Char]
But if I modify the function like this
myFunc' (firstLetter:_) = "Hello" ++ [firstLetter]
That works fine when I call this function. I was wondering why do I need brackets in other cases. What is actually firstLetter.
First, if you check the type of (++) in ghci, you get:
Prelude> :t (++)
(++) :: [a] -> [a] -> [a]
That means it takes two lists of a's as arguments.
Likewise let's see what (:) does:
Prelude> :t (:)
(:) :: a -> [a] -> [a]
So the first argument of (:) need not be a list at all. If we fix a == Char we in fact get (:) :: Char -> String -> String.
We can define a function headStr (recall String == [Char]):
headStr :: String -> Char
headStr (x:_) = x
headStr _ = error "Empty string!"
Note that due to the type of (:) in this case x :: Char.
On the other hand if we try to define:
hello :: String -> String
hello (x:_) = "Hello" ++ x
hello _ = error "Empty string!"
it will not type check because in the non error case we get [Char] ++ Char. As ghci helpfully told us, the second argument to (++) must always be a list and in this case since the first argument is [Char] it must also be [Char].
As you noticed yourself, this can be fixed by wrapping x in a list:
hello' :: String -> String
hello' (x:_) = "Hello" ++ [x]
hello' _ = error "Empty string!"
and this works as expected.
"Hello ++ firstLetter
The types there are:
[Char] ++ Char
As you can see, that isn't possible. You can't add a Char to a [Char], they are different types!
But by doing
[firstLetter]
you are creating a list with 1 element, firstLetter. Because firstLetter is a Char, you'll get a list of Chars, i.e. the list is of type [Char].
Adding 2 lists of the same type is allowed, and that's why it works in the second case.
New to Haskell and the language has been fun so far. I am hoping for a good hint rather than an answer as I am enjoying the mind-altering that is Haskell.
Question: I have a list of strings and I would like to transpose them.
let x = ["hello", "world"]
would become
["hw", "eo", "lr", "ll", "od"]
What I have so far is this:
transposeString :: [a] -> [a]
transposeString ([]:_) = []
transposeString x = (map head x) : transposeString (map tail x)
I definitely know there is something wrong with the type signature. My rational is that
Let y = ["wow", "top"]
map head y
returns "wt" so recursing this on the rest of the list would work?
Thank you in advance for any hints.
Mind that you do not have to provide a type signature: the Haskell compiler can derive one. If you put your implementation in a file:
transposeString ([]:_) = []
transposeString x = (map head x) : transposeString (map tail x)
and query the type with :t in ghci, it returns:
*Main> :t transposeString
transposeString :: [[b]] -> [[b]]
This makes perfect sense:
you transpose a matrix, which is a list of lists. [[b]] is a list of lists of b elements; and
you can derive it from the implementation yourself: map head x means that elements of x must be a list ([b]) since we perform a mapping, we have to nest the list one additional level so [[b]]. The same for tail.
As far as I know, your implementation is correctly. You can specialize it by saying that [b] ~ String, thus adding a type signature for Strings:
transposeString :: [String] -> [String]
transposeString ([]:_) = []
transposeString x = (map head x) : transposeString (map tail x)
which again makes sense because String ~ [Char] thus b ~ Char. But there is not much point in specializing a functions type: you better always use the most generic type signature. In this case [[b]] -> [[b]].
One point of note. Your type signature for tranposeString allows to accept a flat list as an argument. Tranposing a [String] works because [String]s are really just [[Char]]s, but what happens when you try to call transposeString on an [Int]? After all, the type signature allows for it.
On a side note, I ask you, given your current function, what would happen if your called transposeString []?
Do remember String is [Char]
"abc" == 'a' : 'b' : 'c' : []
From here you can use traversable nature of lists:
transpose :: [[a]] -> [[a]]
transpose mat = getZipList $ sequenceA $ map ZipList mat
test' = transpose [[1,2,3],[4,5,6],[7,8,9]] -- == [[1,4,7],[2,5,8],[3,6,9]]
test'' = transpose ["abc", "deg", "klm"] -- == ["adk","bel","cgm"]
You can also check default implementation of transponse in haskell doc
https://hackage.haskell.org/package/base-4.12.0.0/docs/src/Data.OldList.html#transpose
transpose :: [[a]] -> [[a]]
transpose [] = []
transpose ([] : xss) = transpose xss
transpose ((x:xs) : xss) = (x : [h | (h:_) <- xss]) : transpose (xs : [ t | (_:t) <- xss])
I'm currently getting started with Haskell (reading Learn Yourself a Haskell),
and came across lines akin to the following:
map (++"!") ["a", "b"] -- ["a!", "b!"]
map ("!"++) ["a", "b"] -- ["!a", "!b"]
Why is this possible, or how does it work? I can't manage to do the same with other non-commutative operations, like division:
map (3/) [1..3] -- [3.0,1.5,1.0]
map ((/)3) [1..3] -- [3.0,1.5,1.0]
map (3(/)) [1..3] -- error
I feel like I'm missing something here, but the implementation of map doesn't give me any hints.
This code is not valid:
map (3(/)) [1..3]
(/) is prefix function but you use it as infix. Compiler see it as you try to function 3 (a function without arguments), add (/) as an argument.
/ is infix function. So, you can do next:
map ( / 3) [1..3] -- [0.3333333333333333,0.6666666666666666,1.0]
map (3 / ) [1..3] -- [3.0,1.5,1.0]
This is not at all related to map; map’s argument can just be any function.
To understand the functions that you have passed, look at this GHCi session:
Prelude> :t (++"!")
(++"!") :: [Char] -> [Char]
Prelude> (++"!") "Hello"
"Hello!"
Prelude> ("!"++) "Hello"
"!Hello"
Prelude> :t ("!"++)
("!"++) :: [Char] -> [Char]
What is happening here is the syntactic idea of operation sections (Haskell report, Sec. 3.4), which can be read as
(x •) == (\y. x • y)
(• x) == (\y. y • x)
where • can be any operation like ++, * or even funny self-defined operators like ^_^.
If a function is declared in brackets: (++) :: [a] -> [a] -> [a], it can be used with and without them. If used without brackets, they must appear between the arguments: "!" ++ "?", but with the brackets they are just like normal functions: (++) "!" "?".
Haskell permits "partial application" of functions, so ("!"++) is the same as (++) "!" or \x -> (++) "!" x, and (++"?") is the same as \x -> (++) x "?". ("Partial application" is in quotes, because the functions in Haskell always have just one argument, so application is no longer "partial"; in other languages (++) would be viewed as a function of two arguments, so when only one argument is applied, the function is deemed partially applied - in this sense it may be useful to view ("!"++) as a partially applied (++))
Your second example is a valid way of using (/), but if you use (/), it really is not a infix function anymore, so you get a error attempting to specify the first argument to (/) before the function name: 3(/). It still works if you remove the brackets: (3 /) is the same as ((/) 3) or (\x -> (/) 3 x) or (\x -> 3 / x)
I recently started learning Haskell and I'm trying to rewrite something I did for an interview in python in Haskell. I'm trying to convert a string from camel case to underscore separated ("myVariableName" -> "my_variable_name"), and also throw an error if the first character is upper case.
Here's what I have:
import qualified Data.Char as Char
translate_java :: String -> String
translate_java xs = translate_helper $ enumerate xs
where
translate_helper [] = []
translate_helper ((a, num):xs)
| num == 1 and Char.isUpper a = error "cannot start with upper"
| Char.isUpper a = '_' : Char.toLower a : translate_helper xs
| otherwise = a : translate_helper xs
enumerate :: (Num b, Enum b) => [a] -> [(a,b)]
enumerate xs = zip xs [1..]
I realize It's pretty likely I'm going about this in a weird way, and I'd love advice about better ways to implement this, but I'd like to get this to compile as well. Here's the error I'm getting now:
Prelude> :r
[1 of 1] Compiling Main ( translate.hs, interpreted )
translate.hs:4:20:
No instance for (Num
(([Bool] -> Bool) -> (Char -> Bool) -> Char -> t))
arising from a use of `translate_helper' at translate.hs:4:20-35
Possible fix:
add an instance declaration for
(Num (([Bool] -> Bool) -> (Char -> Bool) -> Char -> t))
In the first argument of `($)', namely `translate_helper'
In the expression: translate_helper $ enumerate xs
In the definition of `translate_java':
translate_java xs
= translate_helper $ enumerate xs
where
translate_helper [] = []
translate_helper ((a, num) : xs)
| num == 1 and Char.isUpper a
= error "cannot start with upper
"
| Char.isUpper a
= '_' : Char.toLower a : transla
te_helper xs
| otherwise = a : translate_help
er xs
Failed, modules loaded: none.
Any explanation of what's going on here would be great. I really don't understand where "(Num (([Bool] -> Bool) -> (Char -> Bool) -> Char -> t))" is coming from. I'd think the type declaration for translate_helper would be something like [(a,b)] -> [a]?
You have to replace and by &&. The first one is a function (prefix) that receives a list of boolean values and calculates an and of them all. The second one is a true logical and. The error message is a little bit confusing though. Whenever I get such a strange error message, I usually start to annotate my code with type signatures. Then the compiler is able to give you a more detailed description of what went wrong.
Others have mentioned that you should use (&&) instead of and, so I'll answer your other question: no, I don't think you're going about this in a weird way.
But... I do think it can be even more elegant!
translate_java (x:xs) | isUpper x = error "cannot start with an upper"
translate_java xs = concatMap translate xs where
translate x = ['_' | isUpper x] ++ [toLower x]
There's a few interesting things going on here:
The special case is checked straight away. Don't wait until you're recursing to do this!
The concatMap function is really handy in a lot of cases. It's just a map followed by a concat. If I were writing this myself, I'd probably use xs >>= translate instead.
That ['_' | isUpper x] is a list comprehension; this is a cute idiom for making a list with either 0 or 1 elements in it, depending on whether a predicate holds.
Other than that, the code should be fairly self-explanatory.
The problem is this:
| num == 1 and Char.isUpper a = ...
and is not an infix operator; rather it is a function:
and :: [Bool] -> Bool
So it is interpreting 1 and Char.isUpper a as applying three arguments to the "function" 1. Use && instead.
The error message comes from the way numerals are interpreted. A numeral, say, 1 is actually polymorphic; the specific type it gets depends on the type that is needed. That's why you can say x+1 and it will work whether x is an integer or a double or whatever. So the compiler inferred that the type of 1 needs to be a three-argument function, and then tried to find a numeric type matching that so it could convert 1 into that type (and, naturally, failed).
Here's my solution. It's not as masterful as the answer Daniel Wagner gave using concatMap and the list comprehension, but it's perhaps easier to understand for the beginner.
conv :: String -> String
conv [] = []
conv s#(x:xs) = if Char.isUpper x
then error "First character cannot be uppercase"
else change s
change :: String -> String
change [] = []
change (x:xs) = if Char.isUpper x
then '_' : Char.toLower x : change xs
else x : change xs
The function conv really just checks your criterion that the first character must not be uppercase, and if it isn't it hands over the string to the function change, which does the work. It goes through all the characters one by one, building a list, and if the character is uppercase, it adds an underscore followed by the lowercase version of the character, otherwise if the character is already lowercase it just adds it as it is.