How to make a Haskell function calculate ticket prices? - haskell

I am new to Haskell and am trying to write a function that does the following:
asks user to enter "Youth" "Adult" or "Senior" and indicate of the one they pick
function then calculates total price of tickets (Y = $10.50, A = $20, S = $15)
Sample output:
Enter type of ticket and count:
Senior 7
Total price is: 95.00
Here is what I have come up with so far:
calcPrice :: (String a) => a -> b -> b
calcPrice x y =
if x == "Youth"
then "Total price :" ++ 10.5 * y
else if x == "Adult"
then "Total price :" ++ 20 * y
else if x == "Senior"
then "Total price :" ++ 15 * y
else "Invalid input."
...but I get this error:
homework8.hs:31:15: error:
• Expected kind ‘* -> Constraint’, but ‘String’ has kind ‘*’
• In the type signature: calcPrice :: (String a) => a -> b -> b
|
31 | calcPrice :: (String a) => a -> b -> b |
Any help is appreciated, but my main questions are:
How do we specify the types if we want to output a string and a number?
In other variants, an error message appeared that it was not happy about the comparison in the if/else if statements. why?
How do I make the function output something if it can only be called with arguments that the user has no idea to input until the message is displayed?
EDIT_________________
Here is the new code after making the changes you all have mentioned:
calcPrice :: (Integral a) => String -> a -> String
calcPrice x y =
if x == "Youth"
then "Total price :" ++ Show(10.5 * y)
else if x == "Adult"
then "Total price :" ++ Show(20 * y)
else if x == "Senior"
then "Total price :" ++ Show(15 * y)
else "Invalid input."
and I now get this error...
homework8.hs:34:31: error:
• Data constructor not in scope: Show :: a -> [Char]
• Perhaps you meant variable ‘show’ (imported from Prelude)
|
34 | then "Total price :" ++ Show(10.5 * y) | ^^^^
homework8.hs:36:31: error:
• Data constructor not in scope: Show :: a -> [Char]
• Perhaps you meant variable ‘show’ (imported from Prelude)
|
36 | then "Total price :" ++ Show(20 * y) | ^^^^
homework8.hs:38:31: error:
• Data constructor not in scope: Show :: a -> [Char]
• Perhaps you meant variable ‘show’ (imported from Prelude)
|
38 | then "Total price :" ++ Show(15 * y) | ^^^^
Failed, no modules loaded.

How do we specify the types if we want to output a string and a number?
One way is to give back a tuple of a string and a number:
calcPrice :: String -> Double -> (String, Double)
calcPrice x y =
if x == "Youth"
then ("Total price :", 10.5 * y)
else if x == "Adult"
then ("Total price :", 20 * y)
else if x == "Senior"
then ("Total price :", 15 * y)
else ("Invalid input.", 0/0)
However, as the comments pointed out, in this case it's probably more sensible to encode and append the number part into the string. Conversion from many types to String is easily done with the show function (which is a method of the Show (uppercase) typeclass – note how the show function you use in your value level code is lowercase)
calcPrice :: String -> Double -> String
calcPrice x y =
if x == "Youth"
then "Total price :" ++ show (10.5 * y)
else if x == "Adult"
then "Total price :" ++ show (20 * y)
else if x == "Senior"
then "Total price :" ++ show (15 * y)
else "Invalid input."
The other sub-questions are unclear to me – maybe remove them here and flesh them out as separate questions.
Some stylistic remarks:
Rather than doing a bunch of separate if x== statements, it's neater and in general also more efficient to use case (which is very flexible in Haskell, and often works even for types that can't be equality-compared):
calcPrice x y = case x of
"Youth" -> "Total price :" ++ show (10.5 * y)
"Adult" -> "Total price :" ++ show (20 * y)
"Senior" -> "Total price :" ++ show (15 * y)
_ -> "Invalid input."
Alternative syntax for the same thing: you can simply write a bunch of separate function clauses for specific values of x, rather than only one that accepts any x
calcPrice "Youth" y = "Total price :" ++ show (10.5 * y)
calcPrice "Adult" y = "Total price :" ++ show (20 * y)
calcPrice "Senior" y = "Total price :" ++ show (15 * y)
calcPrice _ _ = "Invalid input."
Not just in Haskell, but any language it's good to follow the DRY principle. A standard way of this is to define local variables, like
calcPrice x y = case x of
"Youth" -> totalPrice ++ show (10.5 * y)
"Adult" -> totalPrice ++ show (20 * y)
"Senior" -> totalPrice ++ show (15 * y)
_ -> "Invalid input."
where totalPrice = "Total price :"
don't do it this way, because the actual code-duplication (++ show, * y) is still there! Instead, define local functions (which are also variables)
calcPrice x y = case x of
"Youth" -> priceMul 10.5
"Adult" -> priceMul 20
"Senior" -> priceMul 15
_ -> "Invalid input."
where priceMul μ = "Total price :" ++ show (μ*y)
In a bigger project you should probably separate the “is the input valid?” from the actual calculation logic, else you'll soon have weird errors popping up deep in your code where you never expected them. Worse, if the error is merely “signalled” by a dedicated string value "Invalid input", it becomes very difficult to find where the error did happen.
Advocates of dynamic languages argue that this is what unit tests are there for, but a static type system reduces the need for that a lot. Specifically, you can accept instead of a string which could easily have lots of invalid values, a type that only has those values that are actually valid:
data PricingTier = Youth | Adult | Senior
calcPrice :: PricingTier -> Double -> String
calcPrice x y = case x of
Youth -> priceMul 10.5
Adult -> priceMul 20
Senior -> priceMul 15
where priceMul μ = "Total price :" ++ show (μ*y)

Related

create a haskell function that returns the definition of a Haskell function that implements the expression

if I have the following data type
data Exp = Var String | Num Integer | OpExp Op Exp Exp data Op = Plus | Times | Minus
I would like to create code that takes a name and an Exp and returns the definition of a haskell function that implements the expression. for simplicity, lets assume it will only contain the variables "x" or "y"
so an example of what I am trying to accomplish
Lib> compile "foo" $ OpExp Times (Num 10) (OpExp Plus (Var "x") (Var "y"))
which would return:
"foo x y = (10 * (x + y))"
and this is what I have defined so far
compile :: String -> Exp -> String
compile name exp = name + " x y = undefined"
compile :: String -> Exp -> String
compile name exp = name ++ " x y = " ++(exp2string exp)
exp2string:: Exp -> String
exp2string (Var s) = s
exp2string (Num i) = show i
exp2string (OpExp o e1 e2) ="(" ++ (exp2string e1) ++ (op2string o) ++(exp2string e2) ++ ")"
op2string:: Op -> String
op2string Plus = " + "
op2string Times = " * "
op2string Minus = " - "

A value is not fixed in `case ~ of`

My situation:
In my code, a value xID seems to be variable in case ~ of structure.
import Debug.Trace
data I = I
{ iID :: Int } deriving Show
data C = C
{ i :: I} deriving Show
x = I 0
aC2 = C (I 2)
aC3 = C (I 3)
aC5 = C (I 5)
xID = iID x
cConverter aC =
trace ((show cIID) ++ (if cIID == xID then "==" else "/=") ++ (show xID) ++ " when " ++ (show x)) $
"Point: " ++ pID
where
pID :: String
pID =
case cIID of
xID -> trace ((show cIID) ++ (if cIID == xID then "==" else "/=") ++ (show xID) ++ " when " ++ (show x)) "X"
_ -> show cIID
cIID = iID . i $ aC
-- xID = iID x
What I expect
I expected that when I run cConverter aC2, I'll get "2" because 2 in aC2 = C (I 2) is not equal to 0 in x = I 0.
What happens
However, I've faced the strange result when I run cConvert aC2 like:
Main> cConverter aC2
"2/=0 when I {iID = 0}
Point: 2==2 when I {iID = 0}
X"
Why I get "X" instead of "2"?
More precisely, Why xID is 2 when cIID is 2, and xID is 3 when cIID is 3?
I think xID always be 0 in this code, but does xID means something other when I use this in the condition of the case?
Clear sample
Here is more clear code without debugging message
data I = I
{ iID :: Int } deriving Show
data C = C
{ i :: I} deriving Show
x = I 0
aC2 = C (I 2)
aC3 = C (I 3)
aC5 = C (I 5)
xID = iID x
cConverter aC =
"Point: " ++ pID
where
pID :: String
pID =
case cIID of
xID -> "X"
_ -> show cIID
cIID = iID . i $ aC
-- xID = iID x
Point 1
GHCi warns me like:
Bug.hs:22:7: Warning:
Pattern match(es) are overlapped
In a case alternative: _ -> ...
It seems to be xID overlaps _.
But why xID overlaps hole?
Anyway, I avoid this problem by using a guard instead of case.
However, I could not understand what happens with my code.
The case
case cIID of
xID -> ...
_ -> ...
introduces a new local variable named xID, which is unrelated to the global xID. Further, since it is a variable it catches everything: the branch _ -> ... will never be taken.
Use this instead:
case cIID of
xID' | xID' == xID -> ...
_ -> ...
or, more simply,
if cIID == xID
then ...
else ...
About "why" it works in this way:
Consider the code
foo :: Either Int String -> Int
foo e = case e of
Left x -> x
Right y -> length y
This is a nice total function: it will always return an Int whatever is the value of the argument e.
Now suppose I add to the code, much later on,
x :: Int
x = 42
This should NOT break foo! Yet, if the x in Left x is now interpreted to be 42, then function foo will crash on e.g. Left 43.
For this reason, pattern matching always introduces new variables, it never performs equality checks with pre-existing variables. To do that, use a guard like x | x == y -> ....

Why does the Haskell `show ` function give me trouble here?

I am just a few days into Haskell and learning it from Learn You a Haskell for a great good. While trying out one of the 99 Haskell problems, I ran into the following error while loading my function into ghci.
The problem asks to write a function elementAt k x which takes a number k, a list x and extracts the kth element of the list x.
Here is my function
elementAt :: Int -> [a] -> a
elementAt k x
| k < 0 = error "You have passed a negative index"
| null x = error "Cannot extract from an empty list"
| (length x) < k = error "The array contains fewer than " ++ (show k) ++ "elements"
elementAt 0 (x:_) = x
elementAt k (_:xs) = elementAt (k-1) xs
On loading this function into ghci I get the error
Couldn't match expected type `a' with actual type `[Char]'
`a' is a rigid type variable bound by
the type signature for elementAt :: Int -> [a] -> a at fun.hs:77:14
Relevant bindings include
x :: [a] (bound at fun.hs:78:13)
elementAt :: Int -> [a] -> a (bound at fun.hs:78:1)
In the expression:
error "The array contains fewer than " ++ (show k) ++ "elements"
In an equation for `elementAt':
elementAt k x
| k < 0 = error "You have passed a negative index"
| null x = error "Cannot extract from an empty list"
| (length x) < k
= error "The array contains fewer than " ++ (show k) ++ "elements"
The trouble seems to lie with the way I have used the show function, but I
don't see why. On removing the show call the function seems to compile and
work perfectly.
You will need to put parentheses around your error message in line 5.
Currently your implementation is equal to this one:
(error "The array contains fewer than ") ++ show k ++ "elements"
While you most likely wanted it to do this:
error ("The array contains fewer than " ++ show k ++ "elements")
You can also use the ($) syntax like so:
error $ "The array contains fewer than " ++ show k ++ "elements"
According to Haskell Report, f x ++ g y parses as (f x) ++ (g y). In your case,
error "The array contains fewer than " ++ (show k) ++ "elements"
parses as
(error "The array contains fewer than ") ++ (show k) ++ "elements"

Haskell: Multiple Case Statements in Single Function

I want to include more than one case statement in a Haskell function (see below for an example of a hypothetical function).
However, it is not legal Haskell. What is a better way of accomplishing the same thing? Furthermore, if the case statements are not returning anything, but simply setting some value, why is it not legal to have more than one case statement in a function?
(I would get a "parse error on input `case'" on line 5)
tester x y =
case (x < 0) of
True -> "less than zero."
False -> "greater than or equal to zero."
case (y == "foo")
True -> "the name is foo."
False -> "the name is not foo."
Note that if my function were simply:
tester x y =
case (x < 0) of
True -> "less than zero."
False -> "greater than or equal to zero."
...then it would compile.
In general the body of a function has to be a single expression (very often made up of smaller expressions). The following isn't allowed, for example:
f x y =
"foo"
"bar"
This is equivalent to your first example—we've just substituted one kind of expression (string literals) for another (your case expressions).
It's certainly possible to include more than one case expression in a Haskell function:
tester :: Int -> String -> (String, String)
tester x y = (a, b)
where
a = case (x < 0) of
True -> "less than zero."
False -> "greater than or equal to zero."
b = case (y == "foo") of
True -> "the name is foo."
False -> "the name is not foo."
Or even:
tester :: Int -> String -> IO ()
tester x y = do
putStrLn $ case (x < 0) of
True -> "less than zero."
False -> "greater than or equal to zero."
putStrLn $ case (y == "foo") of
True -> "the name is foo."
False -> "the name is not foo."
These work because the body of the function is a single expression (although neither is really idiomatic Haskell).
I wouldn't use a case statement in this case though, this IMO looks better:
tester :: Int -> String -> String
tester x y | x < 0 = "less than zero. " ++ expr
| otherwise = "greater than or equal to zero. " ++ expr
where expr = if y == "foo" then "the name is foo." else "the name is not foo."
In general, it looks like what you want is guards. However, as already mentioned, your function is not a single expression. Assuming that you want to return a tuple of strings, it can be written like this using guards (and some added fun from Arrows):
import Control.Arrow
testx x | x < 0 = "Less then zero."
| otherwise = "Greater then or equal to zero."
testy y | y == "foo" = "The name is foo."
| otherwise = "The name is not foo."
tester = curry (testx *** testy)
You could also drop the Control.Arrow bit all together and write:
tester x y = (testx x, testy y)

Strange pattern matching with functions instancing Show

So I'm writing a program which returns a procedure for some given arithmetic problem, so I wanted to instance a couple of functions to Show so that I can print the same expression I evaluate when I test. The trouble is that the given code matches (-) to the first line when it should fall to the second.
{-# OPTIONS_GHC -XFlexibleInstances #-}
instance Show (t -> t-> t) where
show (+) = "plus"
show (-) = "minus"
main = print [(+),(-)]
returns
[plus,plus]
Am I just committing a mortal sin printing functions in the first place or is there some way I can get it to match properly?
edit:I realise I am getting the following warning:
Warning: Pattern match(es) are overlapped
In the definition of `show': show - = ...
I still don't know why it overlaps, or how to stop it.
As sepp2k and MtnViewMark said, you can't pattern match on the value of identifiers, only on constructors and, in some cases, implicit equality checks. So, your instance is binding any argument to the identifier, in the process shadowing the external definition of (+). Unfortunately, this means that what you're trying to do won't and can't ever work.
A typical solution to what you want to accomplish is to define an "arithmetic expression" algebraic data type, with an appropriate show instance. Note that you can make your expression type itself an instance of Num, with numeric literals wrapped in a "Literal" constructor, and operations like (+) returning their arguments combined with a constructor for the operation. Here's a quick, incomplete example:
data Expression a = Literal a
| Sum (Expression a) (Expression a)
| Product (Expression a) (Expression a)
deriving (Eq, Ord, Show)
instance (Num a) => Num (Expression a) where
x + y = Sum x y
x * y = Product x y
fromInteger x = Literal (fromInteger x)
evaluate (Literal x) = x
evaluate (Sum x y) = evaluate x + evaluate y
evaluate (Product x y) = evaluate x * evaluate y
integer :: Integer
integer = (1 + 2) * 3 + 4
expr :: Expression Integer
expr = (1 + 2) * 3 + 4
Trying it out in GHCi:
> integer
13
> evaluate expr
13
> expr
Sum (Product (Sum (Literal 1) (Literal 2)) (Literal 3)) (Literal 4)
Here's a way to think about this. Consider:
answer = 42
magic = 3
specialName :: Int -> String
specialName answer = "the answer to the ultimate question"
specialName magic = "the magic number"
specialName x = "just plain ol' " ++ show x
Can you see why this won't work? answer in the pattern match is a variable, distinct from answer at the outer scope. So instead, you'd have to write this like:
answer = 42
magic = 3
specialName :: Int -> String
specialName x | x == answer = "the answer to the ultimate question"
specialName x | x == magic = "the magic number"
specialName x = "just plain ol' " ++ show x
In fact, this is just what is going on when you write constants in a pattern. That is:
digitName :: Bool -> String
digitName 0 = "zero"
digitName 1 = "one"
digitName _ = "math is hard"
gets converted by the compiler to something equivalent to:
digitName :: Bool -> String
digitName x | x == 0 = "zero"
digitName x | x == 1 = "one"
digitName _ = "math is hard"
Since you want to match against the function bound to (+) rather than just bind anything to the symbol (+), you'd need to write your code as:
instance Show (t -> t-> t) where
show f | f == (+) = "plus"
show f | f == (-) = "minus"
But, this would require that functions were comparable for equality. And that is an undecidable problem in general.
You might counter that you are just asking the run-time system to compare function pointers, but at the language level, the Haskell programmer doesn't have access to pointers. In other words, you can't manipulate references to values in Haskell(*), only values themselves. This is the purity of Haskell, and gains referential transparency.
(*) MVars and other such objects in the IO monad are another matter, but their existence doesn't invalidate the point.
It overlaps because it treats (+) simply as a variable, meaning on the RHS the identifier + will be bound to the function you called show on.
There is no way to pattern match on functions the way you want.
Solved it myself with a mega hack.
instance (Num t) => Show (t -> t-> t) where
show op =
case (op 6 2) of
8 -> "plus"
4 -> "minus"
12 -> "times"
3 -> "divided"

Resources