-
Notifications
You must be signed in to change notification settings - Fork 4
/
Typ.hs
275 lines (201 loc) · 7.16 KB
/
Typ.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
{-# LANGUAGE NoMonomorphismRestriction #-}
{-# LANGUAGE PatternGuards #-}
{-# LANGUAGE Rank2Types, ExistentialQuantification #-}
-- * Type representation, equality and the safe typecast:
-- * the above-the-board version of Data.Typeable
module Typ where
import Control.Monad
import Control.Monad.Error
-- * The language of type representations: first-order and typed
-- It is quite like the language of int/neg/add we have seen before,
-- but it is now typed.
-- It is first order: the language of simple types is first order
class TSYM trepr where
tint :: trepr Int
tarr :: trepr a -> trepr b -> trepr (a->b)
-- * The view interpreter
-- The first interpreter is to view the types
newtype ShowT a = ShowT String
instance TSYM ShowT where
tint = ShowT $ "Int"
tarr (ShowT a) (ShowT b) = ShowT $ "(" ++ a ++ " -> " ++ b ++ ")"
view_t :: ShowT a -> String
view_t (ShowT s) = s
(~~>) = tarr
-- * //
-- * Quantifying over the TSYM interpreter
-- This closes the type universe
newtype TQ t = TQ{unTQ :: forall trepr. TSYM trepr => trepr t}
-- TQ is itself an interpreter, the trivial one
instance TSYM TQ where
tint = TQ tint
tarr (TQ a) (TQ b) = TQ (tarr a b)
-- Sample type expressions
tt1 = (tint `tarr` tint) `tarr` tint
-- tt1 :: (TSYM trepr) => trepr ((Int -> Int) -> Int)
tt2 = tint `tarr` (tint `tarr` tint)
-- tt2 :: (TSYM trepr) => trepr (Int -> Int -> Int)
tt1_view = view_t (unTQ tt1)
-- "((Int -> Int) -> Int)"
tt2_view = view_t (unTQ tt2)
-- "(Int -> (Int -> Int))"
-- * //
-- * Show Typ-able expressions
-- * No Show type class constraint!
-- The signature is quite like gshow in a generic programming
-- library such as EMGM or LIGD
show_as :: TQ a -> a -> String
show_as tr a =
case unTQ tr of ShowAs _ f -> f a
-- The implementation of the interpreter ShowAs shows off
-- the technique of accumulating new TQ as we traverse the old
-- one. We shall see more examples later.
-- One is again reminded of attribute grammars.
data ShowAs a = ShowAs (TQ a) (a -> String)
instance TSYM ShowAs where
tint = ShowAs tint show -- as Int
tarr (ShowAs t1 _) (ShowAs t2 _) =
let t = tarr t1 t2 in
ShowAs t (\_ -> "<function of the type " ++
view_t (unTQ t) ++ ">")
tt0_show = show_as tint 5
-- "5"
tt1_show = show_as tt1 undefined
-- "<function of the type ((Int -> Int) -> Int)>"
-- We can't show functional values, but at least we should be
-- able to show their types
-- * //
-- * Type representation
-- * Compare with Dyta.Typeable.TypeRep
-- It is not a data structure!
data Typ = forall t. Typ (TQ t)
-- * //
-- * Alternative to quantification: copying
-- Before instantiating one interpreter, we fork it.
-- One copy can be instantiated, but the other remains polymorphic
-- Compare with Prolog's copy_term
-- This approach keeps the type universe extensible
-- Again the same pattern: traverse one TQ and build another
-- (another two actually)
data TCOPY trep1 trep2 a = TCOPY (trep1 a) (trep2 a)
instance (TSYM trep1, TSYM trep2)
=> TSYM (TCOPY trep1 trep2)
where
tint = TCOPY tint tint
tarr (TCOPY a1 a2) (TCOPY b1 b2) =
TCOPY (tarr a1 b1) (tarr a2 b2)
-- * //
-- * Equality and safe type cast
-- * c is NOT necessarily a functor or injective!
-- For example, repr is not always a functor
-- I wonder if we can generalize to an arbitrary type function
-- represented by its label lab:
-- newtype EQU a b = EQU (forall lab. (Apply lab a -> Apply lab b)
-- That would let us _easily_ show, for example, that
-- EQU (a,b) (c,d) implies EQU a c, for all types a, b, c, d.
newtype EQU a b = EQU{equ_cast:: forall c. c a -> c b}
-- * Leibniz equality is reflexive, symmetric and transitive
-- Here is the constructive proof
refl :: EQU a a
refl = EQU id
-- * An Unusual `functor'
tran :: EQU a u -> EQU u b -> EQU a b
tran au ub = equ_cast ub au
-- Why it works? We consider (EQU a u) as (EQU a) u,
-- and so instantiate c to be EQU a
newtype FS b a = FS{unFS:: EQU a b}
symm :: EQU a b -> EQU b a
symm equ = unFS . equ_cast equ . FS $ refl
-- Useful type-level lambdas, so to speak
newtype F1 t b a = F1{unF1:: EQU t (a->b)}
newtype F2 t a b = F2{unF2:: EQU t (a->b)}
eq_arr :: EQU a1 a2 -> EQU b1 b2 -> EQU (a1->b1) (a2->b2)
eq_arr a1a2 b1b2 =
unF2 . equ_cast b1b2 . F2 . unF1 . equ_cast a1a2 . F1 $ refl
-- How does this work? what is the type of refl above?
-- * //
-- * Decide if (trepr a) represents a type that is equal to some type b
-- Informally, we compare a value that _represents_ a type b
-- against the _type_ b
-- We do that by interpreting trepr a in a particular way
-- * A constructive `deconstructor'
data AsInt a = AsInt (Maybe (EQU a Int))
instance TSYM AsInt where
tint = AsInt $ Just refl
tarr _ _ = AsInt $ Nothing
-- This function proves useful later
as_int :: AsInt a -> c a -> Maybe (c Int)
as_int (AsInt (Just equ)) r = Just $ equ_cast equ r
as_int _ _ = Nothing
-- * Another constructive `deconstructor'
data AsArrow a =
forall b1 b2. AsArrow (TQ a) (Maybe (TQ b1, TQ b2, EQU a (b1->b2)))
instance TSYM AsArrow where
tint = AsArrow tint Nothing
tarr (AsArrow t1 _) (AsArrow t2 _) =
AsArrow (tarr t1 t2) $ Just (t1,t2,refl)
as_arrow :: AsArrow a -> AsArrow a
as_arrow = id
-- More cases could be added later on...
newtype SafeCast a = SafeCast (forall b. TQ b -> Maybe (EQU a b))
instance TSYM SafeCast where
tint = SafeCast $ \tb ->
case unTQ tb of AsInt eq -> fmap symm eq
tarr (SafeCast t1) (SafeCast t2) =
SafeCast $ \tb -> do
AsArrow _ (Just (b1,b2,equ_bb1b2)) <-
return $ as_arrow (unTQ tb)
equ_t1b1 <- t1 b1
equ_t2b2 <- t2 b2
return $ tran (eq_arr equ_t1b1 equ_t2b2) (symm equ_bb1b2)
-- * Cf. Data.Typeable.gcast
-- Data.Typeable.gcast :: (Data.Typeable.Typeable b, Data.Typeable.Typeable a) =>
-- c a -> Maybe (c b)
-- We use our own `Typeable', implemented without
-- invoking GHC internals
safe_gcast :: TQ a -> c a -> TQ b -> Maybe (c b)
safe_gcast (TQ ta) ca tb = cast ta
where cast (SafeCast f) =
maybe Nothing (\equ -> Just (equ_cast equ ca)) $ f tb
-- There is a tantalizing opportunity of making SafeCast extensible
-- * //
-- * Our own version of Data.Dynamic
-- We replace Data.Typeable with TQ a
data Dynamic = forall t. Dynamic (TQ t) t
tdn1 = Dynamic tint 5
tdn2 = Dynamic tt1 ($ 1)
tdn3 = Dynamic (tint `tarr` (tint `tarr` tint)) (*)
tdn_show (Dynamic tr a) = show_as tr a
newtype Id a = Id a
tdn_eval1 (Dynamic tr d) x = do
Id f <- safe_gcast tr (Id d) tt1
return . show $ f x
tdn_eval2 (Dynamic tr d) x y = do
Id f <- safe_gcast tr (Id d) tt2
return . show $ f x y
tdn1_show = tdn_show tdn1
-- "5"
tdn2_show = tdn_show tdn2
-- "<function of the type ((Int -> Int) -> Int)>"
tdn3_show = tdn_show tdn3
-- "<function of the type (Int -> (Int -> Int))>"
tdn1_eval = tdn_eval1 tdn1 (+4)
-- Nothing
tdn2_eval = tdn_eval1 tdn2 (+4)
-- Just "5"
tdn2_eval' = tdn_eval2 tdn2 3 14
-- Nothing
tdn3_eval = tdn_eval2 tdn3 3 14
-- Just "42"
main = do
print tt1_view
print tt2_view
print tt0_show
print tt1_show
print tdn1_show
print tdn2_show
print tdn3_show
print tdn1_eval
print tdn2_eval
print tdn2_eval'
print tdn3_eval