lambda_driver 1.2.2 → 1.2.3

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.
data/README.md CHANGED
@@ -96,6 +96,21 @@ The context-funciton should recieve 2 arguments.
96
96
  - second arg is a result of g(x)
97
97
  - g is a function passed to `compose_with_lifting`
98
98
 
99
+ If given arguments is Symbol, find context-function from
100
+ default context-functions.
101
+
102
+ - :identify
103
+ - this context nothing to do
104
+ - :maybe
105
+ - computations which may not return a result
106
+ - :list
107
+ - computations which can return multiple possible results
108
+ - :reader
109
+ - computations which read from a shared environment
110
+ - :writer
111
+ - computations which write data in addition to computing values
112
+
113
+ see -> LambdaDriver::Context
99
114
 
100
115
  #### Proc#compose_with_lifting
101
116
 
@@ -0,0 +1,59 @@
1
+ # -*- encoding : utf-8 -*-
2
+ module LambdaDriver::Context
3
+
4
+ Identify = lambda{|f, x| f.call(x) }
5
+
6
+ Maybe = lambda{|f, x|
7
+ mzero_method = x.respond_to?(:mzero?) ? :mzero? : :nil?
8
+ x.send(mzero_method) ? x : f.call(x)
9
+ }
10
+
11
+ List = lambda{|f, x| x.map(&f) }
12
+
13
+ Reader = lambda{|f, (r, x)|
14
+ # set reader env to thread local
15
+ Thread.current[:lambda_driver_reader_ctx_ask] = r
16
+ # define ask method to access context of reader
17
+ f.binding.eval(<<-CODE)
18
+ def ask
19
+ Thread.current[:lambda_driver_reader_ctx_ask]
20
+ end
21
+ CODE
22
+
23
+ begin
24
+ [r, f.call(x)]
25
+ ensure
26
+ # tear down reader context
27
+ Thread.current[:lambda_driver_reader_ctx_ask] = nil
28
+ f.binding.eval("undef ask")
29
+ end
30
+ }
31
+
32
+ Writer = lambda{|f, (x, w)|
33
+ pool = w || []
34
+
35
+ # set writer pool to thread local
36
+ Thread.current[:lambda_driver_writer_ctx_pool] = pool
37
+ # define tell method to write log to context
38
+ f.binding.eval(<<-CODE)
39
+ def tell(s)
40
+ pool = Thread.current[:lambda_driver_writer_ctx_pool]
41
+ pool << s
42
+ end
43
+ CODE
44
+
45
+ begin
46
+ [f.call(x), pool]
47
+ ensure
48
+ # tear down reader context
49
+ Thread.current[:lambda_driver_writer_ctx_pool] = nil
50
+ f.binding.eval("undef tell")
51
+ end
52
+ }
53
+
54
+ def self.[](name)
55
+ name = name.to_s.capitalize
56
+ const_defined?(name) && const_get(name)
57
+ end
58
+
59
+ end
@@ -6,7 +6,7 @@ module LambdaDriver::Liftable
6
6
  #
7
7
  # This method returns composed funciton like bellow.
8
8
  #
9
- # lambda{|args| context(self, g(*args)) }
9
+ # lambda{|args| context.call(self, context.call(g,*args) }
10
10
  #
11
11
  # For example, set context-function that logging the result.
12
12
  #
@@ -15,18 +15,17 @@ module LambdaDriver::Liftable
15
15
  # g = lambda{|y| hash[y]}
16
16
  #
17
17
  # ctx = lambda{|f,x|
18
- # puts "g(x) -> #{x}"
19
- # y = f.call(x)
20
- # puts "f(g(x)) -> #{y}"
21
- # y
18
+ # res = f.call(x)
19
+ # puts "result -> #{res}"
20
+ # res
22
21
  # }
23
22
  #
24
23
  # lifted = f.lift(ctx)
25
24
  # h = lifted.compose_with_lifting g
26
25
  #
27
26
  # h.(:a)
28
- # #=> g(x) -> foo
29
- # #=> f(g(x)) -> 3
27
+ # #=> result -> foo
28
+ # #=> result -> 3
30
29
  # #=> 3
31
30
  #
32
31
  # if context-function does not given,
@@ -49,9 +48,17 @@ module LambdaDriver::Liftable
49
48
  # f <= g # => f.compose_with_lifting(g)
50
49
  #
51
50
  def compose_with_lifting(g)
52
- context = lambda_driver_liftable_context
53
-
54
- lambda{|*args| context.call(self.to_proc, g.to_proc.call(*args)) }.lift(context)
51
+ if @lambda_driver_lifted
52
+ ctx = @lambda_driver_liftable_context
53
+ self.compose(g).tap{|f|
54
+ f.instance_eval do
55
+ @lambda_driver_lifted = true
56
+ @lambda_driver_liftable_context = ctx
57
+ end
58
+ }
59
+ else
60
+ self.lift(DEFAULT_CONTEXT).compose_with_lifting(g)
61
+ end
55
62
  end
56
63
 
57
64
  # This is a default context-function.
@@ -69,10 +76,7 @@ module LambdaDriver::Liftable
69
76
  # h.(:a) # => 3
70
77
  # h.(:b) # => nil (it does not called f)
71
78
  #
72
- DEFAULT_CONTEXT = lambda{|f, x|
73
- mzero_method = x.respond_to?(:mzero?) ? :mzero? : :nil?
74
- x.send(mzero_method) ? x : f.call(x)
75
- }
79
+ DEFAULT_CONTEXT = LambdaDriver::Context[:maybe]
76
80
 
77
81
  # Lift this function to the given context-function.
78
82
  # The lifted fucntion can compose other function with context-fucntion.
@@ -85,9 +89,26 @@ module LambdaDriver::Liftable
85
89
  # - second arg is a result of g(x)
86
90
  # -- g is a function passed to `compose_with_lifting`
87
91
  #
88
- def lift(g = DEFAULT_CONTEXT)
89
- @lambda_driver_liftable_context = g
90
- self
92
+ def lift(ctx = DEFAULT_CONTEXT, &block)
93
+ ctx = block_given? ? block : ctx
94
+ ctx = case ctx
95
+ when String, Symbol then LambdaDriver::Context[ctx]
96
+ else ctx
97
+ end
98
+
99
+ # do not lift same context again
100
+ return self if lambda_driver_lifted? && (ctx == lambda_driver_liftable_context)
101
+
102
+ lambda{|*args| ctx.call(self, *args) }.tap{|f|
103
+ f.instance_eval do
104
+ @lambda_driver_lifted = true
105
+ @lambda_driver_liftable_context = ctx
106
+ end
107
+ }
108
+ end
109
+
110
+ def lambda_driver_lifted?
111
+ @lambda_driver_lifted
91
112
  end
92
113
 
93
114
  def lambda_driver_liftable_context
@@ -95,7 +116,7 @@ module LambdaDriver::Liftable
95
116
  end
96
117
 
97
118
  def >=(g)
98
- g.to_proc.lift(lambda_driver_liftable_context) <= self
119
+ g.to_proc.lift(lambda_driver_liftable_context).compose_with_lifting(self)
99
120
  end
100
121
 
101
122
  def self.included(klass)
@@ -1,4 +1,4 @@
1
1
  # -*- encoding : utf-8 -*-
2
2
  module LambdaDriver
3
- VERSION = "1.2.2"
3
+ VERSION = "1.2.3"
4
4
  end
@@ -0,0 +1,93 @@
1
+ # -*- encoding : utf-8 -*-
2
+ require 'spec_helper'
3
+
4
+ describe LambdaDriver::Context do
5
+
6
+ let(:f){ lambda{|x| x.to_s} }
7
+ let(:g){ lambda{|x| x * 2} }
8
+
9
+ describe LambdaDriver::Context::Identify do
10
+ let(:ctx) { :identify }
11
+ let(:x){ :foo }
12
+ it("f.lift(:identify).call(x).should == f(x)"){
13
+ f.lift(:identify).call(x).should == f.call(x)
14
+ }
15
+
16
+ it("(f.lift(:identify) >= g).call(x).should == g(f(x))"){
17
+ (f.lift(:identify) >= g).call(x).should == g.call(f.call(x))
18
+ }
19
+ end
20
+
21
+ describe LambdaDriver::Context::Maybe do
22
+ let(:x){ :foo }
23
+
24
+ it("f.lift(:maybe).call(nil).should be_nil"){
25
+ f.lift(:maybe).call(nil).should be_nil
26
+ }
27
+
28
+ it("f.lift(:maybe).call(x).should f(x)"){
29
+ f.lift(:maybe).call(x).should == f.call(x)
30
+ }
31
+
32
+ it("(f.lift(:maybe) >= g).call(nil).should be_nil "){
33
+ (f.lift(:maybe) >= g).call(nil).should be_nil
34
+ }
35
+
36
+ it("(f.lift(:maybe) >= g).call(x).should == g(f(x))"){
37
+ (f.lift(:maybe) >= g).call(x).should == g.call(f.call(x))
38
+ }
39
+ end
40
+
41
+ describe LambdaDriver::Context::List do
42
+ let(:x){ [:foo, :bar] }
43
+
44
+ it("f.lift(:list).call([]).should == []"){
45
+ f.lift(:list).call([]).should == []
46
+ }
47
+
48
+ it("f.lift(:list).call(x).should == x.map(&f)"){
49
+ f.lift(:list).call(x).should == x.map(&f)
50
+ }
51
+
52
+ it("(f.lift(:list) >= g).call([]).should == (x.map(&f)).map(&g)"){
53
+ (f.lift(:list) >= g).call([]).should == []
54
+ }
55
+
56
+ it("(f.lift(:list) >= g).call(x).should == (x.map(&f)).map(&g)"){
57
+ (f.lift(:list) >= g).call(x).should == (x.map(&f)).map(&g)
58
+ }
59
+ end
60
+
61
+ describe LambdaDriver::Context::Reader do
62
+ let(:x){ 2 }
63
+ let(:r){ 3 }
64
+
65
+ let(:f){ lambda{|x| (ask + x).to_s} }
66
+ let(:g){ lambda{|x| x * (ask)} }
67
+
68
+ it("f.lift(:reader).call([r, x]).should == [r, f(x)]"){
69
+ f.lift(:reader).call([r, x]).should == [r, (r + x).to_s]
70
+ }
71
+
72
+ it("(f.lift(:reader) >= g).call([r, x]).should == [r, g(f(x))]"){
73
+ (f.lift(:reader) >= g).call([r, x]).should == [r, ((r + x).to_s) * r]
74
+ }
75
+ end
76
+
77
+ describe LambdaDriver::Context::Writer do
78
+ let(:w) { [:hoge] }
79
+ let(:x) { :foo }
80
+ let(:f){ lambda{|x| tell("bar_" + x.to_s); x.to_s } }
81
+ let(:g){ lambda{|x| tell("baz_" + x); x * 2 } }
82
+
83
+ it("f.lift(:writer).call([x, w]).should == [f(x), w]"){
84
+ f.lift(:writer).call([x, w]).should == [x.to_s, w]
85
+ w.should == [:hoge, "bar_foo"]
86
+ }
87
+
88
+ it("(f.lift(:writer) >= g).call([r, x]).should == [r, g(f(x))]"){
89
+ (f.lift(:writer) >= g).call([x, w]).should == [(x.to_s) * 2, w]
90
+ w.should == [:hoge, "bar_foo", "baz_foo"]
91
+ }
92
+ end
93
+ end
@@ -50,11 +50,11 @@ shared_examples_for 'liftable' do
50
50
  it(" f.lift(ctx) >= g returns funciton that compose with ctx"){
51
51
  (subject.lift(ctx) >= g).should be_a_kind_of Proc
52
52
  }
53
- it('"(f.flit(ctx) >= g).call(x) should be ctx(g, f(x))'){
54
- (subject.lift(ctx) >= g).call(x).should == ctx.call(g, subject.to_proc.call(x))
53
+ it('"(f.lift(ctx) >= g).call(x) should be ctx(g, ctx(f, x))'){
54
+ (subject.lift(ctx) >= g).call(x).should == ctx.call(g, ctx.call(subject.to_proc,x))
55
55
  }
56
- it('"(f.flit(ctx) >= g >= h).call(x) should be ctx(h, ctx(g, f(x)))'){
57
- (subject.lift(ctx) >= g >= h).call(x).should == ctx.call(h, ctx.call(g, subject.to_proc.call(x)))
56
+ it('"(f.lift(ctx) >= g >= h).call(x) should be ctx(h, ctx(g, f(x)))'){
57
+ (subject.lift(ctx) >= g >= h).call(x).should == ctx.call(h, ctx.call(g, ctx.call(subject.to_proc, x)))
58
58
  }
59
59
  end
60
60
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: lambda_driver
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.2.2
4
+ version: 1.2.3
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2014-01-30 00:00:00.000000000 Z
12
+ date: 2014-02-06 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rspec
@@ -45,6 +45,7 @@ files:
45
45
  - lib/lambda_driver.rb
46
46
  - lib/lambda_driver/callable.rb
47
47
  - lib/lambda_driver/composable.rb
48
+ - lib/lambda_driver/context.rb
48
49
  - lib/lambda_driver/core_ext.rb
49
50
  - lib/lambda_driver/core_ext/class.rb
50
51
  - lib/lambda_driver/core_ext/method.rb
@@ -64,6 +65,7 @@ files:
64
65
  - lib/lambda_driver/version.rb
65
66
  - lib/lambda_driver/with_args.rb
66
67
  - spec/class_spec.rb
68
+ - spec/context_spec.rb
67
69
  - spec/disjunction_spec.rb
68
70
  - spec/lambda_spec.rb
69
71
  - spec/method_spec.rb
@@ -89,7 +91,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
89
91
  version: '0'
90
92
  segments:
91
93
  - 0
92
- hash: -4555734478937324057
94
+ hash: -2789820637790150552
93
95
  required_rubygems_version: !ruby/object:Gem::Requirement
94
96
  none: false
95
97
  requirements:
@@ -98,7 +100,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
98
100
  version: '0'
99
101
  segments:
100
102
  - 0
101
- hash: -4555734478937324057
103
+ hash: -2789820637790150552
102
104
  requirements: []
103
105
  rubyforge_project:
104
106
  rubygems_version: 1.8.23
@@ -107,6 +109,7 @@ specification_version: 3
107
109
  summary: Drives your code more functioal!
108
110
  test_files:
109
111
  - spec/class_spec.rb
112
+ - spec/context_spec.rb
110
113
  - spec/disjunction_spec.rb
111
114
  - spec/lambda_spec.rb
112
115
  - spec/method_spec.rb