Safe Haskell | None |
---|---|
Language | Haskell2010 |
If you have a data declaration which is a polymorphic product, for example
data Foo a b c = Foo a b c
or
data Foo a b c = Foo { foo :: a, bar :: b, baz :: c }
then you can use Template Haskell to automatically derive the
product-profunctor Default
instances and product-profunctor
"adaptor" with the following splice:
$(makeAdaptorAndInstanceInferrable
"pFoo" ''Foo)
The adaptor for a type Foo
is by convention called pFoo
, but in
practice you can call it anything. If you don't care to specify
the name pFoo
yourself you can use
$(makeAdaptorAndInstanceInferrable'
''Foo)
and it will be named pFoo
automatically.
pFoo
will have the type
pFoo :: ProductProfunctor
p
=> Foo (p a a') (p b b') (p c c')
-> p (Foo a b c) (Foo a' b' c')
and the instance generated will be
instance (ProductProfunctor
p, Default p a a', Default p b b', Default p c c')
=> Default p (Foo a b c) (Foo a' b' c')
If you are confused about the meaning of pFoo
it may help to
consider the corresponding function that works with Applicative
s
(its implementation is given below).
pFooApplicative :: Applicative
f
=> Foo (f a) (f b) (f c)
-> f (Foo a b c)
The product-profunctor "adaptor" (in this case pFoo
) is a
generalization of Data.Traversable.sequence
in two different
ways. Firstly it works on datatypes with multiple type parameters.
Secondly it works on ProductProfunctor
s, which are themselves a
generalization of Applicative
s.
If your type has only one field, for example
data Foo a = Foo a
or
newtype Foo a = Foo a
then you will also get the instance
instanceNewtype
Foo whereconstructor
= Foofield
= \(Foo x) -> x
which allows you to use the polymorphic function pNewtype
instead of pFoo
.
If you prefer not to use Template Haskell then the generated code
can be written by hand because it is quite simple. It corresponds
very closely to what we would do in the more familiar
Applicative
case. For an Applicative
we would write
pFooApplicative ::Applicative
f => Foo (f a) (f b) (f c) -> f (Foo a b c) pFooApplicative f = Foo<$>
foo f<*>
bar f<*>
baz f
whereas for a ProductProfunctor
we write
import Data.Profunctor (lmap
) import Data.Profunctor.Product ((***$
), (****
)) pFoo ::ProductProfunctor
p => Foo (p a a') (p b b') (p c c') -> p (Foo a b c) (Foo a' b' c') pFoo f = Foo***$
lmap
foo (foo f)****
lmap
bar (bar f)****
lmap
baz (baz f)
The Default
instance is then very simple.
instance (ProductProfunctor
p,Default
p a a',Default
p b b',Default
p c c') =>Default
p (Foo a b c) (Foo a' b' c') wheredef
= pFoo (Foodef
def
def
)
Synopsis
- makeAdaptorAndInstanceInferrable :: String -> Name -> Q [Dec]
- makeAdaptorAndInstanceInferrable' :: Name -> Q [Dec]
- makeAdaptorAndInstance :: String -> Name -> Q [Dec]
- makeAdaptorAndInstance' :: Name -> Q [Dec]
Documentation
makeAdaptorAndInstanceInferrable :: String -> Name -> Q [Dec] Source #
For example
$(makeAdaptorAndInstanceInferrable "pFoo" ''Foo)
generates the Default
instance and the adaptor pFoo
.
makeAdaptorAndInstanceInferrable' :: Name -> Q [Dec] Source #
For example
$(makeAdaptorAndInstanceInferrable' ''Foo)
generates the Default
instance and the adaptor pFoo
. The name
of the adaptor is chosen by prefixing the type name "Foo" with
the string "p".
makeAdaptorAndInstance :: String -> Name -> Q [Dec] Source #
Use makeAdaptorAndInstanceInferrable
instead, because it
generates instances with better inference properties. Will be
deprecated in version 0.12.
makeAdaptorAndInstance' :: Name -> Q [Dec] Source #
Use makeAdaptorAndInstanceInferrable
instead, because it
generates instances with better inference properties. Will be
deprecated in version 0.12.