/
TH.hs
162 lines (155 loc) · 5.32 KB
/
TH.hs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
-- | 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 :: 'Data.Profunctor.Product.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 ('Data.Profunctor.Product.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 :: 'Control.Applicative.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
--
-- @
-- instance 'Data.Profunctor.Product.Newtype.Newtype' Foo where
-- 'Data.Profunctor.Product.Newtype.constructor' = Foo
-- 'Data.Profunctor.Product.Newtype.field' = \\(Foo x) -> x
-- @
--
-- which allows you to use the polymorphic function 'Data.Profunctor.Product.Newtype.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 :: 'Control.Applicative.Applicative' f
-- => Foo (f a) (f b) (f c) -> f (Foo a b c)
-- pFooApplicative f = Foo 'Control.Applicative.<$>' foo f
-- 'Control.Applicative.<*>' bar f
-- 'Control.Applicative.<*>' baz f
-- @
--
-- whereas for a @ProductProfunctor@ we write
--
-- @
-- import "Data.Profunctor" ('Data.Profunctor.lmap')
-- import "Data.Profunctor.Product" (('Data.Profunctor.Product.***$'), ('Data.Profunctor.Product.****'))
--
-- pFoo :: 'Data.Profunctor.Product.ProductProfunctor' p
-- => Foo (p a a') (p b b') (p c c') -> p (Foo a b c) (Foo a' b' c')
-- pFoo f = Foo 'Data.Profunctor.Product.***$' 'Data.Profunctor.lmap' foo (foo f)
-- 'Data.Profunctor.Product.****' 'Data.Profunctor.lmap' bar (bar f)
-- 'Data.Profunctor.Product.****' 'Data.Profunctor.lmap' baz (baz f)
-- @
--
-- The 'Default' instance is then very simple.
--
-- @
-- instance ('Data.Profunctor.Product.ProductProfunctor' p, 'Data.Profunctor.Product.Default.Default' p a a', 'Data.Profunctor.Product.Default.Default' p b b', 'Data.Profunctor.Product.Default.Default' p c c')
-- => 'Data.Profunctor.Product.Default.Default' p (Foo a b c) (Foo a' b' c') where
-- 'Data.Profunctor.Product.Default.def' = pFoo (Foo 'Data.Profunctor.Product.Default.def' 'Data.Profunctor.Product.Default.def' 'Data.Profunctor.Product.Default.def')
-- @
module Data.Profunctor.Product.TH where
import Data.Profunctor.Product.Internal.TH (makeAdaptorAndInstanceI)
import qualified Language.Haskell.TH as TH
-- | For example
--
-- @
-- \$(makeAdaptorAndInstanceInferrable \"pFoo\" ''Foo)
-- @
--
-- generates the 'Default' instance and the adaptor @pFoo@.
makeAdaptorAndInstanceInferrable :: String -> TH.Name -> TH.Q [TH.Dec]
makeAdaptorAndInstanceInferrable adaptorNameS =
makeAdaptorAndInstanceI True (Just adaptorNameS)
-- | 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\".
makeAdaptorAndInstanceInferrable' :: TH.Name -> TH.Q [TH.Dec]
makeAdaptorAndInstanceInferrable' =
makeAdaptorAndInstanceI True Nothing
-- | Use 'makeAdaptorAndInstanceInferrable' instead, because it
-- generates instances with better inference properties. Will be
-- deprecated in version 0.12.
makeAdaptorAndInstance :: String -> TH.Name -> TH.Q [TH.Dec]
makeAdaptorAndInstance adaptorNameS =
makeAdaptorAndInstanceI False (Just adaptorNameS)
-- | Use 'makeAdaptorAndInstanceInferrable' instead, because it
-- generates instances with better inference properties. Will be
-- deprecated in version 0.12.
makeAdaptorAndInstance' :: TH.Name -> TH.Q [TH.Dec]
makeAdaptorAndInstance' =
makeAdaptorAndInstanceI False Nothing