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
|
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
|
-
-
+
+
-
+
-
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
|
## defered through the event-loop.
#
## Programing with futures is explicit continuation-passing-style.
# ### ### ### ######### ######### #########
## Requisites
package require Debug
package require Debug::snit
package require debug
package require debug::caller
package require snit
# ### ### ### ######### ######### #########
## Narrative tracing
Debug off future
debug level future
#Debug on future
Debug prefix future {[list [Debug::snit::call]] }
debug prefix future {[debug caller] | }
# ### ### ### ######### ######### #########
## Implementation
snit::type future {
# ### ### ### ######### ######### #########
## Class API
## Simplified construction with integrated setup
# Provide new future configured with arguments. See `on ..`,
# `nop`, `empty`, and `chain`.
typemethod new {args} {
Debug.future {}
debug.future {}
set future [$type create %AUTO%]
if {[llength $args]} { $future {*}$args }
return $future
}
# Provide a new future in default configuration and trigger it to
# return the args and the next idle point. Further configuration
# can be done on the result object.
typemethod return {args} {
Debug.future {}
debug.future {}
set f [$type new]
after idle [list $f return {*}$args]
return $f
}
# ### ### ### ######### ######### #########
## API. Various configuration methods. Chainable.
# Configure callback to invoke on `return`.
method {on return} {cmdprefix args} {
Debug.future {}
debug.future {}
set myOkCmdPrefix $cmdprefix
if {[llength $args]} { $self {*}$args }
return $self
}
# Configure callback to invoke on `error`.
method {on error} {cmdprefix args} {
Debug.future {}
debug.future {}
set myErrorCmdPrefix $cmdprefix
if {[llength $args]} { $self {*}$args }
return $self
}
# Clear all callbacks. The future will now swallow all its invokations.
method nop {} {
Debug.future {}
debug.future {}
set myOkCmdPrefix {}
set myErrorCmdPrefix {}
return
}
# Same as nop.
method empty {} {
Debug.future {}
debug.future {}
set myOkCmdPrefix {}
set myErrorCmdPrefix {}
return
}
# Configure callbacks to post invokations to the other future.
method chain {future args} {
Debug.future {}
debug.future {}
# Inlined on return on error configuration chaining to another
# future.
set myOkCmdPrefix [list $future return]
set myErrorCmdPrefix [list $future error]
if {[llength $args]} { $self {*}$args }
return $self
}
# ### ### ### ######### ######### #########
## API. Invoke the future. This will destroy the instance as well.
method error {message} {
Debug.future {}
debug.future {}
$self Error $message
return
}
method return {args} {
Debug.future {}
debug.future {}
# Syntax: | 0
# : -code x | 2
# : -code x val | 3
# : val | 4
# Allowing multiple -code settings, last one is taken.
set rcode 0
|
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
|
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
|
-
+
-
+
-
+
+
|
# ### ### ### ######### ######### #########
## Destruction. A user is not allowed to destroy instances. Can
## only be done by invoking it (return or error), as part of which
## it destroys itself.
destructor {
Debug.future {}
debug.future {}
if {$myIsDone} return
return -code error "Illegal attempt to destroy unresolved future \"$self\""
}
# ### ### ### ######### ######### #########
## State
variable myIsDone 0
variable myOkCmdPrefix {return -code return}
variable myErrorCmdPrefix {return -code error}
# ### ### ### ######### ######### #########
## Helper commands. Invoke the configured callbacks, if any. This
## is also where the instance destroys itself. Note that callback
## execution is defered to the event loop.
method Ok {result} {
Debug.future {[list ==> $myOkCmdPrefix]}
debug.future {[list ==> $myOkCmdPrefix]}
set cmdprefix $myOkCmdPrefix
DestroySelf
if {![llength $cmdprefix]} return
after 0 [list {*}$cmdprefix $result]
return
}
method Error {message} {
Debug.future {[list ==> $myErrorCmdPrefix]}
debug.future {[list ==> $myErrorCmdPrefix]}
set cmdprefix $myErrorCmdPrefix
DestroySelf
if {![llength $cmdprefix]} return
after 0 [list {*}$cmdprefix $message]
return
}
proc DestroySelf {} {
debug.future {}
upvar 1 self self myIsDone myIsDone
set myIsDone 1
$self destroy
return
}
##
|