Errorhandling and monads? - haskell

I'm trying to understand how to apply the Maybe-idiom from Haskel.. I'm reading http://en.wikibooks.org/wiki/Haskell/Understanding_monads/Maybe which shows that a lookup in a dictionary may return a Maybe and that this value propates through the >>= operator.
The example from the URL:
If we then wanted to use the result from the governmental database lookup in a third lookup (say we want to look up their registration number to see if they owe any car tax), then we could extend our getRegistrationNumber function:
getTaxOwed :: String -- their name
-> Maybe Double -- the amount of tax they owe
getTaxOwed name =
lookup name phonebook >>=
(\number -> lookup number governmentalDatabase) >>=
(\registration -> lookup registration taxDatabase)
Or, using the do-block style:
getTaxOwed name = do
number <- lookup name phonebook
registration <- lookup number governmentalDatabase
lookup registration taxDatabase
Question:
How do I deal with error handling? I think most code will benefit from telling where things went wrong. Rather than just reporting "couldn't find John Doe in either phonebook or governmentalDatabase" it should report which ressource had problems.

You could use the monad instance for Either String, which is essentially defined as
instance Monad (Either String) where
fail msg = Left msg
return x = Right x
Left msg >>= k = Left msg
Right x >>= k = k x
(The actual definition is a bit more involved.)
If we then define dictionaries as pairs consisting of a label and a lookup table
type Dict a b = (String, [(a, b)])
phonebook' :: Dict String Int
phonebook' = ("phone book", phonebook)
governmentalDatabase' :: Dict Int Int
governmentalDatabase' = ("governmental database", governmentalDatabase)
taxDatabase' :: Dict Int Double
taxDatabase' = ("tax database", taxDatabase)
where phonebook, governmentalDatabase, and taxDatabase are as you had them defined before, we can use an alternative monadic lookup function that returns its result in the Either String-monad:
lookup' :: (Eq a, Show a) => a -> Dict a b -> Either String b
lookup' key (descr, table) = case lookup key table of
Nothing -> Left ("couldn't find " ++ show key ++ " in " ++ descr)
Just val -> Right val
Illustrating the power of monads, the only thing that now needs to change in your client function is the type signature:
getTaxOwed :: String -- their name
-> Either String Double -- either an error message
-- or the amount of tax they owe
getTaxOwed name = do
number <- lookup' name phonebook'
registration <- lookup' number governmentalDatabase'
lookup' registration taxDatabase'
Running this function on an unknown name gives:
> getTaxOwed "Joe"
Left "couldn't find \"Joe\" in phone book"

The Maybe data type has only the value "Nothing" for signaling an error. If you want to return a specific error message i suggest the data type "Either", which can return either a "Left a" or "Right a" value. Read more about how to use that at http://learnyouahaskell.com/for-a-few-monads-more#error

Related

How does this trick type-check?

Reading this blog post – https://www.haskellforall.com/2021/05/the-trick-to-avoid-deeply-nested-error.html – I realised I don't understand why the 'trick' actually works in this situation:
{-# LANGUAGE NamedFieldPuns #-}
import Text.Read (readMaybe)
data Person = Person { age :: Int, alive :: Bool } deriving (Show)
example :: String -> String -> Either String Person
example ageString aliveString = do
age <- case readMaybe ageString of
Nothing -> Left "Invalid age string"
Just age -> pure age
if age < 0
then Left "Negative age"
else pure ()
alive <- case readMaybe aliveString of
Nothing -> Left "Invalid alive string"
Just alive -> pure alive
pure Person{ age, alive }
Specifically I'm struggling to understand why this bit
if age < 0
then Left "Negative age"
else pure ()
type checks.
Left "Negative age" has a type of Either String b
while
pure () is of type Either a ()
Why does this work the way it does?
EDIT: I simplified and re-wrote the code into bind operations instead of do block, and then saw Will's edit to his already excellent answer:
{-# LANGUAGE NamedFieldPuns #-}
import Text.Read (readMaybe)
newtype Person = Person { age :: Int} deriving (Show)
example :: String -> Either String Person
example ageString =
getAge ageString
>>= (\age -> checkAge age
>>= (\()-> createPerson age))
getAge :: Read b => String -> Either [Char] b
getAge ageString = case readMaybe ageString of
Nothing -> Left "Invalid age string"
Just age -> pure age
checkAge :: (Ord a, Num a) => a -> Either [Char] ()
checkAge a = if a < 0
then Left "Negative age"
else pure ()
createPerson :: Applicative f => Int -> f Person
createPerson a = pure Person { age = a }
I think this makes the 'trick' of passing the () through binds much more visible - the values are taken from an outer scope, while Left indeed short-circuits the processing.
It typechecks because Either String b and Either a () unify successfully, with String ~ a and b ~ ():
Either String b
Either a ()
------------------
Either String () a ~ String, b ~ ()
It appears in the do block of type Either String Person, so it's OK, since it's the same monad, Either, with the same "error signal" type, String.
It appears in the middle of the do block, and there's no value "extraction". So it serves as a guard.
It goes like this: if it was Right y, then the do block's translation is
Right y >>= (\ _ -> .....)
and the computation continues inside ..... with the y value ignored. But if it was Left x, then
Left x >>= _ = Left x
according to the definition of >>= for Either. Crucially, the Left x on the right is not the same value as Left x on the left. The one on the left has type Either String (); the one on the right has type Either String Person indeed, as demanded by the return type of the do block overall.
The two Left x are two different values, each with its own specific type. The x :: String is the same, of course.

Haskell read function overloading with two inputs

In my haskell program I have a list that represents a database in [(key, value)] format. For example this is a valid database: [("key1", "value1"), ("key2", "value2"), ("key3", "value3")]. The key and value data will always have String type.
My question is: is it possible to code the reading operation by overloading the read function and using that in this way: read dbList "key1"? If yes how can I solve this problem? The output needs to be ("not found","value data for key not exists") or ("found", "value1").
I`ve looked up how can I solve this but all that I found is how to use read function to one input parameter and how to define a new type in order to create an instance of the read for that particular type if it is needed. But I am still curious if I can overload somehow the read function with two input paramaters.
The function you want is lookup, which is part of the Prelude.
> :t lookup
lookup :: Eq a => a -> [(a, b)] -> Maybe b
> let dbList = [("key1", "value1")]
> lookup "key1" dbList
Just "value1"
> lookup "key2" dbList
Nothing
If you really need the output in the tuple form you show, you can pattern-match on the result.
case lookup dbList someKey of
Just x -> ("found", x)
Nothing -> ("not found", "data for " ++ key ++ " does not exist")
For completeness, I will present a way to do it using read. However, this is very unusual and I would consider it a bad idea, since you can just use lookup.
{-# LANGUAGE FlexibleContexts, FlexibleInstances #-}
type DB = [(String, String)]
instance Read (DB -> (String, String)) where
readsPrec _ = \key -> let
f db = case lookup key db of
Just x -> ("found", x)
Nothing -> ("not found", "data for " ++ key ++ " does not exist")
in [(f, "")]
Here we define an instance of Read for the type DB -> (String, String). Recall that the read function has type Read a => String -> a, so this instance gives us an overload of read of type String -> DB -> (String, String).
Our instance defines the readsPrec function, which has type Read a => Int => ReadS a, where ReadS a is an alias for String -> [(a, String)]. So our readsPrec implementation must have type Int -> String -> [(DB -> (String, String), String)]. We don’t care about the “precedence” argument, so we ignore it with _. And we only care about returning a single result in this instance, so we simply return [(f, "")] where f is a function of type DB -> (String, String) that performs the lookup of the key in its argument db.
Now we can use this instance like so:
> read "foo" [("foo", "bar")] :: (String, String)
("found","bar")
> read "baz" [("foo", "bar")] :: (String, String)
("not found","data for baz does not exist")
Again I would stress that this is an unusual instance that may cause confusion in real code—you should just use lookup directly.

Haskell lookup table to return functions

Trying to extend "The Maybe monad" example on this page. Their lookup table phonebook:
phonebook :: [(String, String)]
phonebook = [ ("Bob", "01788 665242"),
("Fred", "01624 556442"),
("Alice", "01889 985333"),
("Jane", "01732 187565") ]
and there chained monad examples:
getRegistrationNumber :: String -- their name
-> Maybe String -- their registration number
getRegistrationNumber name =
lookup name phonebook >>=
(\number -> lookup number governmentalDatabase)
What happens if we want to return a function (that then returns a specific type) instead? So extending from their example, instead of looking up a registration number, we want to lookup lookup either their age, their ZIP, or the years that there property taxes were paid. Given these examples, an INT seems appropriate for the first two, and a List of Ints for the last. FIRST Question: since the lookup table has a type, must all of the return types of the functions be of the same type? I am assuming yes, but am unsure, hence the next question.
lets say that we write these 'finding' functions that return the same type [Int]. Maybe something like these:
getAge :: String -> Maybe [Int]
getAge phoneNumberString =
lookup name phonebook >>==
(\phoneNumberString -> lookup phoneNumberString governmentalAgeDatabase)
getZip :: String -> Maybe [Int]
getZip phoneNumberString =
lookup name phonebook >>==
(\phoneNumberString -> lookup phoneNumberString governmentalZipCodeDatabase)
getTaxPaidYears :: String -> Maybe [Int]
getTaxPaidYears phoneNumberString =
lookup name phonebook >>==
(\phoneNumberString -> lookup phoneNumberString governmentalTaxYearDatabase)
Now, assuming each of the the *Databases return an [Int] type, Second Question How do we write ONE function like lookupPersonsInformation that would return the appropriate information from what's typed in the input String, and given a lookup that returns the appropriate function, returns the information requested? Here is what I am trying to make work:
lookupAppropriateFunction :: [(String, String -> [Int])] --Here I want the second part
-- of the tuple to be the functions
lookupAppropriateFunction = [ ("age", getAge),
("zip", getZip),
("taxes", getTaxPaidYears) ]
lookupPersonsInformation :: String -> Maybe [Int]
lookupPersonsInformation nameAndInfo =
lookup ( words nameAndInfo!!0 ) >>=
( \phoneNumberString -> lookup ( words nameAndInfo!!1 ) lookupAppropriateFunction )
-- >> lookupPersonsInformation "Bob age"
[53] --Bob's age
-- >> lookupPersonsInformation "Fred zip"
[28202] --Fred's age
-- >> lookupPersonsInformation "Alice taxes"
[2010,2011,2013] --Alice's paid taxes years, It looks like she skipped 2012 :)
It is apparent that the errors propagate through to the end as Nothing, but I am unsure how to take the next step in applying this to a higher order function. Is it more in the parsing using words or in the structure of the lookup table that I want to return a function`
I ended up with going with something like the following:
-------------------------------------------------------------------------
intPusher :: String -> Stack -> Maybe Stack
-- ^ Takes a word, and tries to turn it into an Int, and push it onto the stack
intPusher word = case (reads word) of
[] -> \stak -> Nothing
[(x,"")] -> \stak -> Just (x:stak)
[(x,y)] -> \stak -> Nothing
-------------------------------------------------------------------------
dicLookup :: String -> Stack -> Maybe Stack
-- ^ Takes a word, and looks it up in the dictionary
dicLookup word = case (lookup word wordsTable) of
Nothing -> intPusher word
Just f -> f
-------------------------------------------------------------------------
wordsTable :: [(String, Stack -> Maybe Stack)]
-- ^ Checks the string against the commands
wordsTable = [ ("+", addIt)
,("-", subIt)
,("*", multIt)
,("/", divIt)
,("/MOD", modQuotIt)
,("MOD", modIt)
....
,("2DROP", drop2It) ]
-------------------------------------------------------------------------
interpretProgram :: String -> Maybe Stack
interpretProgram str = foldl (>>=) (Just[]) (map dicLookup (words str))
and for each tuple value in the dictionary, I provided the function declaration:
-------------------------------------------------------------------------
addIt :: Stack -> Maybe Stack
-- ^ Adds the first two elements of the stack
addIt stak = case stak of
x:y:xs -> Just (x + y:xs)
x:xs -> Nothing
_ -> Nothing
-------------------------------------------------------------------------
subIt :: Stack -> Maybe Stack
-- ^ Subtracts the first two elements of the stack
subIt stak = case stak of
x:y:xs -> Just (y - x:xs)
x:xs -> Nothing
_ -> Nothing
-------------------------------------------------------------------------
multIt :: Stack -> Maybe Stack
-- ^ Multiplies the first two elements of the stack
multIt stak = case stak of
x:y:xs -> Just (x * y:xs)
x:xs -> Nothing
_ -> Nothing
...
This works by taking a string, breaking it into individual 'words' (if possible, and returning Nothing if it can't) that are then passed into a dictionary to lookup the value of the word compared to the keys in the dictionary, thus acting like a lookup table. If the word is a key in the dictionary, it returns the value, which is a higher order function that does certain tasks (just like the words function, if the higher order function encounters an error, it will return Nothing).
When dealing with Monads, there are only TWO types of return values. Maybe AnyType and Nothing. An AnyType can be any type already declared in the module, or any basic type in Haskell (Int, Char, [Char], etc...). The trick is to return either a Maybe AnyType or a Nothing type. Since Haskell requires terminal declaration for if statements, it can be convention to 'catch' any potential errors and pass along the 'Nothing' type to the final return of a [grand]parent function.

basic Haskell : list comprehension

Me again, I'm trying to iterate through my list of customers to find the correct customer and when I find them I want to display any non-zero int's that are attached to them. I'm not sure how to proceed. I know there will only be 1 record of the person's name in the shop.
type Name = String
type Customer = (Name,Int,Int)
type Shop = [Customer]
shop = [cust1, cust2]
cust1 = ("Steve", 321, 123) :: Customer
cust2 = ("John", 0,678) :: Customer
getName :: Customer -> Name
getName (a, b,c) = a
getNumbers :: Customer -> [Int]
getNumbers (a,b,c) = filter (/=0) [b,c]
rental:: Shop-> Name -> [Int]
rental shop' name' = map getNumbers [ x|x<-shop',getName x == name']
It is very useful to read error message!
test23.hs:10:9:
Couldn't match type `(Name, t0)' with `(Name, Int, Int)'
Expected type: Customer
Actual type: (Name, t0)
You have
getName (a, b) = a
but is defined
type Customer = (Name,Int,Int)
The right function looks like
getName (a, _, _) = a
After correct, you could see next meassage:
test23.hs:17:26:
Couldn't match type `[Int]' with `Int'
Expected type: Customer -> Int
Actual type: Customer -> [Int]
In the first argument of `map', namely `getNumbers'
...
In an equation for `rental'
But error is not in getNumbers, but in signature of rental:: Shop-> Name -> [Int]. Must be:
rental:: Shop-> Name -> [[Int]]
Your answer is pretty close. First of all, you need to update getName to take a 3-tuple, and second you should use concatMap getNumbers instead of map getNumbers.
Although, it looks like you're going to be adding new fields to your Customer type, so I would recommend that you switch to using a record instead:
data Customer = Customer
{ custName :: Name
, custVal1 :: Int -- I don't know what these are, so use real names
, custVal2 :: Int
} deriving (Eq, Show)
And now you could get rid of getName and do
getNumbers :: Customer -> [Int]
getNumbers c = filter (/= 0) [custVal1 c, custVal2 c]
rental :: Shop -> Name -> [Int]
rental shop' name' = concatMap getNumbers [x | x <- shop', custName x == name']
Now if you were to add another field to Customer, you don't have to update all your functions that don't depend on that field.

Body Mass Index program in haskell

I'm trying to write a simple program in Haskell that can determine someone's body mass index.
Here's what I have written:
type Height = Float
type Weight = Float
type PeopleStats = [(String, Height, Weight)]
and...
bmi :: Height -> Weight -> Float
bmi heightCm weightKg = weightKg/(heightCm)^2
healthy :: Height -> Weight -> Bool
healthy heightCm weightKg | 25 > index && 18 < index = True
| otherwise = False
where index = bmi heightCm weightKg
So far, the function "healthy" can calculate someone's BMI, and the function "healthyPeople" returns a boolean statement determining if the person's BMI falls within the limits which is considered normal for a healthy person.
I want to write a function called "healthyPeople".
healthyPeople :: PeopleStats -> [String]
This function needs to take a list of PeopleStats and returns a list of names (Strings) of people who are deemed to be "healthy" from the "healthy" function.
For example:
If I input [("Lee", 65, 185), ("Wang", 170, 100), ("Tsu", 160, 120)] I will get a list of the names of the people whose BMI returns true form the boolean function in "healthy".
Please help !!!!
First, I think you probably meant to define bmi as:
bmi :: Height -> Weight -> Float
bmi heightCm weightKg = weightKg/(heightCm/100)^2
since the formula uses height in meters.
Now, here's a step by step way to do it using helper functions. I defined a type:
type PersonStats = (String, Height, Weight)
and some functions on that type:
healthyPerson :: PersonStats -> Bool
healthyPerson (name, h, w) = healthy h w
getName :: PersonStats -> String
getName (name, h, w) = name
With those in place, the final function becomes trivial:
healthyPeople :: PeopleStats -> [String]
healthyPeople people = map getName $ filter healthyPerson people
or in point-free notation:
healthyPeople :: PeopleStats -> [String]
healthyPeople = map getName . filter healthyPerson
First you filter out the healthy people from the list, then you map the list of stats into a list of names.
You can express the entire function in one go without the helpers if you use lambdas.
There's a standard Haskell function named filter that does exactly (well, almost) what you want here. It has type (a -> Bool) -> [a] -> [a], i.e., it takes a predicate and a list and returns the members that satisfy the predicate.
You can't apply it directly to PeopleStats because the types don't match up, but it's not hard to write a function to connect the two:
healthyPerson :: (String, Height, Weight) -> Bool
healthyPerson (_, h, w) = healthy h w
healthyPeople :: [(String, Height, Weight)] -> [String]
healthyPeople people = map name $ filter healthyPerson people
where name (s, _, _) = s
This does what you want.
Let's think about what you want to do. You have a list, and you want to (a) select only certain items from the list, and (b) do something to each element of the list. This being Haskell, let's express this in types. The first thing you need—well, it'll have to take a list [a], and a way to check if each element is good. How can it check? Well, it should be a function a -> Bool. And it should give us back a smaller list. In other words, something like [a] -> (a -> Bool) -> [a]. Then we want to take our list and do something to each element. In other words, we'll need a list [a], and a function a -> b. Thus, we'll want something of the type [a] -> (a -> b) -> [b]. Now that we have the types, we're golden: we can use Hoogle to search for them. I highly, highly recommend using Hoogle regularly; it's a Haskell search engine which searches both types—the uniquely awesome part—and function/datatype/typeclass/module/package names. The first function, as it turns out, is the second result for the query: filter :: (a -> Bool) -> [a] -> [a]. This takes a function and a list and returns only the elements of the list for which the function is true. The second function is the first result, map :: (a -> b) -> [a] -> [b], which calls the given function on every element of the given list and returns a list of the results. Note that the arguments have the function, not the list, first; this is more natural, as you'll see shortly.
We want to put these two together for healthyPeople:
healthyPeople :: PeopleStats -> [String]
healthyPeople sts = map (\(n,_,_) -> n) $ filter (\(_,h,w) -> healthy h w) sts
This does what you want. $ is function application, but effectively groups the right-hand side because of its precedence; this allows us to elide parentheses. Here we see why it's nice to have map take its function first; we pass it the name-extracting function ((n,_,_) is a pattern which will match a triple and assign n its first element, ignoring the other two), and then (via $) the filtered list.
This is nice, but not how I'd actually write it. Since sts is the last parameter to the function and to its body, it's unnecessary. In truth, all functions in Haskell take only one argument; this means that if you don't pass enough arguments, you get a function which expects the missing arguments and returns the result. With the help of the function-composition operator ., this gives us
healthyPeople :: PeopleStats -> [String]
healthyPeople = map (\(n,_,_) -> n) . filter (\(_,h,w) -> healthy h w)
And that's probably how I'd write it! You'll find yourself using map and filter a lot; they're real workhorses in functional programming.
There is another idiomatic way you can write healthyPeople; you can use a list comprehension, as follows:
healthyPeople :: PeopleStats -> [String]
healthyPeople stats = [n | (n,h,w) <- stats, healthy h w]
This reads as "construct the list of every n such that (n,h,w) is an element of stats and healthy h w is true. If any of the pattern matches or the predicates fail (you can have more than one of each, though you don't need that here), that element is skipped; otherwise, the left side of the | is executed. It's effectively another way of writing the map/filter version.
Edit 1: As many others are saying, your units are off in bmi; you should have heightCm/100. Also, your healthy function has code equivalent to
f x | cond = True
| otherwise = False
This is equivalent to writing, in a C-like,
bool f(some_type x) {
if (cond)
return true;
else
return false;
}
Instead, you should just write
bool f(some_type x) {
return cond;
}
Or, in this case
f x = cond
This gives you the shorter code
healthy :: Height -> Weight -> Bool
healthy heightCm weightKg = let index = bmi heightCm weightKg
in 25 > index && 18 < index
(You can use a where clause too, but here's a let just because I like it better :))
First off, note that your definition of BMI is incorrect - you need to convert centimetres into metres:
bmi heightCm weightKg = weightKg/(heightCm/100)^2
With that fixed, I came up with the following:
healthyPeople :: PeopleStats -> [String]
healthyPeople [] = []
healthyPeople ((name, height, weight):xs) | healthy height weight = name : healthyPeople xs
| otherwise = healthyPeople xs
This is fairly straight forward. It uses list-based recursion to recurse over all the elements of the list, and it uses guards similarly to how you used them in the healthy function to switch behaviour based on whether the person at the head of the list is healty or not. If they are healthy, their name is concatted with the result of processing the rest of the list.
Next time, you should try solving the problem yourself and then ask for help (and show what you've tried). You'll learn far more!
type Height = Float
type Weight = Float
data PersonStats = PersonStats
{ personName :: String, personHeight :: Height, personWeight :: Weight }
bmi :: Height -> Weight -> Float
bmi heightCm weightKg = weightKg/(heightCm / 100)^2
healthy :: Height -> Weight -> Bool
healthy heightCm weightKg = 25 > index && 18 < index
where index = bmi heightCm weightKg
healthyPerson :: PersonStats -> Bool
healthyPerson p = healthy (personHeight p) (personWeight p)
healthyPeople :: [PersonStats] -> [String]
healthyPeople = map personName . filter healthyPerson

Resources