-
Notifications
You must be signed in to change notification settings - Fork 0
/
Component2.hs
343 lines (257 loc) · 9.62 KB
/
Component2.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
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
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
{-# LANGUAGE GADTs #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE FunctionalDependencies #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE StandaloneDeriving #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE ConstraintKinds #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TypeFamilyDependencies #-}
{-# LANGUAGE ScopedTypeVariables #-}
module Component2 where
import qualified Data.Map as Map
import Data.Map(Map)
import Data.Word(Word32,Word64)
import Data.Registry
-- import Data.Typeable
import Type.Reflection(TypeRep(..),typeOf)
import Data.Type.Equality(testEquality)
import Data.Constraint(Constraint,Dict(..),withDict)
-- ======================================================
-- The Name class associates a unique name to a type and
-- to a unique Module with that type.
class Name n t | n -> t where
open :: Nm n -> t
encap:: Nm n -> Module t
encap nm = Module nm (open nm)
-- ======================================================
-- The Reify class associates a unique name 'n' to a
-- type 't', which can have an arbitrary class constraint 'c'
-- This allows us to lift a class to piece of data which
-- encodes the same information. There are two ways one might
-- use this:
-- 1) the data can be 'Dict' from Data.Constraint. (easy to do)
-- 2) the data can be a record structure. (better for type inference)
class Reify n c t | n -> c, n -> t where
reify :: c => Nm n -> t
-- =============================================
-- Component Names (Uninhabited Types)
-- The idea is to build a class Name instance indexed by each of these uninhabited types.
-- Since these types are unique, there can only be one instance at that type.
-- That instance can be thought of as naming a Type with a unique name.
-- Usually the type is a record structure with a bunch of functions and values
-- that can be thought of as a Module.
data LongHash
data ShortHash
data Mock
data JustAda
data MultiAsset
data Shelley
data Goguen
-- A singleton type, one value constructor for each component name. Thus a value
-- (Nm t) like ShortHash or MultiAsset uniquely identifies an instance, and is a
-- first class value, that can be passed around to identitfy Modules.
data Nm t where
LongHash :: Nm LongHash
ShortHash :: Nm ShortHash
Mock :: Nm Mock
JustAda :: Nm JustAda
MultiAsset :: Nm MultiAsset
Shelley :: Nm Shelley
Goguen :: Nm Goguen
deriving instance Show (Nm t)
-- ===============================================================================
-- A Module encapsulate both a Name and its Type (usually a Record of operations)
-- This is a first class value, that can be passed around, returned for functions
-- and 'open'ed to extract the operations of the Module.
data Module t where
Module:: Name n t => Nm n -> t -> Module t
-- ========================================
-- What can be signed?
class Signable a where
instance Signable Word32 where
instance Signable Coin where
instance Signable Word64 where
instance Signable Value where
-- ===================
-- Crypto Modules
data Crypto publickey secretkey signature = Crypto
{ sign:: forall a . Signable a => a -> secretkey -> signature
, verify:: forall a . Signable a => publickey -> a -> signature -> Either String ()
, keysize :: Int
, makepair :: IO (publickey,secretkey)
}
-- We can create a concrete value of type (Crypto a b c)
longHash :: Crypto Word64 Word64 Word64
longHash = Crypto
{ sign = \ x secret -> undefined
, verify = \ public a signature -> undefined
, keysize = 64
, makepair = pure (undefined,undefined)
}
-- Assign a unigue name to this value
instance Name LongHash (Crypto Word64 Word64 Word64) where
open LongHash = longHash
-- We can also assign a unique name to an anonymous value, but now its not really anonymous
-- since we can 'open' the name to get the value back!
instance Name ShortHash (Crypto Word32 Word32 Word32) where
open ShortHash = Crypto
{ sign = \ x secret -> undefined
, verify = \ public a signature -> undefined
, keysize = 32
, makepair = pure (undefined,undefined)
}
-- We can 'open' a Crypto module and use its contents. The type shown can be infered
-- foo:: (Name n (Crypto t t2 t1), Signable a) => Nm n -> a -> (Int, t2 -> t1)
foo mod y = (keysize + 18, sign y)
where Crypto{..} = open mod
-- ==================================
-- Concrete Asset types
newtype Coin = Coin Int deriving Show
data Value = Value Coin (Map String Integer)
-- ==================================
-- MultiAsset Modules
data Asset value = Asset
{ zero :: value
, plus :: value -> value -> value
, comment :: String
}
instance Name JustAda (Asset Coin) where
open JustAda =
Asset { zero = Coin 0
, plus = \ (Coin x) (Coin y) -> Coin(x+y)
, comment = "Coin assets"
}
instance Name MultiAsset (Asset Value) where
open MultiAsset = Asset zero plus comment
where zero = Value (Coin 0) Map.empty
plus (Value (Coin x) xs) (Value (Coin y) ys) = Value (Coin (x+y)) (Map.unionWith (+) xs ys)
comment = "Multi assets"
-- =======================================================================
-- Era Modules. An Era specifies a Crypto and Asset choice of operations
data Era t1 t2 t3 v = Era
{ crypto :: Crypto t1 t2 t3
, asset :: Asset v
}
-- Lets Name a couple of Era's
instance Name Shelley (Era Word32 Word32 Word32 Coin) where
open Shelley = Era (open ShortHash) (open JustAda)
instance Name Goguen (Era Word64 Word64 Word64 Value) where
open Goguen = Era (open LongHash) (open MultiAsset)
-- We can make an IO(), a main function, from an Era
makeMain:: forall x y z a . Signable a => Crypto x y z -> Asset a -> IO ()
makeMain c v = do
(public,secret) <- makepair
let foo = sign zero secret
pure ()
where Crypto{..} = c
Asset{..} = v
makeMain2 c v = do
(public,secret) <- makepair
let foo = sign zero secret
pure ()
where Crypto{..} = open c
Asset{..} = open v
main1 = makeMain crypto asset where Era{..} = open Shelley
main2 = makeMain crypto asset where Era{..} = open Goguen
-- ==========================================================
-- What is really cool is that this is completely compatible
-- with Data.Registry
registry =
val (encap LongHash)
+: val (open ShortHash)
+: val (open JustAda)
+: val (encap MultiAsset)
+: val (encap Shelley)
+: val (encap Goguen)
+: fun (makeMain @Word32 @Word32 @Word32 @Coin)
+: end
main3 = make @(IO ()) registry
-- ===========================================
-- Show instances
instance Show (Crypto a b c) where
show Crypto{..} = "Crypto "++show keysize
instance Show (Asset v) where
show Asset{..} = comment
instance Show (Era t1 t2 t3 v) where
show Era{..} = "Era: "++show crypto++", "++show asset
instance Show (Module t) where
show (Module nm x) = "Module "++show nm
-------------------------------------------------------------
data Exists f where
Hide:: TypeRep a -> f a -> Exists f
-- =============================================
class MAsset value where
mzero :: value
mplus :: value -> value -> value
mcomment :: forall proxy . proxy value -> String
instance (MAsset Coin) where
mzero = Coin 0
mplus = \ (Coin x) (Coin y) -> Coin(x+y)
mcomment _ = "Coin assets"
instance Reify Mock (MAsset Coin) (Asset Coin) where
reify Mock = Asset mzero mplus (mcomment ([]::[Coin]))
instance Reify JustAda (MAsset Coin) (Dict (MAsset Coin)) where
reify JustAda = Dict
-- bar :: (c, Reify n c (Asset value)) => Nm n -> value
bar m = plus zero zero
where Asset{..} = reify m
-- =============================================
-- Example with Associated Type Classes
class MultAsset t where
data Ass t :: *
nzero :: Ass t
nplus :: Ass t -> Ass t -> Ass t
ncomment :: Nm t -> String
instance (MultAsset Shelley) where
newtype Ass Shelley = C Coin deriving Show
nzero = C (Coin 0)
nplus = \ (C (Coin x)) (C (Coin y)) -> C(Coin(x+y))
ncomment Shelley = "Coin assets"
instance Reify Shelley (MultAsset Shelley) (Asset (Ass Shelley)) where
reify Shelley = Asset nzero nplus (ncomment Shelley)
unC (C x) = x
-- =============================================
-- 2nd Example with Associated Type Classes using 'type' instead of 'data'
class MultAsset2 t where
type Ass2 t :: *
nzero2 :: Nm t -> Ass2 t
nplus2 :: Nm t -> Ass2 t -> Ass2 t -> Ass2 t
ncomment2 :: Nm t -> String
instance (MultAsset2 Goguen) where
type Ass2 Goguen = Coin
nzero2 Goguen = Coin 0
nplus2 Goguen (Coin x) (Coin y) = Coin(x+y)
ncomment2 Goguen = "Coin assets"
instance Reify Goguen (MultAsset2 Goguen) (Asset Coin) where
reify Goguen = Asset (nzero2 Goguen) (nplus2 Goguen) (ncomment2 Goguen)
-- ========================================================
data ClassRep (c::Constraint) where
Show :: TypeRep t -> ClassRep (Show t)
MAsset :: MAsset t => TypeRep t -> ClassRep(MAsset t)
instance Show (ClassRep c) where
show (Show x) = "Show "++show x
show (MAsset x) = "MAsset "++show x
intrep:: TypeRep Int
intrep = typeOf(7)
coinrep:: TypeRep Coin
coinrep = typeOf(undefined)
shelley:: TypeRep Shelley
shelley = typeOf(undefined)
data Lib c where
Lib :: c => ClassRep(c) -> Lib c
instance Show (Lib c) where
show (Lib classrep) = "Lib ("++show classrep++")"
using :: forall c a. Lib c -> (c => a) -> a
using (Lib y) x = x
{-
class Reify2 c where
using :: Lib c -> (c => a) -> a
instance Reify2 (MAsset Coin) where
using (Lib (MAsset coinrep)) x = x
-}
-- baz:: TypeRep c -> c
baz c = using (Lib (MAsset c)) (mplus mzero mzero)