Consider this example (from https://codereview.stackexchange.com/questions/23456/crtitique-my-haskell-function-capitalize):
import Data.Char
capWord [] = []
capWord (h:t) = toUpper h : map toLower t
capitalize = unwords . map capWord . words
Is there a good way to abstract over the "back and forth" transformation, e.g. unwords . f . words
? The best I could come up was
class Lift a b | a -> b where
up :: a -> b
down :: b -> a
instance Lift String [String] where
up = words
down = unwords
lifted :: (Lift a b) => (b -> b) -> a -> a
lifted f = down . f . up
capitalize = lifted (map capWord)
but it feels not very flexible, and needs MultiParamTypeClasses
, FunctionalDependencies
, TypeSynonymInstances
and FlexibleInstances
- which might be an indicator that it goes slightly over the top.
Your lifted
is actually the same as dimap
from Data.Profunctor
:
onWords = dimap words unwords
capitalize = onWords (map capWord)
That might not be the direction of generalization you thought about. But look at the type of the equivalent function in Control.Functor
from category-extras
:
dimap :: Bifunctor f (Dual k) k k => k b a -> k c d -> k (f a c) (f b d)
This version generalizes it over everything which is both a QFunctor
and a co-PFunctor
. Not that useful in everyday scenarios, but interesting.
I'd say the best answer is "no, because abstracting over that doesn't buy you anything". In fact your solution is far less flexible: there can be only one instance of Lift String [String]
in scope and there are more ways to split string into a list of strings than just words/unwords
(which means you'll start throwing newtypes or even more arcane extensions into the mix). Keep it simple — the original capitalize
is just fine the way it is.
Or, if you really insist:
lifted :: (a -> b, b -> a) -> (b -> b) -> a -> a
lifted (up, down) f = down . f . up
onWords = lifted (words, unwords)
onLines = lifted (lines, unlines)
capitalize = onWords $ map capWord
Conceptually the same thing as your typeclass, except without abusing typeclass machinery so much.
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