raskell 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,188 @@
1
+ require 'singleton'
2
+
3
+
4
+ class Foldl
5
+
6
+ alias_method :standard_ruby_kind_of?, :kind_of?
7
+
8
+ def kind_of?(clazz)
9
+ [Proc].include?(clazz) || standard_ruby_kind_of?(clazz)
10
+ end
11
+
12
+ def initialize(*monoids) # must be [fn, unit] pair, a monoid
13
+ @monoids = monoids
14
+ self
15
+ end
16
+
17
+ def monoids
18
+ @monoids
19
+ end
20
+
21
+ def *(lamb)
22
+ ->(x) { self.( lamb.( x ) ) }
23
+ end
24
+
25
+ def |(lamb)
26
+ ->(x) { lamb.( self.( x ) ) }
27
+ end
28
+
29
+ def <<(val)
30
+ self.(val.())
31
+ end
32
+
33
+ def >>(lamb)
34
+ lamb.(self)
35
+ end
36
+
37
+ def +(foldl)
38
+ if foldl.kind_of?(Foldl)
39
+ Foldl.new(*(self.monoids + foldl.monoids))
40
+ else
41
+ raise "Cannot add two non-folds together"
42
+ end
43
+ end
44
+
45
+ def call(stream)
46
+ if stream.respond_to?(:to_stream)
47
+ if @monoids.length > 1
48
+ fn = ->(acc, el) { F.zip_with.(F.apply_fn).(@monoids.map(&:first), acc, @monoids.map {|x| el }).to_a }
49
+ F.foldleft.(fn, @monoids.map(&:last)).(stream).to_a
50
+ else
51
+ F.foldleft.(*@monoids.first).(stream)
52
+ end
53
+ else
54
+ raise "Cannot call Foldl on an object that does not have to_stream defined."
55
+ end
56
+ end
57
+
58
+ @@foldl = ->(f,u) { Foldl.new([f,u])}
59
+ def self.foldl
60
+ @@foldl
61
+ end
62
+ end
63
+
64
+ class F
65
+ include Singleton
66
+ end
67
+
68
+ F.define_singleton_method(:foldl) { Foldl.foldl }
69
+
70
+ class Scanl
71
+ alias_method :standard_ruby_kind_of?, :kind_of?
72
+
73
+ def kind_of?(clazz)
74
+ [Proc].include?(clazz) || standard_ruby_kind_of?(clazz)
75
+ end
76
+
77
+ def initialize(*monoids) # must be [fn, unit] pair, a monoid
78
+ @monoids = monoids
79
+ self
80
+ end
81
+
82
+ def monoids
83
+ @monoids
84
+ end
85
+
86
+ def *(lamb)
87
+ ->(x) { self.( lamb.( x ) ) }
88
+ end
89
+
90
+ def |(lamb)
91
+ ->(x) { lamb.( self.( x ) ) }
92
+ end
93
+
94
+ def <<(val)
95
+ self.(val.())
96
+ end
97
+
98
+ def >>(lamb)
99
+ lamb.(self)
100
+ end
101
+
102
+ def +(scanl)
103
+ if scanl.kind_of?(Scanl)
104
+ Scanl.new(*(self.monoids + scanl.monoids))
105
+ else
106
+ raise "Cannot add two non-folds together"
107
+ end
108
+ end
109
+
110
+ def call(stream)
111
+ if stream.respond_to?(:to_stream)
112
+ if @monoids.length > 1
113
+ fn = ->(acc, el) { F.zip_with.(F.apply_fn).(@monoids.map(&:first), acc, @monoids.map {|x| el }).to_a }
114
+ F.scanleft.(fn, @monoids.map(&:last)).(stream)
115
+ else
116
+ F.scanleft.(*@monoids.first).(stream)
117
+ end
118
+ else
119
+ raise "Cannot call Foldl on an object that does not have to_stream defined."
120
+ end
121
+ end
122
+
123
+ @@scanl = ->(f,u) { Scanl.new([f,u])}
124
+ def self.scanl
125
+ @@scanl
126
+ end
127
+ end
128
+ F.define_singleton_method(:scanl) { Scanl.scanl }
129
+
130
+
131
+ class Mapl
132
+ alias_method :standard_ruby_kind_of?, :kind_of?
133
+
134
+ def kind_of?(clazz)
135
+ [Proc].include?(clazz) || standard_ruby_kind_of?(clazz)
136
+ end
137
+
138
+ def initialize(*functions)
139
+ @functions = functions
140
+ self
141
+ end
142
+
143
+ def functions
144
+ @functions
145
+ end
146
+
147
+ def *(lamb)
148
+ ->(x) { self.( lamb.( x ) ) }
149
+ end
150
+
151
+ def |(lamb)
152
+ ->(x) { lamb.( self.( x ) ) }
153
+ end
154
+
155
+ def <<(val)
156
+ self.(val.())
157
+ end
158
+
159
+ def >>(lamb)
160
+ lamb.(self)
161
+ end
162
+
163
+ def +(mapl)
164
+ if mapl.kind_of?(Mapl)
165
+ Mapl.new(*(self.functions + mapl.functions))
166
+ else
167
+ raise "Cannot add two non-maps together"
168
+ end
169
+ end
170
+
171
+ def call(stream)
172
+ if stream.respond_to?(:to_stream)
173
+ if @functions.length > 1
174
+ F.mapleft.(@functions).(stream)
175
+ else
176
+ F.mapleft.(@functions.first).(stream)
177
+ end
178
+ else
179
+ raise "Cannot call Mapl on an object that does not have to_stream defined."
180
+ end
181
+ end
182
+
183
+ @@map = ->(f) { Mapl.new(f)}
184
+ def self.map
185
+ @@map
186
+ end
187
+ end
188
+ F.define_singleton_method(:map) { Mapl.map }
@@ -0,0 +1,34 @@
1
+ class Identity
2
+
3
+ alias_method :standard_kind_of?, :kind_of?
4
+ def kind_of?(clazz)
5
+ clazz == Proc || standard_kind_of?(clazz)
6
+ end
7
+
8
+ def call(arg)
9
+ arg
10
+ end
11
+
12
+ def *(lamb)
13
+ lamb
14
+ end
15
+
16
+ def |(lamb)
17
+ lamb
18
+ end
19
+
20
+ def +(lamb)
21
+ ->(x) { x } + lamb
22
+ end
23
+
24
+ def <<(val)
25
+ # feed data from the right
26
+ self.(val.())
27
+ end
28
+
29
+ def >>(lamb)
30
+ # feed data from the left, assuming I am a wrapped Object of some sort
31
+ lamb.(self)
32
+ end
33
+
34
+ end
@@ -0,0 +1,23 @@
1
+ class Integer
2
+ def self.empty
3
+ 0
4
+ end
5
+
6
+ def foldl(func, unit)
7
+ i = 0
8
+ while i <= self
9
+ unit = func.(unit, i)
10
+ i+=1
11
+ end
12
+ unit
13
+ end
14
+
15
+ def foldr(func, unit)
16
+ i = self
17
+ while i >= 0
18
+ unit = func.(i, unit)
19
+ i-=1
20
+ end
21
+ unit
22
+ end
23
+ end
@@ -0,0 +1,5 @@
1
+ require 'singleton'
2
+
3
+ class Nothing
4
+ include Singleton
5
+ end
@@ -0,0 +1,27 @@
1
+ class Object
2
+
3
+ def self.empty
4
+ self.class.new
5
+ end
6
+
7
+ def fmap(fn)
8
+ fn.(self)
9
+ end
10
+
11
+ def lift
12
+ ->() { self }
13
+ end
14
+
15
+ def call(*args)
16
+ self
17
+ end
18
+
19
+ def apply(fn)
20
+ fn.(self)
21
+ end
22
+
23
+ def deep_clone
24
+ self.respond_to?(:clone) && !self.kind_of?(Numeric) && !self.kind_of?(TrueClass) && !self.kind_of?(FalseClass) && !self.kind_of?(NilClass) ? self.clone : self
25
+ end
26
+
27
+ end
@@ -0,0 +1,111 @@
1
+
2
+ class Proc
3
+ alias_method :standard_ruby_call, :call
4
+ attr_accessor :is_tupled
5
+
6
+ def fmap(lamb)
7
+ self * lamb
8
+ end
9
+
10
+ def self.pure(arg)
11
+ ->(x) { arg }
12
+ end
13
+
14
+ def **(lamb) ## <*> from Control.Applicative
15
+ ->(x) { self.(x, lamb.(x)) }
16
+ end
17
+
18
+
19
+ ## <**> from Control.Applicative
20
+ def ^(lamb)
21
+ ->(x) { lamb.(self.(x), x) }
22
+ end
23
+
24
+ def bind(lamb)
25
+ ->(x) { lamb.(self.(x), x)}
26
+ end
27
+
28
+ # Just a friendly reminder
29
+ # .() is shorthand for .call()
30
+ # and self.arity is the number of arguments this Proc takes
31
+
32
+ def [](*args)
33
+ call(*args)
34
+ end
35
+
36
+ def call(*args)
37
+ args_to_consume = args.take(self.arity < 0 ? self.arity.abs : self.arity) ## the < 1 here is because ruby stores *args as an arity of one more than the required args, in a negative number
38
+ remaining_args = args.drop(self.arity < 0 ? self.arity.abs : self.arity)
39
+
40
+ if !args_to_consume.respond_to?(:length) ## then we're dealing with a *args-only lambda, and we have enough args. just apply them all
41
+ result = self.standard_ruby_call(*args)
42
+ elsif self.arity == 0
43
+ result = self.standard_ruby_call()
44
+ elsif args.length == 0
45
+ #interpret application with no arguments on a non-zero-arity function as a no-op
46
+ return self
47
+ elsif (self.arity < 0 && args.length >= self.arity.abs - 1) ## then we're dealing with a *args-based lambda, and we have enough args. just apply them all
48
+ result = self.standard_ruby_call(*args)
49
+ elsif args_to_consume.length < self.arity
50
+ #if you have too few arguments, return a lambda asking for more before attempting to re-apply
51
+ return ->(x) { self.call( *( args + [x] ) ) }
52
+ elsif self.arity < 0
53
+ return ->(*xs) { self.call( *( args + xs.deep_clone ) ) }
54
+ else
55
+ #otherwise, apply the arguments
56
+ result = self.standard_ruby_call(*args_to_consume)
57
+ end
58
+ # if the result is a proc, make sure to unwrap further by recursively calling with any remaining arguments
59
+ (result.kind_of?(Proc) && remaining_args.length > 0) || (result.kind_of?(Proc) && remaining_args.length == 0 && result.respond_to?(:arity) && result.arity == 0) ? result.call(*remaining_args) : result
60
+ end
61
+
62
+ def *(lamb)
63
+ # You, then me
64
+ # like function composition
65
+ if lamb.class != Proc
66
+ lamb | self
67
+ else
68
+ ->(x) { self.( lamb.( x ) ) }
69
+ end
70
+ end
71
+
72
+ def |(lamb)
73
+ # Me, then you
74
+ # like unix pipes
75
+ if lamb.class != Proc
76
+ lamb * self
77
+ else
78
+ ->(x) { lamb.( self.( x ) ) }
79
+ end
80
+ end
81
+
82
+ def +(lamb)
83
+ ## later need to check if they have the same arity, once I've fixed the arity function to handle nesting lambdas
84
+ this = self
85
+
86
+ result = ->(xs) {
87
+ if lamb.is_tupled && this.is_tupled
88
+ this.(xs) + lamb.(xs)
89
+ elsif lamb.is_tupled
90
+ [this.(xs)] + lamb.(xs)
91
+ elsif this.is_tupled
92
+ this.(xs) + [lamb.(xs)]
93
+ else
94
+ [this.(xs),lamb.(xs)]
95
+ end
96
+ }
97
+ result.is_tupled = true
98
+ result
99
+ end
100
+
101
+ def <<(val)
102
+ # feed data from the right
103
+ self.(val.())
104
+ end
105
+
106
+ def >>(lamb)
107
+ # feed data from the left, assuming I am a wrapped Object of some sort
108
+ lamb.(self.())
109
+ end
110
+
111
+ end
@@ -0,0 +1,440 @@
1
+ class Stream
2
+ include Enumerable
3
+
4
+ def each(&fn)
5
+ item = self.next_item
6
+ while item != [:done]
7
+ fn.call(item[1]) if item.first == :yield
8
+ item = item.last.next_item
9
+ end
10
+ self
11
+ end
12
+
13
+ attr_accessor :state, :next_item_function
14
+ def initialize(next_func, state, options={})
15
+ @next_item_function = next_func
16
+ @state = state
17
+ @options = options
18
+ ## step_fn should return one of [:done], [:yield element stream], or [:skip stream]
19
+ self
20
+ end
21
+
22
+ def ==(stream)
23
+ if stream.respond_to?(:to_stream)
24
+ stream = stream.to_stream
25
+ next1 = self.another
26
+ next2 = stream.another
27
+ equal_so_far = next1 == next2 || (next1.first != :skip && next1[1] == next2[1])
28
+ while equal_so_far && !(next1 == [:done] || next2 == [:done])
29
+ next1 = next1.last.another
30
+ next2 = next2.last.another
31
+ equal_so_far = next1 == next2
32
+ end
33
+ equal_so_far
34
+ else
35
+ false
36
+ end
37
+ end
38
+
39
+ def ===(stream)
40
+ if stream.respond_to?(:to_stream)
41
+ stream = stream.to_stream
42
+ next1 = self.another
43
+ next2 = stream.another
44
+ equal_so_far = next1 == next2 || (next1.first != :skip && next1[1] === next2[1])
45
+ while equal_so_far && !(next1 == [:done] || next2 == [:done])
46
+ next1 = next1.last.another
47
+ next2 = next2.last.another
48
+ equal_so_far = next1 === next2
49
+ end
50
+ equal_so_far
51
+ else
52
+ false
53
+ end
54
+ end
55
+
56
+ def self.to_stream(stream)
57
+ stream
58
+ end
59
+
60
+ def to_stream
61
+ self
62
+ end
63
+
64
+ def from_stream
65
+ FromStream.new().(self)
66
+ end
67
+
68
+ def to_a
69
+ from_stream
70
+ end
71
+
72
+ def next_item
73
+ result = @next_item_function.(@state)
74
+ @state[2] = @state[1].clone.drop(@state[0]) if @options[:type] == "enumerator"
75
+ result
76
+ end
77
+
78
+ def next_item_function
79
+ @next_item_function
80
+ end
81
+
82
+ def another
83
+ item = self.next_item
84
+ while item.first == :skip
85
+ item = item.last.next_item
86
+ end
87
+ item
88
+ end
89
+
90
+ def foldl(func, unit)
91
+ from_stream.foldl(func, unit)
92
+ end
93
+
94
+ def call(*args)
95
+ next_fn = ->(next_item) {
96
+ tag = next_item.first
97
+ fn = next_item[1]
98
+ stream = next_item.last
99
+ if tag == :done
100
+ [:done]
101
+ elsif tag == :skip
102
+ [:skip, Stream.new(next_fn, stream.state)]
103
+ elsif tag == :yield
104
+ [:yield, fn.(*args), Stream.new(next_fn, stream.state)]
105
+ else
106
+ raise "#{next_item} is a malformed stream result!"
107
+ end
108
+ } * self.next_item_function
109
+ Stream.new(next_fn, self.state)
110
+ end
111
+
112
+ end
113
+
114
+
115
+ class FromStream
116
+ def initialize(clazz=Array, options={})
117
+ if clazz.kind_of?(Hash)
118
+ options = clazz
119
+ clazz = Array
120
+ end
121
+ @before_function = options['before']
122
+ @after_function = options['after']
123
+ @output_type = clazz
124
+ end
125
+
126
+ attr_accessor :before_function, :after_function
127
+ singleton_class.send(:alias_method, :standard_new, :new)
128
+ def self.new(clazz=Array,options={})
129
+ options['before'] && options['after'] ? ->(x) { self.standard_new(clazz,options).(x) } : self.standard_new(clazz,options)
130
+ end
131
+
132
+
133
+
134
+
135
+ alias_method :standard_kind_of?, :kind_of?
136
+ def kind_of?(clazz)
137
+ clazz == Proc || standard_kind_of?(clazz)
138
+ end
139
+
140
+
141
+ def call(stream)
142
+ before = @before_function
143
+ after = @after_function
144
+ stream = before.(stream) if before
145
+ result = self.unfold(stream)
146
+ after ? after.(result) : result
147
+ end
148
+
149
+
150
+ def unfold(stream)
151
+ result = @output_type.new
152
+ # puts 'unfolding'
153
+ # puts stream.inspect
154
+ next_val = stream.next_item
155
+ while next_val.first != :done
156
+ #puts next_val.inspect
157
+ result << next_val[1] if next_val.first == :yield
158
+ next_val = next_val.last.next_item
159
+ end
160
+ result
161
+ end
162
+
163
+ def join(after)
164
+ before = self
165
+ if after.class == before.class && !after.before_function && !before.after_function
166
+ before.class.new({
167
+ 'before' => before.before_function,
168
+ 'after' => after.after_function
169
+ })
170
+ elsif ToStream == after.class
171
+ StreamTransducer.new({
172
+ 'before' => before.before_function,
173
+ 'inside' => (after.before_function || Identity.new) * ((after.kind_of?(StreamTransducer) ? inside_function : nil) || Identity.new) * (before.after_function || Identity.new),
174
+ 'after' => after.after_function
175
+ })
176
+ elsif StreamTransducer == after.class && !before.after_function && !after.before_function
177
+ StreamTransducer.new({
178
+ 'before' => before.before_function,
179
+ 'inside' => after.inside_function,
180
+ 'after' => after.after_function
181
+ })
182
+ else
183
+ ->(xs) { after.(before.(xs)) }
184
+ end
185
+ end
186
+
187
+ def fuse(before_converter, after_converter)
188
+ before_converter.join(after_converter)
189
+ end
190
+
191
+ def *(lamb)
192
+ if lamb.kind_of?(Identity)
193
+ self
194
+ elsif [FromStream, StreamTransducer].include?(lamb.class)
195
+ ## then fuse away the streams by just making this the Identity.new function
196
+ self.fuse(lamb, self)
197
+ else
198
+ self.class.new({ 'before' => (self.before_function || Identity.new) * lamb ,
199
+ 'after' => self.after_function})
200
+ end
201
+ end
202
+
203
+ def |(lamb)
204
+ if lamb.kind_of?(Identity)
205
+ self
206
+ elsif [FromStream, ToStream, StreamTransducer].include?(lamb.class)
207
+ ## then fuse away the streams by just making this the Identity.new function
208
+ self.fuse(self, lamb)
209
+ else
210
+ self.class.new({ 'before' => self.before_function,
211
+ 'after' => (self.after_function || Identity.new) | lamb })
212
+ end
213
+ end
214
+
215
+ def <=(val)
216
+ # feed data from the right
217
+ self.(val.())
218
+ end
219
+
220
+ def >=(lamb)
221
+ # feed data from the left, assuming I am a wrapped Object of some sort
222
+ lamb.(self.())
223
+ end
224
+ end
225
+
226
+ class ToStream
227
+ ## Represents a generic to_stream function
228
+ def initialize(options={})
229
+ @before_function = options['before']
230
+ @after_function = options['after']
231
+ end
232
+
233
+ attr_reader :before_function, :after_function
234
+ singleton_class.send(:alias_method, :standard_new, :new)
235
+ def self.new(options={})
236
+ options['before'] && options['after'] ? ->(x) { self.standard_new(options).(x) } : self.standard_new(options)
237
+ end
238
+
239
+
240
+
241
+ alias_method :standard_kind_of?, :kind_of?
242
+
243
+ def kind_of?(clazz)
244
+ clazz == Proc || standard_kind_of?(clazz)
245
+ end
246
+
247
+
248
+ def call(collection)
249
+ before = @before_function
250
+ after = @after_function
251
+ collection = before.(collection) if before
252
+ result = collection.to_stream
253
+ after ? after.(result) : result
254
+ end
255
+
256
+ def join(after)
257
+ ## to = ToStream, from = FromStream
258
+ ## to = ToStream, from = ToStream
259
+ ## to = ToStream, from = StreamTransducer
260
+ before = self
261
+ if after.class == before.class && !after.before_function && !before.after_function
262
+ before.class.new({
263
+ 'before' => before.before_function,
264
+ 'after' => after.after_function
265
+ })
266
+ elsif FromStream == after.class
267
+ StreamTransducer.new({
268
+ 'before' => before.before_function,
269
+ 'inside' => (after.before_function || Identity.new) * ((after.kind_of?(StreamTransducer) ? inside_function : nil) || Identity.new) * (before.after_function || Identity.new),
270
+ 'after' => after.after_function
271
+ })
272
+ elsif StreamTransducer == after.class && !before.after_function && !after.before_function
273
+ StreamTransducer.new({
274
+ 'before' => before.before_function,
275
+ 'inside' => after.inside_function,
276
+ 'after' => after.after_function
277
+ })
278
+
279
+ else
280
+ ->(xs) { after.(before.(xs)) }
281
+ end
282
+ end
283
+
284
+ def fuse(before_converter, after_converter)
285
+ before_converter.join(after_converter)
286
+ end
287
+
288
+ def *(lamb)
289
+ if lamb.kind_of?(Identity)
290
+ self
291
+ elsif [FromStream, ToStream, StreamTransducer].include?(lamb.class)
292
+ ## then fuse away the streams by just making this the Identity.new function
293
+ self.fuse(lamb, self)
294
+ else
295
+ self.class.new({ 'before' => (self.before_function || Identity.new) * lamb ,
296
+ 'after' => self.after_function})
297
+ end
298
+ end
299
+
300
+ def |(lamb)
301
+ if lamb.kind_of?(Identity)
302
+ self
303
+ elsif [FromStream, ToStream, StreamTransducer].include?(lamb.class)
304
+ ## then fuse away the streams by just making this the Identity.new function
305
+ self.fuse(self, lamb)
306
+ else
307
+ self.class.new({ 'before' => self.before_function,
308
+ 'after' => (self.after_function || Identity.new) | lamb })
309
+ end
310
+ end
311
+
312
+ def <=(val)
313
+ # feed data from the right
314
+ self.(val.())
315
+ end
316
+
317
+ def >=(lamb)
318
+ # feed data from the left, assuming I am a wrapped Object of some sort
319
+ lamb.(self.())
320
+ end
321
+ end
322
+
323
+ class StreamTransducer
324
+
325
+
326
+ def initialize(options={})
327
+ @before_function = options['before']
328
+ @after_function = options['after']
329
+ @inside_function = options['inside']
330
+ end
331
+
332
+ attr_accessor :before_function, :after_function, :inside_function
333
+ singleton_class.send(:alias_method, :standard_new, :new)
334
+ def self.new(options={})
335
+ if options['inside'] && options['inside'].class != Identity
336
+ options['before'] && options['after'] ? ->(x) { self.standard_new(options).(x) } : self.standard_new(options)
337
+ else
338
+ ->(x) { self.standard_new(options).(x) }
339
+ end
340
+ end
341
+
342
+
343
+
344
+ def +(stream_transducer)
345
+ ##TODO handle case where before function and after functions exist
346
+ if stream_transducer.kind_of?(StreamTransducer) && !stream_transducer.before_function && !stream_transducer.after_function && !self.before_function && !self.after_function
347
+ StreamTransducer.new({
348
+ 'inside' => self.inside_function + stream_transducer.inside_function
349
+ })
350
+ else
351
+ raise "#{stream_transducer.class} should be of class StreamTransducer to be combined via + with another StreamTransducer"
352
+ end
353
+ end
354
+
355
+
356
+ alias_method :standard_kind_of?, :kind_of?
357
+ def kind_of?(clazz)
358
+ clazz == Proc || standard_kind_of?(clazz)
359
+ end
360
+
361
+
362
+ def call(arg)
363
+ before = self.before_function || Identity.new
364
+ after = self.after_function || Identity.new
365
+ after <= (F.from_stream * ->(stream) {
366
+ next_fn = self.inside_function * stream.next_item_function
367
+ Stream.new(next_fn, stream)
368
+ } <= F.to_stream.(before.(arg)))
369
+ end
370
+
371
+ def join(after)
372
+
373
+ ## to = StreamTransducer, from = FromStream
374
+ ## to = StreamTransducer, from = ToStream
375
+ ## to = StreamTransducer, from = StreamTransducer
376
+ before = self
377
+ if after.class == before.class && !after.before_function && !before.after_function
378
+ before.class.new({
379
+ 'before' => before.before_function,
380
+ 'inside' => after.inside_function * before.inside_function,
381
+ 'after' => after.after_function
382
+ })
383
+ elsif [ToStream,FromStream].include?(after.class) && !after.before_function && !before.after_function ## TODO TOMORROW figure this otu
384
+ ## if i am a transducer and have no after, and from has no before
385
+ ## then I cleanly merge with from and make a new transducer
386
+ ## if i have an after, then I produce a lambda?
387
+ ## NEXT STEP is to make a buuunch of test cases for all of this transducer/from/to merge stuff
388
+ ## and then keep implementing until they all pass
389
+ ## then build
390
+ StreamTransducer.new({
391
+ 'before' => before.before_function,
392
+ 'inside' => before.inside_function,
393
+ 'after' => after.after_function
394
+ })
395
+ else
396
+ ->(xs) { after.(before.(xs)) }
397
+ end
398
+ end
399
+
400
+ def fuse(before_converter, after_converter)
401
+ before_converter.join(after_converter)
402
+ end
403
+
404
+ def *(lamb)
405
+ if lamb.kind_of?(Identity)
406
+ self
407
+ elsif [ToStream, StreamTransducer].include?(lamb.class)
408
+ ## then fuse away the streams by just making this the Identity.new function
409
+ self.fuse(lamb, self)
410
+ else
411
+ self.class.new({ 'before' => (self.before_function || Identity.new) * lamb ,
412
+ 'inside' => self.inside_function,
413
+ 'after' => self.after_function})
414
+ end
415
+ end
416
+
417
+ def |(lamb)
418
+ if lamb.kind_of?(Identity)
419
+ self
420
+ elsif [FromStream, ToStream, StreamTransducer].include?(lamb.class)
421
+ ## then fuse away the streams by just making this the Identity.new function
422
+ self.fuse(self, lamb)
423
+ else
424
+ self.class.new({ 'before' => self.before_function,
425
+ 'inside' => self.inside_function,
426
+ 'after' => (self.after_function || Identity.new) | lamb })
427
+ end
428
+ end
429
+
430
+ def <=(val)
431
+ # feed data from the right
432
+ self.(val.())
433
+ end
434
+
435
+ def >=(lamb)
436
+ # feed data from the left, assuming I am a wrapped Object of some sort
437
+ lamb.(self.())
438
+ end
439
+
440
+ end