Understanding Haskell Pattern Matching - haskell

Using pattern matching, define a function:
ifThenElse :: Bool -> Int -> Int -> Int
which gives its second argument if the condition (the first argument) is True, and the
third argument if the condition is False (for example, ifThenElse (3 > 5) 7 12 gives
12).
How would I go about writing this?
Here is what I have so far:
ifThenElse :: Bool -> Int -> Int -> Int
ifThenElse True x1 y1 = x1
ifThenElse False x1 y1 = y1

Here is a hint.
You can solve your problem using if-else statement:
ifThenElse :: Bool -> Int -> Int -> Int
ifThenElse p x y = if p then x else y
But, Bool is enumerated type with only two values. You can match ifThenElse's arguments with Bool values and define what to do if function gets False or True. It's called pattern-matching:
ifThenElse :: Bool -> Int -> Int -> Int
ifThenElse False = ...
ifThenElse True = ...
And at last, if you use pattern-matching and in some case resulting value doesn't depend from particular argument, you can hide that argument using placeholder:
f :: Int -> Int -> Int -> Int
f 0 x y = x + y
f 1 _ y = y -- in case of getting `1` function returns just `y`

Related

How to pass not a function that returns a boolean in another function?

I want to negate a function in the if clause of another function like bellow:
isBig :: Integer -> Bool
isBig n = n > 9999
function :: Integer -> Integer
function n =
if not isBig n then ... else ...
It complies when it's just 'if isBig n then else' but I'm not sure why it doesn't work for 'not isBig' as I get this error:
*Couldn't match expected type Bool' with actual type Integer -> Bool'
Many thanks in advance.
You want not (isBig n). not isBig n tries to pass two arguments to not, both isBig and n. isBig is an Integer -> Bool but a Bool is expected, hence the error.
In general, function application in Haskell is left-associative, meaning that an expression like this:
f 2 3 5
Is parsed like this:
(((f 2) 3) 5)
Likewise, the arrows in function types are right-associative, so for example if we had this definition for f:
f :: Int -> Int -> Int -> Int
f x y z = x * y + z
That type signature is the same as:
f :: Int -> (Int -> (Int -> Int))
So it looks like this as you apply more arguments:
f :: Int -> (Int -> (Int -> Int))
(f 2) :: (Int -> (Int -> Int))
((f 2) 3) :: (Int -> Int)
(((f 2) 3) 5 :: Int
==
f :: Int -> Int -> Int -> Int
f 2 :: Int -> Int -> Int
f 2 3 :: Int -> Int
f 2 3 5 :: Int
When you’re applying a chain of functions to an argument, you end up with parentheses associating to the right:
f (g (h x))
In this case it’s common to use the $ operator, which is right-associative and has low precedence, just to reduce the nesting of brackets:
f $ g $ h x
And you can do so in your case: not $ isBig n
You can also use composition to factor out the chain of functions and apply it to different arguments elsewhere:
fgh = f . g . h
fgh x
==
(f . g . h) x
==
f (g (h x))
isNotBig = not . isBig
isNotBig n
==
(not . isBig) n
==
not (isBig n)

Haskell - fast naming of projection functions

Suppose I want to define a function f in terms of some other predefined function g as follows:
f :: Int -> Int -> Int
f 2 b = g b
f _ _ = 1
That is, I want to define the projection, f(2,_) : Int->Int to be the same as g(_) : Int->Int. Gladly, Haskell has first class functions, and so definitions like the following squarePlusOne are valid and standard:
plusOne :: Int -> Int
plusOne i = i+1
square :: Int -> Int
square i = i*i
squarePlusOne :: Int -> Int
squarePlusOne = plusOne . Square
With Haskell's currying (ie. f takes just one Int as input and returns an (Int->Int) typed function), I am surprised I cannot write
f 2 = g
Why not? Or is there some other syntax?
Indeed, writing f 2 = g is a valid way to define f. However, when defining functions in this way, remember you must define the whole function with the same pattern signature. That is, you may not exhaust your function by writing
f 2 = g
f i j = i+j
Instead, this may be achieved like so:
f 2 = g
f i = (\j-> i+j)
You can use the const function, which creates a function that ignores its argument to return a fixed value.
f :: Int -> Int -> Int
f 2 = g -- f 2 x = g x
f _ = const 1 -- f _ x = const 1 x == (\_ -> 1) x == 1

How to return different type in recursion function? (Haskell)

I need to return for example either an integer or a Boolean in a recursive function.
An example code of my problem:
findInt :: [Int] -> Either Int Bool
findInt (x:xs) =
if x == 1 then x
else False : findInt xs
Then the error says couldn't match expected type "Either Int Bool" with actual type "Int". But I want to check whether the element is in this list and if it does it will return the element and tell me if it's not by returning a Boolean.
If you have a look at the Either type, you'll see that it has two constructors, Left and Right. In order to construct a value of the Either type, you need to use one of the constructors. e.g.
if x == 1 then Left x
Specifically, Left is used to construct the first type of the Either type (Int in this case), and Right is used for the second type (Bool in this case).
Your function as given has no type: x :: Int and (False : _) :: [Bool], and these two types can't be unified (read :: as "has a type").
Tweaked,
findInt (x:xs) =
if x == 1 then Left x
else Right False : findInt xs
it still has no type: Left x :: Either Int b and (Right False : _) :: [Either a Bool], and these two types can't be unified either.
But
findInt :: [Int] -> Either Int Bool
findInt (x:xs) =
if x == 1 then Left x
else findInt xs
findInt [] = Right False
has a type, because Left x :: Either Int b and Right False :: Either a Bool, and these two types can be unified:
Either Int b
Either a Bool
----------------- a ~ Int , b ~ Bool
Either Int Bool
The unified type is indeed Either Int Bool, as the type signature specifies.
Since you never use True in your output, your use of Either Int Bool is isomorphic to Maybe Int:
a2b :: Either Int Bool -> Maybe Int
a2b (Right x) = Just x
a2b (Left x) = Nothing
b2a :: Maybe Int -> Either Int Bool
b2a (Just x) -> Right x
b2a Nothing -> Left False
As such, I would just use Maybe Int to simplify your function.
findInt :: [Int] -> Maybe Int
findInt [] = Nothing
findInt (x:xs) = if x == 1 then Just x else findInt xs

No instance for (Num Bool) arising from the literal ‘0’

My code:
divisibleBy :: Int -> Int -> Bool
divisibleBy x y
| mod x y == 0 = True
| otherwise = False
isEven :: Int -> Bool
isEven x
| (divisibleBy x 2) == 0 = True
| otherwise = False
Error:
practical1.hs:30:28: error:
• No instance for (Num Bool) arising from the literal ‘0’
• In the second argument of ‘(==)’, namely ‘0’
In the expression: (divisibleBy x 2) == 0
In a stmt of a pattern guard for
an equation for ‘isEven’:
(divisibleBy x 2) == 0
|
30 | | (divisibleBy x 2) == 0 = True |
The divisibleBy function works, but the isEven one doesn't. What am I doing wrong?
Well the error message already says this. You write:
isEven :: Int -> Bool
isEven x
| (divisibleBy x 2) == 0 = True
| otherwise = False
Now if we typecheck this, we see that (divisibleBy x 2) will return a Bool, and you cannot perform a (==) with a Bool and a number (a Bool is in Haskell not a number).
Why you write this with == 0 is not really clear for me, we can write it as:
isEven :: Int -> Bool
isEven x
| (divisibleBy x 2) = True
| otherwise = False
But now it is still inelegant: we do not have to check if a condition holds to return True and otherwise False, we can simply return the definition, so:
isEven :: Int -> Bool
isEven x = divisibleBy x 2
Or we can omit the x parameter, by using flip :: (a -> b -> c) -> b -> a -> c:
isEven :: Int -> Bool
isEven = flip divisibleBy 2
The same holds for the divisibleBy function, we can rewrite it to:
divisibleBy :: Int -> Int -> Bool
divisibleBy x y = mod x y == 0
Or without parameters:
divisibleBy :: Int -> Int -> Bool
divisibleBy = ((0 ==) .) . mod
Swapping the parameters of divisableBy
It looks however more Haskell to swap the parameters of the function, so we can write it as:
divisibleBy :: Int -> Int -> Bool
divisibleBy x y = mod y x == 0
Since now we can define a function divisibleBy 2, that will check for any parameter whether that number is divisible by two.
In that case, the isEven function, looks like:
isEven :: Int -> Bool
isEven = divisibleBy 2

Haskell, error unwrapping Maybe Int

I wrote code:
is3 :: Int -> Maybe Int
is3 x = is3temp 0 x
is3temp :: Int -> Int -> Maybe Int
is3temp p x = if (abs p*p*p) < (abs x) then (is3temp (p+(signum x)) x) else (if p*p*p == x then (Just p) else Nothing)
c :: Maybe Int -> Int
c (Just x) = 2*x+1
c Nothing = 0
--fun::Int -> Int
--fun = c.is3
c1:: Int -> Int
c1 x = 2*x +1
c1 0 = 0
fun::Int -> Int
fun x = (is3 x) >>= c1
As you can see is3 takes Int and returns Maybe Int.
In fun I take result from is3 unwrap it and try to send it to c1.
And get error
ERROR file:1627.hs:47 - Type error in application
*** Expression : is3 x >>= c1
*** Term : c1
*** Type : Int -> Int
*** Does not match : a -> b c
What is wrong here?
Let's inspect all types that come together in is3 x >>= c1:
x :: Int
is3 :: Int -> Maybe Int
c1 :: Int -> Int
||| /???\
is3 x :: Maybe Int ||| ???????
(>>=) :: Maybe a -> (a -> Maybe b) -> Maybe b
(>>=) is3 x :: (Int -> Maybe b) -> Maybe b
is3 x >>= c1 :: $!%&/#?
As you can see, c1 doesn't work as right hand side of >>=, since it's return type is wrong. It must return some kind of Maybe. One can easily fix this with return . c1:
return . c1 :: Int -> Maybe Int
or with fmap, since the monad laws dictate that both must have the same effect:
fmap c1 :: Maybe Int -> Maybe Int
fmap c1 (is3 x) :: Maybe Int
However, this will also change fun's type. That being said, your functions name are rather bad and c1 is probably a typo (if fun's type is correct). It's possible that you actually wanted to use c:
fun :: Int -> Int
fun x = c (is3 x)
Some code that compiles:
is3 :: Int -> Maybe Int
is3 x = is3temp 0 x
is3temp :: Int -> Int -> Maybe Int
is3temp p x = if (abs p*p*p) < (abs x) then (is3temp (p+(signum x)) x) else (if p*p*p == x then (Just p) else Nothing)
c1 :: Int -> Int
c1 0 = 0
c1 x = 2*x +1
fun :: Int -> Maybe Int
fun = (fmap c1) . is3
I changed the type of fun from Int -> Int to Int -> Maybe Int, and am using fmap instead of >>=.
Also I flipped the order of the matches in c1. Matches are applied top to bottom, so the 0 case never would have applied in the order you have it.

Resources