Suppose in my application I have a set of functions that have very regular implementations (like specific logging functions). I have a type
data ShowFns =
{ showFn1 :: Int -> Bool -> Double -> String
, showFn2 :: Double -> Char -> String
}
Both of these could be implemented simply as
showFn1' :: Int -> Bool -> Double -> String
showFn1' a b c = show a <> " " <> show b <> " " <> show c
showFn2' :: Double -> Char -> String
showFn2' a b = show a <> " " <> show b
fnCollection :: ShowFns
fnCollection = ShowFns showFn1' showFn2'
However, it seems like this repetitive pattern could be derived from a recursive type where all the leaf types have Show
instances.
I would rather write:
fnCollection :: ShowFns
fnCollection = ShowFns toShowFn toShowFn
I have a sense this is possible because this is sorta how the Servant machinery works but servant is a bit more complicated and I haven't been able to replicate that machinery down to my simpler example. I have tried a few formulations with open type families but I can't seem to get the type system to reconcile the recursive case ShowMe
and the base case ()
.
The standard trick for implementing variadic functions looks like this:
{-# LANGUAGE FlexibleInstances #-}
class ShowMe a where showMeRaw :: [String] -> a
instance ShowMe String where showMeRaw = unwords . reverse
instance (Show a, ShowMe b) => ShowMe (a -> b) where
showMeRaw ss a = showMeRaw (show a : ss)
One then normally adds a single top-level helper:
showMe :: ShowMe a => a
showMe = showMeRaw []
Then your showFun1'
and showFun2'
would both be just calls to showMe
. Try it out in ghci:
> (showMe :: Int -> Bool -> Double -> String) 3 True 2.5
"3 True 2.5"
> (showMe :: Double -> Char -> String) 2.5 'a'
"2.5 'a'"
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With