timedestoyer444
timedestoyer444

Reputation: 35

Combining multiple functions into one in Haskell?

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.

Upvotes: 2

Views: 143

Answers (2)

Chris
Chris

Reputation: 36581

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

Upvotes: 4

Fyodor Soikin
Fyodor Soikin

Reputation: 80754

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

Upvotes: 3

Related Questions