File: Boilerplate.hs

package info (click to toggle)
haskell-generic-data 1.1.0.1-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 304 kB
  • sloc: haskell: 2,577; makefile: 6
file content (150 lines) | stat: -rw-r--r-- 3,542 bytes parent folder | download | duplicates (2)
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
{-# LANGUAGE
  FlexibleInstances,
  TemplateHaskell #-}

module Inspection.Boilerplate where

import Control.Applicative (liftA2)
import Language.Haskell.TH

import Generic.Data

{- Example output this generates (modulo reordering):
eqEmptyR, eqEmptyS, eqEmptyG :: Empty a -> Empty a -> Bool
eqEmptyR = \_ _ -> True
eqEmptyS = (==)
eqEmptyG = geq
-}

class AppendQ q where
  ($++) :: q -> DecsQ -> DecsQ

  infixr 2 $++

instance AppendQ (Q Dec) where
  ($++) = liftA2 (:)

instance AppendQ (Q [Dec]) where
  ($++) = liftA2 (++)

instance AppendQ q => AppendQ [q] where
  ps $++ qs = foldr ($++) qs ps

type Top = Name -> ExpQ -> DecsQ

mk_ :: String -> Maybe Name -> Name -> (TypeQ -> TypeQ) -> Top
mk_ bname fname_ gname ty_ tname ref = do
  nameR <- newName (bname ++ nameBase tname ++ "R")  -- Reference
  nameS <- newName (bname ++ nameBase tname ++ "S")  -- Stock
  nameG <- newName (bname ++ nameBase tname ++ "G")  -- Generic
  let ty = ty_ (conT tname)
      stock = case fname_ of
        Nothing -> pure []
        Just fname ->
              sigD nameS ty
          $++ funD' nameS (varE fname)
          $++ pure []
  (     sigD nameR ty
    $++ sigD nameG ty
    $++ funD' nameR ref
    $++ funD' nameG (varE gname)
    $++ stock
    $++ pure [] )

funD' :: Name -> ExpQ -> DecQ
funD' name body = funD name [clause [] (normalB body) []]

--

newVar :: String -> Q TypeQ
newVar x = varT <$> newName x

-- Eq and Ord

-- Sometimes there isn't an Eq constraint on the parameter.
mk_eq_ :: (TypeQ -> TypeQ) -> Top
mk_eq_ = mk_ "eq" (Just '(==)) 'geq

mk_eq :: Top
mk_eq = mk_eq_ ty where
  ty f = do
    a <- newVar "a"
    [t| Eq $a => $f $a -> $f $a -> Bool |]

mk_eq' :: Top
mk_eq' = mk_eq_ ty where
  ty f = do
    a <- newVar "a"
    [t| $f $a -> $f $a -> Bool |]

-- Sometimes there isn't an Ord constraint on the parameter.
mk_compare_ :: (TypeQ -> TypeQ) -> Top
mk_compare_ = mk_ "compare" (Just 'compare) 'gcompare

mk_compare :: Top
mk_compare = mk_compare_ ty where
  ty f = do
    a <- newVar "a"
    [t| Ord $a => $f $a -> $f $a -> Ordering |]

mk_compare' :: Top
mk_compare' = mk_compare_ ty where
  ty f = do
    a <- newVar "a"
    [t| $f $a -> $f $a -> Ordering |]

-- Functor, Foldable, Traversable

mk_fmap :: Top
mk_fmap = mk_ "fmap" (Just 'fmap) 'gfmap ty where
  ty f = do
    a <- newVar "a"
    b <- newVar "b"
    [t| ($a -> $b) -> $f $a -> $f $b |]

mk_foldMap :: Top
mk_foldMap = mk_ "foldMap" (Just 'foldMap) 'gfoldMap ty where
  ty f = do
    a <- newVar "a"
    m <- newVar "m"
    [t| Monoid $m => ($a -> $m) -> $f $a -> $m |]

mk_foldr :: Top
mk_foldr = mk_ "foldr" (Just 'foldr) 'gfoldr ty where
  ty f = do
    a <- newVar "a"
    b <- newVar "b"
    [t| ($a -> $b -> $b) -> $b -> $f $a -> $b |]

mk_traverse :: Top
mk_traverse = mk_ "traverse" (Just 'traverse) 'gtraverse ty where
  ty f = do
    a <- newVar "a"
    b <- newVar "b"
    g <- newVar "g"
    [t| Applicative $g => ($a -> $g $b) -> $f $a -> $g ($f $b) |]

mk_sequenceA :: Top
mk_sequenceA = mk_ "sequenceA" (Just 'sequenceA) 'gsequenceA ty where
  ty f = do
    a <- newVar "a"
    g <- newVar "g"
    [t| Applicative $g => $f ($g $a) -> $g ($f $a) |]

-- Applicative (no stock deriving)

mk_ap :: Top
mk_ap = mk_ "ap" Nothing 'gap ty where
  ty f = do
    a <- newVar "a"
    b <- newVar "b"
    [t| $f ($a -> $b) -> $f $a -> $f $b |]

mk_liftA2 :: Top
mk_liftA2 = mk_ "liftA2" Nothing 'gliftA2 ty where
  ty f = do
    a <- newVar "a"
    b <- newVar "b"
    c <- newVar "c"
    [t| ($a -> $b -> $c) -> $f $a -> $f $b -> $f $c |]