I was ruminating on Semigroups this morning.
In Haskell, a Semigroup
is a combination of a type and a function taking two of that type and returning a new one, where the function is associative.
Let’s see if some different things are semigroups:
* Int
and +
: (x + y) + z == x + (y + z)
:heavy_check_mark:
* Int
and *
: (x * y) * z == x * (y * z)
:heavy_check_mark:
* Int
and /
: (x / y) / z != x / (y / z)
:heavy_multiplication_x:
* String
and ++
(concatenation): (x ++ y) ++ z == x ++ (y ++ z)
:heavy_check_mark:
* Maybe String
and concatenation whenever there is a value:(Just "ab" <> Nothing) <> Just "cd" == Just "ab" <> Just "cd" == Just "abcd"
Just "ab" <> (Nothing <> Just "cd") == Just "ab" <> Just "cd" == Just "abcd"
:heavy_check_mark:
In Haskell <>
generalises all of these operations.
Yesterday @Yura showed us some code like long "verbose" <> short 'v' <> help "Enable verbose mode"
Even if you don’t know what type it’s dealing with, you still know it’s joining them all together, and any part of it can be refactored out. So without any anxiety at all, we can change it to
let verboseOptions = long "verbose" <> short 'v'
in verboseOptions <> help "Enable verbose mode"
Compare with, for example, joinOptionModifiers (long "verbose") (joinOptionModifiers (short 'v') (help "Enable verbose mode"))
where you’d have to look up the definition of joinOptionModifiers
to know what its return type is, or if it can be refactored in any way.