So I'm following the tutorial Learn you a Haskell for Great Good! and so far I absolutely love Haskell. But in one of the functions mentioned in the tutorial I'm getting a warning that the if-statement is redundant.
Edit: Let me be clear, the intent of the function is to act exactly the same way the elem function works (the one that is provided with Haskell by default).
Here's the original function:
elem' :: (Eq a) => a -> [a] -> Bool
elem' y ys = foldl (\acc x -> if x == y then True else acc) False ys
Originally there were two warnings, one was eta reduction, so I removed the ys from the beginning and end of the function name to arrive at:
elem' :: (Eq a) => a -> [a] -> Bool
elem' y = foldl (\acc x -> if x == y then True else acc) False
Now I tried to reduce the function to the following and it results in an error:
elem' :: (Eq a) => a -> [a] -> Bool
elem' y = foldl (\acc x -> x == y)
I think I'm just very new to Haskell and can't see the obvious solution. Can someone tell me what code change would keep the function working correctly and yet remove the compiler warning?
if x == y then True else acc is the same as x == y || acc.
Typing your last definition into GHCi without a type annotation:
Prelude> let elem' y = foldl (\acc x -> x == y)
Prelude> :t elem'
elem' :: (Eq b) => b -> Bool -> [b] -> Bool
It doesn't match your declared type.
You forgot the False at the end! If you add it in:
Prelude> let elem' y = foldl (\acc x -> x == y) False -- note the False at the end
Prelude> :t elem'
elem' :: (Eq b) => b -> [b] -> Bool
It has the right type!
Related
I'm trying to create my own version of elem function, which returns True or False based on if the element x is inside the given array.
elem' :: Eq(t)=>t->[t]->Bool
elem' x xs = or [foldl (\acc -> \a -> if a == x then True else False) [] xs]
What I'm trying to do here is to fill the array using foldl with True/False values and then make or function, which returns True if at least one of them is True (so what I want to get). However, this code results in the following compilation error:
main.hs:11:71: error:
* Couldn't match expected type `Bool' with actual type `[a0]'
* In the second argument of `foldl', namely `[]'
In the expression:
foldl (\ acc -> \ a -> if a == x then True else False) [] xs
In the first argument of `or', namely
`[foldl (\ acc -> \ a -> ...) [] xs]'
|
11 | elem' x xs = or [foldl (\acc -> \a -> if a == x then True else False) [] xs]
|
^^
The idea of using a foldl is not to make a list of elements, and thus not to use or.
If you make use of foldl, then in case a == x fails, you look if the elem on the previous elements was succesful, so we return acc in that case. Furthermore you can not use [] as the "start value" for the accumulator, since the foldl is supposed to return a Bool, not a list:
elem' :: (Foldable f, Eq t) => t -> f t -> Bool
elem' x xs = foldl (\acc a -> if a == x then True else acc) False xs
Here it is however not a good idea to use foldl, since that means we will enumerate over the entire list. If we make use of foldr, we can stop from the moment we have found an element, so we can rewrite this to:
elem' :: (Foldable f, Eq t) => t -> f t -> Bool
elem' x = foldr (\e -> if x == e then const True else id) False
If we thus check:
elem' 1 ([1,4,2,5] ++ repeat 3)
It will return True for the foldr approach, whereas it will loop for the foldl approach.
This code works
max_elem :: (Ord a) => [a] -> a
max_elem [x] = x
max_elem [] = error "No elements"
max_elem (x:xs)
|x > max_elem xs = x
|otherwise = max_elem xs
I want to have it so it returns Nothing if their are no elements and Just x for the maximum element
I tried the following
max_elem :: (Ord a) => [a] -> Maybe a
max_elem [x] = Just x
max_elem [] = Nothing
max_elem (x:xs)
|x > max_elem xs = Just x
|otherwise = max_elem xs
I got the following error. Recommendations to fix this please.
• Couldn't match expected type ‘a’ with actual type ‘Maybe a’
‘a’ is a rigid type variable bound by
the type signature for:
max_elem :: forall a. Ord a => [a] -> Maybe a
at <interactive>:208:13
• In the second argument of ‘(>)’, namely ‘max_elem xs’
In the expression: x > max_elem xs
In a stmt of a pattern guard for
an equation for ‘max_elem’:
x > max_elem xs
• Relevant bindings include
xs :: [a] (bound at <interactive>:211:13)
x :: a (bound at <interactive>:211:11)
max_elem :: [a] -> Maybe a (bound at <interactive>:209:1)
You get your error because of this line: x > max_elem xs. max_elem xs has type Maybe a where a is an element of a list. It has type a. You can't compare values of different types. a and Maybe a are different types. See Haskell equality table:
https://htmlpreview.github.io/?https://github.com/quchen/articles/blob/master/haskell-equality-table.html
Replace == operator with > and you will get the same table.
You can solve problem in your code by replacing x > max_elem xs with Just x > max_elem xs. Does it make sense to you?
As you can see, Maybe a data type has Ord a => Ord (Maybe a) instance which is actually really handy! So you can implement your function in even more concise way to utilize this Ord instance:
max_elem :: Ord a => [a] -> Maybe a
max_elem = foldr max Nothing . map Just
Though, this probably won't be the most efficient solution if you care about performance.
The error message was clear enough to solve your problem.
|x > max_elem xs = Just x
The problem is that you compare x which is a with max_elem which is Maybe a. That's why you got such error message. You can solve the problem with this code below.
max_elem :: (Ord a) => [a] -> Maybe a
max_elem [] = Nothing
max_elem (x:xs) = case (max_elem xs) of
Nothing -> Just x
Just y -> if x > y then Just x else Just y
We can generalize this task and work with all Foldables. Here we thus use the foldr :: Foldable t => (a -> b -> b) -> b -> t a -> b function that folds a certain Foldable structure. We can do this with the function max . Just, and as initial element Nothing:
max_elem :: (Ord a, Foldable f) => f a -> Maybe a
max_elem = foldr (max . Just) Nothing
Note that this works since Haskell defines Maybe a to be an instance of Ord, given a is an instance of Ord, and it implements it in a way that Nothing is smaller than any Just element.
This makes the above definition perhaps a bit "unsafe" (in the sense that we here rely on the fact that from the moment we have a Just x, the max will select such Just x over a Nothing). When we would use min, this would not work (not without using some tricks).
We can also use pattern guards and thus solve the case where the list is empty in a different way, like:
max_elem :: Ord a => [a] -> Maybe a
max_elem [] = Nothing
max_elem l = Just (maximum l)
The problem is x > max_elem xs; max_elem xs is Maybe a, not a, meaning that it might return Nothing. However, you do know that it will only return Nothing if xs is empty, but you know xs won't be empty because you matched the case where it would using [x]. You can take advantage of this fact by writing a "non-empty" maximum:
max_elem_ne :: Ord a => a -> [a] -> a
max_elem_ne m [] = m
max_elem_ne m (x:xs)
| m > x = max_elem m xs
| otherwise = max_elem x xs
Or, alternatively, using max:
max_elem_ne :: Ord a => a -> [a] -> a
max_elem_ne m [] = m
max_elem_ne m (x:xs) = max_elem (max m x) xs
You can think of the first argument as the maximum value seen "so far", and the second list argument as the list of other candidates.
In this last form, you might have noticed that max_elem_ne is actually a just left fold, so you could even just write:
max_elem_ne :: Ord a => a -> [a] -> a
max_elem_ne = foldl' max
Now, with max_elem_ne, you can write your original max_elem:
Then you can write:
max_elem :: Ord a => [a] -> Maybe a
max_elem [] = Nothing
max_elem (x:xs) = Just (max_elem_ne x xs)
So you don't have to do any extraneous checks (like you would if you redundantly pattern matched on results), and the whole thing is type-safe.
You can also use the uncons :: [a] -> Maybe (a,[a]) utility function with fmap and uncurry to write:
max_elem :: Ord a => [a] -> Maybe a
max_elem = fmap (uncurry max_elem_ne) . uncons
Say I wanted to remove all zeros at the end of a list:
removeEndingZeros :: (Num a, Eq a) => [a] -> [a]
removeEndingZeros (xs ++ [0]) = removeEndingZeros xs
removeEndingZeros xs = xs
This does not work because of the (++) operator in the argument. How can I determine the end of a list through pattern-matching?
There is a function in Data.List to do this:
dropWhileEnd :: (a -> Bool) -> [a] -> [a]
dropWhileEnd p = foldr (\x xs -> if p x && null xs then [] else x : xs) []
So you can drop the trailing zeros with
dropWhileEnd (== 0)
Another, very similar, function can be implemented like this:
dropWhileEnd2 :: (a -> Bool) -> [a] -> [a]
dropWhileEnd2 p = foldr (\x xs -> if null xs && p x then [] else x : xs) []
dropWhileEnd2 p has exactly the same semantics as reverse . dropWhile p . reverse, but can reasonably be expected to be faster by a constant factor. dropWhileEnd has different, non-comparable strictness properties than the others (it's stricter in some ways and less strict in others).
Can you figure out circumstances under which each can be expected to be faster?
I want to filter a list by predicates curried from another list.
For instance:
multifilter :: (a -> a -> Bool) -> [a] -> [a] -> [a]
multifilter _ _ [] = []
multifilter _ [] _ = []
multifilter f (x:xs) ys = (filter (f x) ys) ++ (multifilter f xs ys)
With usage such as:
prelude> multifilter (==) [1,2,3] [5,3,2]
[2,3]
Is there a standard way to do this?
You can use intersectBy:
λ> :t intersectBy
intersectBy :: (a -> a -> Bool) -> [a] -> [a] -> [a]
λ> intersectBy (==) [1,2,3] [5,3,2]
[2,3]
You can use hoogle to search functions using type signature and finding them.
Note: This answer implements the specification expressed by the words and example in the question, rather than the different one given by the implementation of multifilter there. For the latter possibility, see gallais' answer.
Sibi's answer shows how you should actually do it. In any case, it is instructive to consider how you might write your function using filter. To begin with, we can establish two facts about it:
multifilter can be expressed directly as filter pred for some appropriate choice of pred. Given a fixed "predicate list", whether an element of the list you are multifiltering will be in the result only depends on the value of that element.
In multifilter f xs ys, the list you are filtering is xs, and the "predicate list" is ys. Were it not so, you would get [3,2] rather than [2,3] in your (quite well-chosen) example.
So we have:
multifilter :: (a -> a -> Bool) -> [a] -> [a] -> [a]
multifilter f xs ys = filter pred xs
where
pred = undefined -- TODO
All we need to do is implementing pred. Given an element x, pred should produce True if, for some element y of ys, f x y is true. We can conveniently express that using any:
pred x = any (\y -> f x y) ys
-- Or, with less line noise:
pred x = any (f x) ys
Therefore, multifilter becomes...
multifilter :: (a -> a -> Bool) -> [a] -> [a] -> [a]
multifilter f xs ys = filter pred xs
where
pred x = any (f x) ys
-- Or, more compactly:
multifilter :: (a -> a -> Bool) -> [a] -> [a] -> [a]
multifilter f xs ys = filter (\x -> any (f x) ys) xs
... which is essentially equivalent to intersectBy, as you can see by looking at intersectBy's implementation.
A third option is to use a list comprehension:
multifilter rel xs ys = [ x | x <- xs, y <- ys, x `rel` y ]
or, if you want partial application:
multifilter p xs ys = [ x | x <- xs, let f = p x, y <- ys, f y ]
If you want to use filter,
relate rel xs ys = filter (uncurry rel) $ liftM2 (,) xs ys
(and throw in map fst)
The answer you have accepted provides a function distinct from the one defined in your post: it retains elements from xs when yours retains elements from ys. You can spot this mistake by using a more general type for multifilter:
multifilter :: (a -> b -> Bool) -> [a] -> [b] -> [b]
Now, this can be implemented following the specification described in your post like so:
multifilter p xs ys = fmap snd
$ filter (uncurry p)
$ concatMap (\ x -> fmap (x,) ys) xs
If you don't mind retaining the values in the order they are in in ys then you can have an even simpler definition:
multifilter' :: (a -> b -> Bool) -> [a] -> [b] -> [b]
multifilter' p xs = filter (flip any xs . flip p)
Simply use Hoogle to find it out via the signature (a -> a -> Bool) -> [a] -> [a] -> [a]
https://www.haskell.org/hoogle/?hoogle=%28a+-%3E+a+-%3E+Bool%29+-%3E+%5Ba%5D+-%3E+%5Ba%5D+-%3E+%5Ba%5D
yields intersectBy:
intersectBy :: (a -> a -> Bool) -> [a] -> [a] -> [a]
I am trying to convert a function of maximum utilizing a foldr function to one that also incorporates the maybe type. It is originally:
maximum' :: (Ord a) => [a] -> a
maximum' = foldr1 (\x acc -> if x > acc then x else acc)
It works perfectly with sets, but not with the empty set. I want to convert it to use maybe types instead.
My thought process was:
mymax :: (Ord a) => [Maybe a] -> Maybe a
mymax = foldr (\(Just x) (Just b) -> if ((Just x) > (Just b)) then (Just x) else (Just b)) Nothing
It compiles with no errors and works when I give it the empty set. However, when I give it a set with numbers in it, it no longer works! Can someone point me in the right direction on how I can get the functionality of it also getting the maximum from a list of maybes?
I really want to use the foldr function in my solution....
I've now tried:
mymax :: (Ord a) => [a] -> Maybe a
mymax = foldr (\x b -> if x > b then (Just x) else (Just b)) Nothing
But it will not compile:
Couldn't match expected type `Maybe b -> Maybe a'
with actual type `Maybe a0'
In the return type of a call of `Just'
Probable cause: `Just' is applied to too many arguments
In the expression: (Just x)
In the expression: if x > b then (Just x) else (Just b)
Failed, modules loaded: none.
If you want to do it in a single foldr, we can exploit the fact that Ord a => Ord (Maybe a), that is, any ordering on a can be extended to an ordering on Maybe a.
We also have Just x > Nothing, for all x :: Ord a => a.
mymax :: (Ord a) => [a] -> Maybe a
mymax = foldr (\x b -> let x' = Just x in if x' > b then x' else b) Nothing
-- equivalently
mymax = foldr (\x b -> let x' = Just x in max x' b) Nothing
mymax = foldr (\x' b -> max x' b) Nothing . map (\x -> Just x)
mymax = foldr max Nothing . map Just
If we want to do minimum, we'll have to do it a bit differently, since Nothing is the lower bound for the type Ord a => Maybe a, which means that foldr min Nothing . map Just == const Nothing, which isn't useful.
mymin :: (Ord a) => [a] -> Maybe a
mymin = foldr (\x b -> case b of
Nothing -> Just x
Just y -> Just (min x y)
) Nothing
-- which is equivalent to
mymin = foldr (\x b -> Just $ case b of
Nothing -> x
Just y -> min x y
) Nothing
mymin = foldr (\x b -> Just $ maybe x (min x) b) Nothing
mymin = foldr (\x -> Just . maybe x (min x)) Nothing
Honestly, though, I think pattern matching makes solutions a lot clearer
mymax [] = Nothing
mymax (a:as) = Just $ foldr max a as
mymin [] = Nothing
mymin (a:as) = Just $ foldr min a as
I think you actually want mymax to have type (Ord a) => [a] -> Maybe a. Here's a potential implementation that uses pattern matching and your original maximum' function:
mymax :: (Ord a) => [a] -> Maybe a
mymax [] = Nothing
mymax xs = Just (maximum' xs)
Also, Learn You A Haskell is a great resource if you haven't already come across it!