-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathOneClass.hs
More file actions
201 lines (165 loc) · 6.04 KB
/
Copy pathOneClass.hs
File metadata and controls
201 lines (165 loc) · 6.04 KB
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
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
module Gist.OneClass
( Gist(..)
, Gister(..)
, Showily(..)
, ConfigPrintf(..)
, ConfigMaybe(..)
, ConfigList(..)
, ConfigSet(..)
, MyFloat(..)
, defaultConfGister
, defaultConfGisterF
, runGister
, record
) where
import Data.Kind ( Constraint
, Type
)
import Data.Maybe ( isJust )
import qualified Data.Set as Set
import Data.Set ( Set )
import Data.Void ( Void
, absurd
)
import GHC.Generics ( Generic )
import Prettyprinter
import qualified Text.Printf as Printf
newtype Prec = Prec Int
deriving newtype (Eq, Ord, Num)
class Gist a where
{-# MINIMAL gist | gistPrec #-}
type Config a :: Type
type Config a = ()
type HasDefaultConfig a :: Constraint
type HasDefaultConfig a = ()
defaultConfig :: HasDefaultConfig a => Config a
default defaultConfig :: (Config a ~ ()) => Config a
defaultConfig = ()
gistPrec :: Prec -> Config a -> a -> Doc ann
gist :: Config a -> a -> Doc ann
gistPrecF
:: HasDefaultConfig a => Prec -> (Config a -> Config a) -> a -> Doc ann
gistF :: HasDefaultConfig a => (Config a -> Config a) -> a -> Doc ann
gistPrec_ :: HasDefaultConfig a => Prec -> a -> Doc ann
gist_ :: HasDefaultConfig a => a -> Doc ann
gist = gistPrec 0
gistPrec _ = gist
gistF f = gist (f $ defaultConfig @a)
gistPrecF p f = gistPrec p (f $ defaultConfig @a)
gist_ = gist (defaultConfig @a)
gistPrec_ prec = gistPrec prec (defaultConfig @a)
-- brittany doesn't handle GADT syntax for this.
-- data Gister a where
-- FnGister :: (forall ann . Int -> a -> Doc ann) -> Gister a
-- ConfGister :: Gist a => Config a -> Gister a
data Gister a
= FnGister (forall ann . Prec -> a -> Doc ann)
| Gist a => ConfGister (Config a)
-- cannot derive generic
defaultConfGister :: forall a . (Gist a, HasDefaultConfig a) => Gister a
defaultConfGister = ConfGister $ defaultConfig @a
defaultConfGisterF
:: forall a
. (Gist a, HasDefaultConfig a)
=> (Config a -> Config a)
-> Gister a
defaultConfGisterF f = ConfGister $ f $ defaultConfig @a
runGisterPrec :: Prec -> Gister a -> a -> Doc ann
runGisterPrec prec = \case
FnGister f -> f prec
ConfGister c -> gistPrec prec c
runGister :: Gister a -> a -> Doc ann
runGister = runGisterPrec 0
newtype Showily a = Showily a
instance Show a => Gist (Showily a) where
type Config (Showily a) = ()
type HasDefaultConfig (Showily a) = ()
gistPrec (Prec prec) _ (Showily a) = pretty $ showsPrec prec a ""
instance Gist Void where
gist _ = absurd
instance Gist () where
gist _ _ = "()"
data ConfigMaybe a = ConfigMaybe
{ showConstructors :: Bool
, gistElem :: Gister a
}
deriving stock Generic
instance Gist (Maybe a) where
type Config (Maybe a) = ConfigMaybe a
type HasDefaultConfig (Maybe a) = (Gist a, HasDefaultConfig a)
defaultConfig = ConfigMaybe False (ConfGister $ defaultConfig @a)
gistPrec prec (ConfigMaybe {..}) = if showConstructors
then \case
Nothing -> "Nothing"
Just x -> parensIf (prec > 10) $ "Just" <+> runGisterPrec 11 gistElem x
else \case
Nothing -> "_"
Just x -> runGisterPrec prec gistElem x
data ConfigPrintf = ConfigPrintf
{ printfFmt :: Maybe String
}
deriving stock Generic
newtype Printfily a = Printfily a
instance (Show a, Printf.PrintfArg a) => Gist (Printfily a) where
type Config (Printfily a) = ConfigPrintf
defaultConfig = ConfigPrintf Nothing
gist (ConfigPrintf {..}) (Printfily a) = case printfFmt of
Nothing -> viaShow a
Just fmt -> pretty (Printf.printf fmt a :: String)
deriving via Printfily Int instance Gist Int
deriving via Printfily Float instance Gist Float
deriving via Printfily Double instance Gist Double
-- | Demonstrate that newtype deriving works.
newtype MyFloat = MyFloat Float
deriving newtype (Floating, Fractional, Num, Show, Gist)
data ConfigList a = ConfigList
{ gistElem :: Gister a
, showFirst :: Maybe Int
}
deriving stock Generic
instance Gist [a] where
type Config [a] = ConfigList a
type HasDefaultConfig [a] = (Gist a, HasDefaultConfig a)
defaultConfig = ConfigList (ConfGister $ defaultConfig @a) Nothing
gistPrec _ (ConfigList {..}) xs =
let elems = case showFirst of
Nothing -> runGister gistElem <$> xs
Just n -> case splitAt n xs of
(start, [] ) -> runGister gistElem <$> start
(start, _ : _) -> (runGister gistElem <$> start) ++ ["..."]
in align $ list elems
data ConfigSet a = ConfigSet
{ gistElem :: Gister a
}
deriving stock Generic
instance Gist (Set a) where
type Config (Set a) = ConfigSet a
type HasDefaultConfig (Set a) = (Gist a, HasDefaultConfig a)
defaultConfig = ConfigSet (ConfGister $ defaultConfig @a)
gistPrec _ (ConfigSet {..}) xs =
group
$ encloseSep (flatAlt "{ " "{") (flatAlt " }" "}") ", "
$ (runGister gistElem <$> Set.toList xs)
data ConfigTuple2 a b = ConfigTuple2
{ gistFst :: Gister a
, gistSnd :: Gister b
}
deriving stock Generic
instance Gist (a, b) where
type Config (a, b) = ConfigTuple2 a b
type HasDefaultConfig (a, b)
= (Gist a, Gist b, HasDefaultConfig a, HasDefaultConfig b)
defaultConfig = ConfigTuple2 (ConfGister $ defaultConfig @a)
(ConfGister $ defaultConfig @b)
gistPrec _ (ConfigTuple2 {..}) (a, b) =
tupled [runGister gistFst a, runGister gistSnd b]
parensIf :: Bool -> Doc ann -> Doc ann
parensIf cond = if cond then parens else id
record :: Prec -> Maybe (Doc ann) -> [(Doc ann, Doc ann)] -> Doc ann
record prec mConstr fields =
parensIf (prec > 10 && isJust mConstr)
$ maybe id (\constr contents -> constr <+> align contents) mConstr
$ group
$ encloseSep (flatAlt "{ " "{") (flatAlt "\n}" "}") ", "
$ flip map fields
$ \(key, val) -> key <+> "=" <+> val