[Haskell-cafe] Avoiding code-duplication for what is roughly fmap
Tom Ellis
tom-lists-haskell-cafe-2017 at jaguarpaw.co.uk
Sat Oct 5 14:40:55 UTC 2019
On Fri, Oct 04, 2019 at 08:38:33AM +0100, Tom Ellis wrote:
> On Thu, Oct 03, 2019 at 04:55:24PM -0400, Sebastiaan Joosten wrote:
> > I'm writing a lot of code that looks like this:
> > instance WhiteSpaced ClassItem where
> > removeWS (Method a b c) = Method (removeWS a) (removeWS b) (removeWS c)
> > removeWS (Declaration b) = Declaration (removeWS b)
>
> I'll give you an example using product-profunctors, but could you provide
> the class and data type definitions? I'd like the example to actually
> reflect the reality of the situation you are dealing with.
It sounds like you've already got a satisfactory implementation using
generics. I'll give a sketch of the product-profunctors version. It is,
itself, a sort of generics library.
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE UndecidableInstances #-}
import Data.Profunctor (Profunctor, dimap)
import Data.Profunctor.Product (ProductProfunctor, SumProfunctor,
empty, (***!),
defaultEmpty, defaultProfunctorProduct)
import Data.Profunctor.Product.Default (Default, def)
-- The thing we're trying to generate
newtype WhiteSpaced a b = WhiteSpaced { removeWSExplicit :: a -> b }
-- Some data types whose implementation we won't bother giving
data Contract
data MethodHeader
data DefinitionExpr
data Balanced
-- A data type to which we're interested in giving a default
-- "WhiteSpaced" instance of some sort
data ClassItem
= Method Contract MethodHeader DefinitionExpr
| Declaration Balanced
-- The typeclass polymorphic function we're interested in
removeWS :: Default WhiteSpaced a a => a -> a
removeWS = removeWSExplicit def
-- Boilerplate instance. Generally derived by TH or generics, but
-- that's not implemented for sum types yet ...
instance ( SumProfunctor p
, ProductProfunctor p
, Default p Contract Contract
, Default p MethodHeader MethodHeader
, Default p DefinitionExpr DefinitionExpr
, Default p Balanced Balanced ) =>
Default p ClassItem ClassItem where
def = error "To be filled in -- by hand, TH, or generics"
-- Boilerplate instances. I believe they can be derived via "deriving
-- via newtype".
instance Functor (WhiteSpaced a) where
fmap f = WhiteSpaced . fmap f . removeWSExplicit
instance Applicative (WhiteSpaced a) where
pure = WhiteSpaced . pure
f <*> x = WhiteSpaced (removeWSExplicit f <*> removeWSExplicit x)
instance Profunctor WhiteSpaced where
dimap f g = WhiteSpaced . dimap f g . removeWSExplicit
instance ProductProfunctor WhiteSpaced where
empty = defaultEmpty
(***!) = defaultProfunctorProduct
More information about the Haskell-Cafe
mailing list