🎉I am writing these notes at Brick, a magical mystery no-bullshit publishing platform. Turns out writing goes much faster when I don't have to hit “Publish” or do
git commit
.You can use it too — check it out at Brick.do.
I don't use these combinators often, but when I do, they save me from writing a bunch of boring code.
Recently-ish lens got supplemented with an enticingly named module Control.Lens.Unsound
. It provides:
-- UNION OF LENSES!
lensProduct :: ALens' s a -> ALens' s b -> Lens' s (a, b)
-- UNION OF TRAVERSALS!!
adjoin :: Traversal' s a -> Traversal' s a -> Traversal' s a
⚠️ The module gives plenty of warning, but we are here for the good news, not the bad news. But yeah — these operations will lead to wrong results if used on overlapping subsets of the structure.
I won't cover lensProduct
because I haven't used it recently. Let's look at adjoin
.
I have an AST with a bunch of top-level declarations:
data Module = Module
{ functions :: [Function]
, types :: [Type]
, classes :: [Class]
}
Each of Function
, Type
, Class
has a field name
. Yeah, this is with -XDuplicateRecordFields enabled. I want to write a traversal that goes through names of all top-level entities — maybe so that you can gather them, maybe so that you can rename them all at once.
Without adjoin
, it's kinda meh. Life becomes dull, washed out.
With adjoin
, this is super easy.
import Data.Generics.Labels () -- to get #blah lenses
allTopLevelNames :: Traversal' Module String
allTopLevelNames =
(#functions . each . #name) `adjoin`
(#types . each . #name) `adjoin`
(#classes . each . #name)