A neat trick for monoidal combinator libraries

We all love monoidal combinator libraries. What I mean by this is a library that offers a monoid that can e.g. accumulate some settings. Most famous example is probably optparse-applicative where we can have each of the options or flags enriched with some information by appending to some monoid.

We also all know that whenever you do many appends to a monoid, it is often nicer, to instead use a list and use mconcat. But this often is a tradeoff! you now have replaced <> with , but instead you have this annoying mconcat.

You don’t need this tradeof though! Define this simple IsList instance and all your worries will be a thing of the past!

import Data.List qualified

data SomeMonoid = ..

instance IsList SomeMonoid where 
  type Item SomeMonoid = SomeMonoid
  fromList = mconcat
  toList = Data.List.singleton

Now you can always just hand the elements you want to add to your monoid as a list! (of course you need to enable -XOverloadedLists)

This makes working with these libraries even nicer in practice :slight_smile:

PS: yes, the lawful good amongst you may have noticed that IsList has a rule ( fromList . toList = id) attached to it and rest assured, these instances follow it. (keep in mind, the opposite would not be true, but gladly, it doesn’t need to!)

PPS: A trivial example of this in use can be found here.

2 Likes

I’m a fan of Ollie Charles’ “List of Monoids” Pattern: instead of asking the library consumer to turn on -XOverloadedLists, make a point of having functions consume lists of any monoidal argument and do the mconcat internally.

My personal experience with -XOverloadedLists is that I struggle to identify the types in play unless they’re “sequence-like”. Even writing lists of [(k, v)] and having the IsList instance convert them to a Map or HashMap resulted in code that I didn’t enjoy rereading.

3 Likes

the nice thing about OverloadeLists, is that you get both in one, easy to digest, package :slight_smile:

Hrm:

…that doesn’t look like “easy to digest”. There’s also the experience with Disciple/Discus where (->)-notation was overloaded with effects; many people just wanted their Miranda™/Haskell-style pure functions (and type) back. And of course, that do-do notation which some here seem enamoured with, which is causing confusion regarding when an (sub)expression is monadic or not.

There’s already enough “write-only” programming languages - does Haskell really have to be another?

3 Likes