Non-Obvious Haskell Idiom: ViewPattern Argument Transform

1 month ago 5

Let’s say we’re making a game where the player carries some inventory, and this is a set of items for quick membership tests. Our function to print that inventory might have the signature

In[3]:

printInventory :: Set Item -> IO Int

where each item is printed on its own line, and then the integer returned is the width of the inventory listing – i.e. the length of the longest line printed. The simplest implementation of this would be in terms of a list of items1 You could argue we should perform a side-effectful fold over the set instead to find the maximum length printed. That would probably be a better design and side-step the entire conversion problem, but bear with me. Coming up with good examples is hard.:

In[4]:

printInventory itemSet = do -- Convert set of items to a list of items. let items = Set.toList itemSet -- Print all items with their index, collecting the printed width. widths <- for (zip [1..] items) $ \(idx, item) -> do let printed = show idx <> ". " <> name item putStrLn printed pure (length printed) -- Return the largest of the printed widths, and zero if none were printed. pure (foldr max 0 widths)

The reason we need to transform the set to a list first is that sets are not traversable, so the for function does not work on them.

However, this implementation leaves us with a loose itemSet variable that has a value but is not meant to be used in the method. Having variables around that we intend not to use is … perhaps not a code smell, but at least a mildly unpleasant aroma. When writing code, we try to write it so that we name only the things we care about.

This is where we can use view patterns to transform an argument to a function before we give it a name. By moving the Set.toList transformation up into a view pattern, we get to name the result of that transformation and keep the raw value un-named:

In[5]:

printInventory (Set.toList -> items) = do widths <- for (zip [1..] items) $ \(idx, item) -> do let printed = show idx <> ". " <> name item putStrLn printed pure (length printed) pure (foldr max 0 widths)

Maybe it’s the type of code I’m in the middle of writing2 Wrangling a compatibility layer between two apis., but I find this useful very often.

Read Entire Article