sebfisch (owner)

Revisions

gist: 176983 Download_button fork
public
Description:
Experiments with eval-time choice annotations for Curry
Public Clone URL: git://gist.github.com/176983.git
Embed All Files: show embed
etc.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
-- This snippet simulates naive dictionary passing for Curry type
-- classes using the explicit-sharing package for functional logic
-- programming in Haskell.
--
-- It defines a dictionary for the class
--
-- class Arb a where arb :: a
--
-- and a corresponding instance for the type Bool
--
-- instance Arb Bool where arb = False ? True
--
-- See Curry mailing list thread started by Wolfgang Lux on Aug 28, 2009.
--
-- After simulating the naive dictionary passing, we adjust the
-- sharing behaviour of the dictionary data type and investigate the
-- such non-sharing types further.
 
{-# LANGUAGE MultiParamTypeClasses, FlexibleInstances #-}
 
import Control.Monad.Sharing
import Data.Monadic.List
 
-- first we need some boilerplate for nondeterministic booleans and pairs
data Pair m a b = Pair (m a) (m b)
 
instance (Monad m, Shareable m a, Shareable m b) => Shareable m (Pair m a b)
 where
  shareArgs f (Pair x y) = return Pair `ap` f x `ap` f y
 
instance (Monad m, Convertible m a c, Convertible m b d)
      => Convertible m (Pair m a b) (c,d)
 where
  convArgs f (Pair x y) = return (,) `ap` (f =<< x) `ap` (f =<< y)
 
-- now we define the dictionary
data Arb m a = Arb (m a)
 
instance (Monad m, Shareable m a) => Shareable m (Arb m a)
 where
  -- the usual implementation of shareArgs:
  -- shareArgs f (Arb x) = return Arb `ap` f x
 
  -- an alternative version that does not share recursively
  shareArgs _ = return
 
-- arb (Arb x) = x
arb :: Monad m => m (Arb m a) -> m a
arb m = m >>= \x -> case x of Arb y -> y
 
-- instArbBool = (Arb arbBool)
instArbBool :: MonadPlus m => m (Arb m Bool)
instArbBool = return (Arb arbBool)
 
-- arbBool = False ? True
arbBool :: MonadPlus m => m Bool
arbBool = return False `mplus` return True
 
-- arbP2 da db = (arb da, arb db)
arbP2 :: Monad m => m (Arb m a) -> m (Arb m b) -> m (Pair m a b)
arbP2 da db = return (Pair (arb da) (arb db))
 
-- arbL2 da = [arb da, arb da]
arbL2 :: (Monad m, Shareable m a, Sharing m) => m (Arb m a) -> m (List m a)
arbL2 da = do d <- share da
              cons (arb d) (cons (arb d) nil)
 
-- test functions
 
arbP2Bool :: [(Bool,Bool)]
arbP2Bool = evalLazy (arbP2 instArbBool instArbBool)
 
-- with the usual Shareable instance this yields two results with the
-- version that does not share recursively it yields four.
arbL2Bool :: [[Bool]]
arbL2Bool = evalLazy (arbL2 instArbBool)
 
-- How about nesting? We define two types similar to the Arb
-- dictionary. One specifies its argument to be evaluated using
-- call-time choice, the other uses the eval-time choice with the
-- syntax proposed by Wolfgang.
--
-- data CTC a = CTC a
-- data ETC a = ETC ?a
 
data CTC m a = CTC (m a)
data ETC m a = ETC (m a)
 
-- the difference between ETC and CTC can be seen in their Shareable
-- instances
 
instance (Monad m, Shareable m a) => Shareable m (CTC m a)
 where
  -- the argument of CTC is shared recursively
  shareArgs f (CTC x) = return CTC `ap` f x
 
instance (Monad m, Shareable m a) => Shareable m (ETC m a)
 where
  -- the argument of ETC is not shared recursively
  shareArgs _ (ETC x) = return (ETC x)
 
-- Boilerlate again: in order to observe the results of computations
-- that produce values of type 'CTC a' or 'ETC a' we define a
-- corresponding non-monadic type and associated Convertible
-- instances.
 
data Res a = Res a deriving Show
 
instance (Monad m, Convertible m a b) => Convertible m (CTC m a) (Res b)
 where convArgs f (CTC x) = return Res `ap` (f =<< x)
 
instance (Monad m, Convertible m a b) => Convertible m (ETC m a) (Res b)
 where convArgs f (ETC x) = return Res `ap` (f =<< x)
 
-- here is a simple function that allows to detect sharing:
--
-- dup x = (x,x)
dup :: (Monad m, Sharing m, Shareable m a) => m a -> m (Pair m a a)
dup x = do y <- share x
           return (Pair y y)
 
-- We can observe the difference between ETC and CTC by wrapping
-- arbBool inside them and passing the result to dup.
 
ctcCoin :: MonadPlus m => m (CTC m Bool)
ctcCoin = return (CTC arbBool)
 
etcCoin :: MonadPlus m => m (ETC m Bool)
etcCoin = return (ETC arbBool)
 
-- dup (CTC arbBool) ~> two results
ctcArgsAreShared :: [(Res Bool,Res Bool)]
ctcArgsAreShared = evalLazy (dup ctcCoin)
 
-- dup (ETC arbBool) ~> four results
etcArgsAreNotShared :: [(Res Bool,Res Bool)]
etcArgsAreNotShared = evalLazy (dup etcCoin)
 
-- Here is a function that demonstrates sharing inside the ETC constructor
--
-- shareInside :: Bool -> (ETC (Bool,Bool), ETC (Bool,Bool))
-- shareInside x = dup (ETC (x,x))
shareInside :: (MonadPlus m, Sharing m)
            => m Bool
            -> m (Pair m (ETC m (Pair m Bool Bool)) (ETC m (Pair m Bool Bool)))
shareInside x = do y <- share x
                   dup (return (ETC (return (Pair y y))))
 
sharedETCargsRemainShared :: [(Res (Bool,Bool),Res (Bool,Bool))]
sharedETCargsRemainShared = evalLazy (shareInside arbBool)
 
-- Bernds examples:
 
-- data ETC2 a = ETC2 ?a ?a
-- data ETC3 a = ETC3 ?a a
 
data ETC2 m a = ETC2 (m a) (m a)
data ETC3 m a = ETC3 (m a) (m a)
 
data Res2 a = Res2 a a
 
instance (Monad m, Shareable m a) => Shareable m (ETC2 m a)
 where
  shareArgs _ (ETC2 x y) = return (ETC2 x y)
 
instance (Monad m, Shareable m a) => Shareable m (ETC3 m a)
 where
  shareArgs f (ETC3 x y) = return ETC3 `ap` return x `ap` f y
 
instance (Monad m, Convertible m a b) => Convertible m (ETC2 m a) (Res2 b)
 where
  convArgs f (ETC2 x y) = return Res2 `ap` (f =<< x) `ap` (f =<< y)
 
instance (Monad m, Convertible m a b) => Convertible m (ETC3 m a) (Res2 b)
 where
  convArgs f (ETC3 x y) = return Res2 `ap` (f =<< x) `ap` (f =<< y)
 
f2 :: (Monad m, Sharing m) => m Int -> m (ETC2 m Int)
f2 x = do y <- share x
          return (ETC2 y y)
 
f3 :: (Monad m, Sharing m) => m Int -> m (ETC3 m Int)
f3 x = do y <- share x
          return (ETC3 y y)
 
plus2 :: Monad m => m (ETC2 m Int) -> m Int
plus2 m = m >>= \ (ETC2 x y) -> liftM2 (+) x y
 
plus3 :: Monad m => m (ETC3 m Int) -> m Int
plus3 m = m >>= \ (ETC3 x y) -> liftM2 (+) x y
 
test2 :: [Int]
test2 = evalLazy (plus2 (f2 (return 0 `mplus` return 1)))
 
test3 :: [Int]
test3 = evalLazy (plus3 (f3 (return 0 `mplus` return 1)))
 
-- another example by Bernd:
 
data C m a = C (m a)
 
instance (Monad m, Shareable m a) => Shareable m (C m a)
 where
  shareArgs _ = return
 
plus :: Monad m => m (C m Int) -> m (C m Int) -> m Int
plus a b = a >>= \ (C c) ->
           b >>= \ (C d) ->
           c >>= \ x ->
           d >>= \ y ->
           return (x+y)
 
cnst :: Monad m => m a -> m b -> m a
cnst x _ = x
 
e1, e2, e3 :: (MonadPlus m, Sharing m) => m Int
e1 = share (return (C (return 0 `mplus` return 1))) >>= \x -> plus x x
 
-- y is not shared (does not occur more than once)
e2 = let y = return 0 `mplus` return 1
      in share (return (C y)) >>= \x -> plus x x
 
-- now, y is shared
e3 = share (return 0 `mplus` return 1) >>= \y ->
     share (return (C y)) >>= \x -> cnst (plus x x) y
 
-- *Main> evalLazy e1 :: [Int]
-- [0,1,1,2]
-- *Main> evalLazy e2 :: [Int]
-- [0,1,1,2]
-- *Main> evalLazy e3 :: [Int]
-- [0,2]