One would like to be able to find all language extensions in a given Haskell project in order to populate the other-extensions field of the .cabal file (cabal-install uses this for dependency resolution).

To extract extensions from pragmas, we use Jacinda to handle patterns and fields, viz.

ja '~.{%/LANGUAGE\s*.*\s*#-/}{`3}' -i HS_SRC_FILE

The regex /LANGUAGE\s*.*\s*#-/ filters to lines with a LANGUAGE pragma and the ~. operator deduplicates.

We can run this on a whole project with fd, viz.

fd '\.(x|chs|hs|y)$' . -x ja '~.{%/LANGUAGE\s*.*\s*#-/}{`3}' -i | ja '~.$0'

Note the second invocation ja '~.$0' to deduplicate.

We can do better.

{-#LANGUAGE DerivingVia, GeneralizedNewtypeDeriving#-}

is valid Haskell but is not handled by the above regular expression.

Jacinda has support for capture groups with ~*. From the manpages:

~* Match, returning nth capture group Str -> Int -> Regex -> Option Str

With this in mind:


fn findExtensions(line) := let val extStr ≔ line ~* 1 /{-#\s*LANGUAGE\s*(.*)#-}/ val extList ≔ (\s.split s /,\s*/)"extStr in extList end;

~.(λx.(intercalate'\n')"(findExtensions x)):?$0

We can invoke this on all files in a project like so:

fd '\.(x|chs|hs|y)$' . -x ja run hsExtensions.jac -i | ja '~.$0'

This will produce something like

FlexibleContexts OverloadedStrings DeriveAnyClass DeriveGeneric TupleSections TemplateHaskell DeriveDataTypeable StandaloneDeriving GeneralizedNewtypeDeriving DeriveFunctor FlexibleInstances

which is precisely what we need.