Home > Blockchain >  Combining multiple functions into one in Haskell?
Combining multiple functions into one in Haskell?

Time:11-09

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
  • Related