I want to implement a function which takes a key k :: Either a b, and a list of pairs. I want the keys to have two shapes. One of the shape Left l, which will perform a lookup on the left half of the pairs, and one for the right half. Both shall return the other half of the first matching pair in the list. If no match is found, the function should return Nothing.
An instance of the Output with example list:
namesAges = [("Felix", 45), ("Grace", 25), ("Hans", 57), ("Ivy", 25)]
bidirectionalLookup (Left "Grace") namesAges == Just (Right 25)
bidirectionalLookup (Right 57) namesAges == Just (Left "Hans")
I've successfully defined the functions for lookupRhs as well as lookupLhs but I am not able to combine them in my "bidirectionalLookup" function. What function form do I use? ITE, case-of, pattern-matching?
This is one version of my attempts. I have many modifications but none gave me any results. I have a feeling that I am on the wrong track.
namesAges = [("Felix", 45), ("Grace", 25), ("Hans", 57), ("Ivy", 25)]
lookupLhs :: Eq a => a -> [(a, b)] -> Maybe b
lookupLhs x ((l, r) : namesAges) = if x == l then Just r else lookupLhs x namesAges
lookupRhs :: Eq b => b -> [(a, b)] -> Maybe a
lookupRhs x ((l, r) : namesAges) = if x == r then Just l else lookupRhs x namesAges
bidirectionalLookup :: (Eq b, Eq a) => Either a b -> [(a, b)] -> Maybe (Either a b)
bidirectionalLookup (Left x) namesAges = lookupLhs x
bidirectionalLookup (Right x) namesAges = lookupRhs x
bidirectionalLookup _ _ = Nothing
I am aware that this is beginner level and that I may be completely off track (or have the answer right in front of my nose for that matter), still any kind of help will be greatly appreciated.
CodePudding user response:
Your bidirectionalLookup
function is supposed to return a value of type Maybe (Either a b)
but lookupLhs
and lookupRhs
return just a Maybe
type.
You need to match against the return of these functions, and appropriately rewrap the result.
bidirectionalLookup :: (Eq b, Eq a) => Either a b -> [(a, b)] -> Maybe (Either a b)
bidirectionalLookup (Left x) namesAges =
case lookupLhs x namesAges of
Nothing -> Nothing
Just result -> Just (Right result)
bidirectionalLookup (Right x) namesAges =
case lookupRhs x namesAges of
Nothing -> Nothing
Just result -> Just (Left result)
Note that your lookup functions feature non-exhaustive pattern matching as they never handle an empty list. This is easily remedied by pattern matching the list as empty and returning Nothing
. For the other case you may find conditional guards a more idiomatic way of handling this than if/else.
lookupLhs :: Eq a => a -> [(a, b)] -> Maybe b
lookupLhs _ [] = Nothing
lookupLhs x ((l, r) : namesAges)
| x == l = Just r
| otherwise = lookupLhs x namesAges
CodePudding user response:
First of all, your lookupLhs
and lookupRhs
functions will inevitably crash on non-existent keys, because they are not defined for empty lists. You need to add that case:
lookupLhs _ [] = Nothing
And as far as bidirectionalLookup
, you're almost there. All you need is just provide the second argument for lookupLhs
and lookupRhs
, and then map over the resulting Maybe
to insert either Left
or Right
constructor:
bidirectionalLookup (Left x) namesAges = Left <$> lookupLhs x namesAges
bidirectionalLookup (Right x) namesAges = Right <$> lookupRhs x namesAges