Am I overcomplicating this code with newtypes?

I’ve started learning PureScript - and I’m working through the PureScript by example exercises.

In Ch3 the final exercise is

(Difficult) Write a function removeDuplicates which 
removes duplicate address book entries with the same first 
and last names. Hint: Use PSCi to find the type of the 
Data.List.nubBy function, which removes duplicate 
elements from a list based on an equality predicate.

instead of using nubBy I decided that I wanted to try creating an Eq instance for the relevant records. I figured out that this required wrapping them in newtype - which then adds some over head for unwrapping and wrapping the record.

in one example I had to change a function like so

-- before
findAddress :: String -> String -> String -> AddressBook -> Maybe Entry 
findAddress street city state = head <<< filter filterAddresses 
  where 
        filterAddresses :: Entry -> Boolean 
        filterAddresses { address: a } =  
          a.street == street && 
          a.city == city && 
          a.state == state 

-- after
findAddress :: String -> String -> String -> AddressBook -> Maybe Entry
findAddress street city state = head <<< filter filterAddresses
  where 
        filterAddresses :: Entry -> Boolean
        filterAddresses = unwrap >>> (\{ address: wa } -> 
                          (unwrap >>> (\a -> 
                              a.street == street &&
                              a.city == city &&
                              a.state == state)) wa)

nesting calls to unwrap feels particularly ugly to me - and I’m certain that there must be a cleaner way to do this. Any suggestions?

Here is my complete code

1 Like

If you use newtypes, I think you can simplify by using pattern-matching instead of wrapping/unwrapping.

findAddress :: String -> String -> String -> AddressBook -> Maybe Entry
findAddress street city state = head <<< filter filterAddresses
  where 
        filterAddresses :: Entry -> Boolean
        filterAddresses (Entry { address: (Address a) }) =
            a.street == street &&
            a.city == city &&
            a.state == state

If you want to avoid pattern-matching, I suspect you could use Lens, view (_Newtype <<< prop (SProxy :: SProxy "address")), but I haven’t figured out how the intended ways of creating, composing, and using Lens to get multiple values out, like { street :: String, city :: String, state :: String }. The profunctor-lens repo needs more docs :frowning: – it looks like a great tool to understand.

2 Likes

Thanks for that, it looks a lot cleaner! I wasn’t deliberately trying to avoid pattern matching, I just didn’t realise I could use it that way.

Lenses could be a good way to do it! If I can figure out how to get multiple values out I’ll let you know.

1 Like

Also note that you could do the unwrapping before filterAddresses: filter (filterAddresses <<< unwrap <<< _.address <<< unwrap)

1 Like