lambda_driver 1.2.2 → 1.2.3

Sign up to get free protection for your applications and to get access to all the features.
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