necromancy 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/Gemfile ADDED
@@ -0,0 +1,2 @@
1
+ source 'https://rubygems.org'
2
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,7 @@
1
+ Copyright (c) 2013 pasberth
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
4
+
5
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
6
+
7
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.rst ADDED
@@ -0,0 +1,141 @@
1
+ Necromancy
2
+ ================================================================================
3
+
4
+ Necromancy conjures up the functional code.
5
+
6
+ .. code:: ruby
7
+
8
+ require 'necromancy'
9
+ N = Necromancy.new
10
+
11
+ # [:foo, :bar, :baz].map{|s| s.to_s }.map{|s| s.upcase }
12
+ [:foo, :bar, :baz].map &N.to_s . upcase
13
+
14
+ # [:foo, :hoge, :bar, :fuga].select{|s| s.to_s.length > 3} # => [:hoge, :fuga]
15
+ [:foo, :hoge, :bar, :fuga].select &N.to_s . length > 3
16
+
17
+ Features
18
+ --------------------------------------------------------------------------------
19
+
20
+ Function composition
21
+ ________________________________________________________________________________
22
+
23
+ Every messages to instance of `Necromancy` are function composition
24
+ by default. that is left-to-right composition.
25
+
26
+ .. code:: ruby
27
+
28
+ N.f.g == ->(o) { :g.to_proc(:f.to_proc(o)) } == ->(o) { o.f.g }
29
+
30
+
31
+ Application with arguments
32
+ ________________________________________________________________________________
33
+
34
+ If a message was called with some argument given,
35
+ their arguments are given into that function each time.
36
+
37
+ .. code:: ruby
38
+
39
+ N.f(x) == ->(o) { :f.to_proc(o, x) } == ->(o) { o.f(x) }
40
+
41
+
42
+ Rich extensions.
43
+ ________________________________________________________________________________
44
+ If you want, you can use extensions by clojuring up the evil spirit.
45
+
46
+ .. code:: ruby
47
+
48
+ M = Necromancy.Alternative.new
49
+ M.x | M.y == ->(o) { o.x || o.y }
50
+
51
+ Examples
52
+ --------------------------------------------------------------------------------
53
+
54
+ Simple Function composion
55
+ ________________________________________________________________________________
56
+
57
+ First, your create a `Necromancy` object.
58
+ it is immutable, you can save it to any variable you like.
59
+ for example, that is constant, global varibale, instance variable, class variable, local variable, etc.
60
+
61
+ .. code:: ruby
62
+
63
+ N = Necromancy.new
64
+
65
+ After, you send some message to N when you need to write a simple block.
66
+
67
+ .. code:: ruby
68
+
69
+ (1..5).map &N ** 2 # => [1, 4, 9, 16, 25]
70
+
71
+ Function composion
72
+ ________________________________________________________________________________
73
+
74
+ .. code:: ruby
75
+
76
+ N = Necromancy.Category.new
77
+ ary = ('A'..'Z').to_a
78
+ (0..4).map &N > ary.method(:[]) # => ["A", "B", "C", "D", "E"]
79
+
80
+ Multiple accessing to attribtues
81
+ ________________________________________________________________________________
82
+
83
+ .. code:: ruby
84
+
85
+ N = Necromancy.Arrow.new
86
+ str = "foo"
87
+ lambda(&N.upcase & :capitalize & :reverse).(str) # => ["FOO", "Foo", "oof"]
88
+
89
+
90
+ Maybe evaluating
91
+ ________________________________________________________________________________
92
+
93
+ .. code:: ruby
94
+
95
+ N = Necromancy.Alternative.new
96
+ n = N >> N.upcase!
97
+ "foo".tap &n # => "FOO"
98
+ nil.tap &n # => nil
99
+
100
+ Alias importation
101
+ ________________________________________________________________________________
102
+
103
+ .. code:: ruby
104
+
105
+ N = Necromancy.Alternative.using(:>> => :then).new
106
+ str_or_nil = ["foo", nil].sample
107
+ str_or_nil.tap &(N.then N.upcase!) # => nil or "FOO"
108
+
109
+ Hiding importation
110
+ ________________________________________________________________________________
111
+
112
+ .. code:: ruby
113
+
114
+ N = Necromancy.Alternative.hiding(:*, :**).new
115
+ (1..5).map &N ** 2 # => [1, 4, 9, 16, 25]
116
+
117
+ Specifying importation
118
+ ________________________________________________________________________________
119
+
120
+ .. code:: ruby
121
+
122
+ N = Necromancy.Alternative.(:>>).new
123
+ str_or_nil = ["foo", nil].sample
124
+ str_or_nil.tap &N >> N.upcase! # => nil or "FOO"
125
+ (1..5).map &N ** 2 # => [1, 4, 9, 16, 25]
126
+
127
+ Multiple module importation
128
+ ________________________________________________________________________________
129
+
130
+ .. code:: ruby
131
+
132
+ N = Necromancy.Arrow.Alternative.hiding(:*, :**).new
133
+ [nil, 42, "foo"].map &N.is_a?(Integer) >> (N * 2 & N ** 2) | N # => [nil, [84, 1764], "foo"]
134
+
135
+
136
+ Installation
137
+ --------------------------------------------------------------------------------
138
+
139
+ .. code:: sh
140
+
141
+ gem install necromancy
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.1.0
@@ -0,0 +1,7 @@
1
+ require 'necromancy'
2
+
3
+ L = Necromancy.Alternative.new
4
+ puts (1..100).map &(L%15).zero? >> proc{"FizzBuzz"} |
5
+ (L%3).zero? >> proc{"Fizz"} |
6
+ (L%5).zero? >> proc{"Buzz"} |
7
+ L
@@ -0,0 +1,8 @@
1
+ require 'necromancy'
2
+
3
+ L = Necromancy.Alternative.using(:<< => :if,
4
+ :>> => :then,
5
+ :| => :else).new
6
+ puts (1..100).map &( (L%15).zero? .then(proc{"FizzBuzz"}) .else \
7
+ (L%3).zero? .then(proc{"Fizz"}) .else \
8
+ (L%5).zero? .then(proc{"Buzz"}) .else L )
@@ -0,0 +1,6 @@
1
+ require 'necromancy'
2
+
3
+ L = Necromancy.Alternative.hiding(:*, :**).new
4
+
5
+ ary = [1, nil, 2, nil, 3]
6
+ p ary.map &(L | proc{0}) * 10 # => [10, 0, 20, 0, 3]
@@ -0,0 +1,47 @@
1
+ require 'necromancy'
2
+ require 'necromancy/control'
3
+ require 'necromancy/control/applicative'
4
+
5
+ module Necromancy
6
+
7
+ module Control::Alternative; extend Control
8
+
9
+ include Control::Applicative
10
+
11
+ def empty?(x, *xs)
12
+ not x
13
+ end
14
+
15
+ protected :empty?
16
+
17
+ def *(callable)
18
+ str = make_evaluable_string(callable)
19
+ necromancy = "self.empty?(*(xs = (#{str}))) ? xs : (args.concat(xs); #{@necromancy})"
20
+ self.class.new(necromancy, @references.dup)
21
+ end
22
+
23
+ def |(callable)
24
+ str = make_evaluable_string(callable)
25
+
26
+ if @is_alternative_or
27
+ exprs = [str, *@exprs]
28
+ else
29
+ exprs = [str, @necromancy]
30
+ end
31
+
32
+ necromancy = exprs.inject do |else_expr, cond_expr|
33
+ "self.empty?(*(xs = (#{cond_expr}))) ? (#{else_expr}) : xs"
34
+ end
35
+
36
+ self.class.new(necromancy, @references.dup).instance_eval do
37
+ @is_alternative_or = true
38
+ @exprs = exprs
39
+ self
40
+ end
41
+ end
42
+
43
+
44
+ alias __Applicative_Astarisk *
45
+ protected :__Applicative_Astarisk
46
+ end
47
+ end
@@ -0,0 +1,30 @@
1
+ require 'necromancy'
2
+ require 'necromancy/control'
3
+
4
+ module Necromancy
5
+
6
+ module Control::Applicative; extend Control
7
+
8
+ def *(callable)
9
+ str = make_evaluable_string(callable)
10
+ self.class.new("args.concat((#{str})); #{@necromancy}", @references.dup)
11
+ end
12
+
13
+ def **(callable)
14
+ str = make_evaluable_string(callable)
15
+ self.class.new(str, @references.dup).__Applicative_Astarisk(self)
16
+ end
17
+
18
+ def <<(callable)
19
+ self.class.new("args.pop; #{@necromancy}", @references.dup).__Applicative_Astarisk(callable)
20
+ end
21
+
22
+ def >>(callable)
23
+ str = make_evaluable_string(callable)
24
+ self.class.new("args.pop; #{str}", @references.dup).__Applicative_Astarisk(self)
25
+ end
26
+
27
+ alias __Applicative_Astarisk *
28
+ protected :__Applicative_Astarisk
29
+ end
30
+ end
@@ -0,0 +1,23 @@
1
+ require 'necromancy'
2
+ require 'necromancy/control'
3
+ require 'necromancy/control/category'
4
+
5
+ module Necromancy
6
+
7
+ module Control::Arrow; extend Control
8
+
9
+ include Control::Category
10
+
11
+ def &(callable)
12
+ str = make_evaluable_string(callable)
13
+ necromancy = "(#{@necromancy}) + (#{str})"
14
+ self.class.new(necromancy, @references.dup)
15
+ end
16
+
17
+ def *(callable)
18
+ str = make_evaluable_string(callable)
19
+ necromancy = "stack << [] << args; args = stack[-1][0..-2]; stack[-2].concat((#{@necromancy})); args = [stack[-1][-1]]; stack[-2].concat((#{str})); args = stack.pop; stack.pop"
20
+ self.class.new(necromancy, @references.dup)
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,20 @@
1
+ require 'necromancy'
2
+ require 'necromancy/control'
3
+
4
+ module Necromancy
5
+
6
+ module Control::Category; extend Control
7
+
8
+ def >(callable)
9
+ str = make_evaluable_string(callable)
10
+ necromancy = "args = (#{@necromancy}); #{str}"
11
+ self.class.new(necromancy, @references.dup)
12
+ end
13
+
14
+ def <(callable)
15
+ str = make_evaluable_string(callable)
16
+ necromancy = "args = (#{str}); #{@necromancy}"
17
+ self.class.new(necromancy, @references.dup)
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,47 @@
1
+ module Necromancy
2
+
3
+ module Control
4
+
5
+ def new
6
+ mod = self
7
+ Class.new(::Necromancy::Necromancy) { include mod }.new
8
+ end
9
+
10
+ def branch(&block)
11
+ mod = self
12
+ Module.new { include mod; extend Control; module_eval(&block) }
13
+ end
14
+
15
+ private :branch
16
+
17
+ def call(*targets)
18
+ branch { protected *instance_methods }.using(*targets)
19
+ end
20
+
21
+ def using(*targets)
22
+ names = targets.select { |t| t.is_a? Symbol }
23
+ aliases = targets.select { |t| t.is_a? Hash }.inject(:merge) || {}
24
+ branch do
25
+ public *names
26
+ aliases.each do |org, als|
27
+ alias_method(als, org)
28
+ protected org
29
+ public als
30
+ end
31
+ end
32
+ end
33
+
34
+ def hiding(*names)
35
+ branch { protected *names }
36
+ end
37
+
38
+ def method_missing(name, *args, &block)
39
+ super unless ('A'..'Z').include? name[0]
40
+ if ::Necromancy::Control.const_defined? name
41
+ branch { include ::Necromancy::Control.const_get(name) }
42
+ else
43
+ super
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,82 @@
1
+ module Necromancy
2
+
3
+ class Necromancy < BasicObject
4
+
5
+ protected *instance_methods
6
+ protected
7
+
8
+ def initialize(necromancy = "args", references = [])
9
+ @necromancy = necromancy
10
+ @references = references
11
+ end
12
+
13
+ def method_missing(name, *args, &block)
14
+ getproc = ":#{name}.to_proc"
15
+
16
+ if args.size == 0
17
+ getargs = nil
18
+ elsif args.size == 1
19
+ getargs = ", (#{make_evaluable_literal(args[0])})"
20
+ else
21
+ getargs = ", *(#{make_evaluable_literal(args)})"
22
+ end
23
+
24
+ if block
25
+ getblock = ", &(#{make_evaluable_literal(block)})"
26
+ else
27
+ getblock = nil
28
+ end
29
+
30
+ necromancy = "[#{getproc}.(*(#{@necromancy})#{getargs}#{getblock})]"
31
+ self.class.new(necromancy, @references.dup)
32
+ end
33
+
34
+ def to_proc
35
+ instance_eval("->(*args) { i = 0; stack = []; xs = (#{@necromancy}); xs.size == 1 ? xs.first : xs }")
36
+ end
37
+
38
+ def class
39
+ @class ||= (class << self; self end).superclass
40
+ end
41
+
42
+ def get_ref(i)
43
+ @references[i]
44
+ end
45
+
46
+ def set_ref(i, v)
47
+ @references[i] = v
48
+ end
49
+
50
+ def add_val(v)
51
+ i = @references.size
52
+ @references[i] = v
53
+ i
54
+ end
55
+
56
+ def make_evaluable_literal(anyref)
57
+ case anyref
58
+ when nil, ::Integer, ::Float, ::Symbol
59
+ anyref.inspect
60
+ else
61
+ i = add_val(anyref)
62
+ "self.get_ref(i + #{i})"
63
+ end
64
+ end
65
+
66
+ def make_evaluable_string(anyref)
67
+ case anyref
68
+ when Necromancy
69
+ references = anyref.instance_eval {@references}
70
+ necromancy = anyref.instance_eval {@necromancy}
71
+ @references.concat(references)
72
+ "stack << i; i = #{references.size}; xs = (#{necromancy}); i = stack.pop; xs"
73
+ when ::Symbol
74
+ "[:#{anyref}.to_proc.(*args)]"
75
+ else
76
+ prc = anyref.to_proc
77
+ i = add_val(prc)
78
+ "[self.get_ref(i + #{i}).(*args)]"
79
+ end
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,4 @@
1
+ module Necromancy
2
+
3
+ VERSION = "0.1.0"
4
+ end
data/lib/necromancy.rb ADDED
@@ -0,0 +1,12 @@
1
+ module Necromancy
2
+
3
+ require 'necromancy/version'
4
+ require 'necromancy/necromancy'
5
+ require 'necromancy/control'
6
+ require 'necromancy/control/category'
7
+ require 'necromancy/control/arrow'
8
+ require 'necromancy/control/applicative'
9
+ require 'necromancy/control/alternative'
10
+
11
+ extend Control
12
+ end
@@ -0,0 +1,16 @@
1
+ version = File.read("VERSION").chomp
2
+ `perl -i -pe 's/VERSION = "[\\d\\.]+"/VERSION = "#{version}"/' lib/necromancy/version.rb`
3
+ Gem::Specification.new do |s|
4
+ s.name = "necromancy"
5
+ s.version = version
6
+ s.authors = ["pasberth"]
7
+ s.description = %{Necromancy conjures up the functional code.}
8
+ s.summary = %q{experimental release}
9
+ s.email = "pasberth@gmail.com"
10
+ s.homepage = "https://github.com/pasberth/necromancy"
11
+ s.license = 'MIT'
12
+ s.require_paths = ["lib"]
13
+ s.files = `git ls-files`.split("\n")
14
+ s.test_files = `git ls-files -- spec/*`.split("\n")
15
+ s.add_development_dependency "rspec"
16
+ end
@@ -0,0 +1,89 @@
1
+ require 'necromancy'
2
+
3
+ describe Necromancy::Control::Alternative do
4
+
5
+ let(:l) { described_class.new }
6
+ shared_examples_for "pure" do
7
+
8
+ example { proc(&f * l).(r).should == proc(&f).(r, proc(&l).(r)) }
9
+ example { proc(&f * l | l).(r).should == proc(&f).(r, proc(&l).(r)) }
10
+ example { proc(&f * l | g).(r).should == proc(&f).(r, proc(&l).(r)) }
11
+ example { proc(&l | g).(r).should == proc(&l).(r) }
12
+ example { proc(&l | f * l).(r).should == proc(&l).(r) }
13
+ example { proc(&l | l ** f).(r).should == proc(&l).(r) }
14
+ example { proc(&l ** f).(r).should == proc(&f).(r, proc(&l).(r)) }
15
+ example { proc(&l ** f | l).(r).should == proc(&f).(r, proc(&l).(r)) }
16
+ example { proc(&l ** f | g).(r).should == proc(&f).(r, proc(&l).(r)) }
17
+ example { proc(&l >> g).(r).should == proc(&g).(r) }
18
+ example { proc(&l >> f * l).(r).should == proc(&f).(r, proc(&l).(r)) }
19
+ example { proc(&g << l).(r).should == proc(&g).(r) }
20
+ example { proc(&f * l << l).(r).should == proc(&f).(r, proc(&l).(r)) }
21
+ pending { proc(&h * l * l).(r).should == proc(&f).(r, proc(&l).(r), proc(&l).(r)) }
22
+ end
23
+
24
+ shared_examples_for "empty" do
25
+
26
+ example { proc(&f * l).(r).should == proc(&l).(r) }
27
+ example { proc(&f * l | g).(r).should == proc(&g).(r) }
28
+ example { proc(&l | g).(r).should == proc(&g).(r) }
29
+ example { proc(&l | f * l).(r).should == proc(&l).(r) }
30
+ example { proc(&l | l ** f).(r).should == proc(&l).(r) }
31
+ example { proc(&l ** f).(r).should == proc(&l).(r) }
32
+ example { proc(&l ** f | l).(r).should == proc(&l).(r) }
33
+ example { proc(&l ** f | g).(r).should == proc(&g).(r) }
34
+ example { proc(&l >> g).(r).should == proc(&l).(r) }
35
+ example { proc(&l >> f * l).(r).should == proc(&l).(r) }
36
+ example { proc(&g << l).(r).should == proc(&l).(r) }
37
+ example { proc(&f * l << l).(r).should == proc(&l).(r) }
38
+ end
39
+
40
+ it_behaves_like "empty" do
41
+ let(:r) { nil }
42
+ let(:f) { described_class.new.inspect }
43
+ let(:g) { described_class.new.inspect }
44
+ end
45
+
46
+ it_behaves_like "empty" do
47
+ let(:r) { false }
48
+ let(:f) { described_class.new.& }
49
+ let(:g) { !described_class.new }
50
+ end
51
+
52
+ it_behaves_like "pure" do
53
+ let(:r) { true }
54
+ let(:f) { described_class.new.& }
55
+ let(:g) { !described_class.new }
56
+ end
57
+
58
+ it_behaves_like "pure" do
59
+ let(:r) { 0 }
60
+ let(:f) { described_class.new.^ }
61
+ let(:g) { !described_class.new }
62
+ end
63
+
64
+ it_behaves_like "pure" do
65
+ let(:r) { [] }
66
+ let(:f) { described_class.new.+ }
67
+ let(:g) { described_class.new.first }
68
+ end
69
+
70
+ it_behaves_like "pure" do
71
+ let(:r) { {} }
72
+ let(:f) { described_class.new.merge }
73
+ let(:g) { described_class.new[:x] }
74
+ end
75
+
76
+ it_behaves_like "empty" do
77
+ let(:r) { {} }
78
+ let(:l) { described_class.new[:x] }
79
+ let(:f) { described_class.new.== }
80
+ let(:g) { described_class.new.inspect }
81
+ end
82
+
83
+ it_behaves_like "pure" do
84
+ let(:r) { {x: 42} }
85
+ let(:l) { described_class.new[:x] }
86
+ let(:f) { described_class.new.!= }
87
+ let(:g) { described_class.new.inspect }
88
+ end
89
+ end
@@ -0,0 +1,48 @@
1
+ require 'necromancy'
2
+
3
+ describe Necromancy::Control::Applicative do
4
+
5
+ let(:l) { described_class.new }
6
+
7
+ shared_examples_for "an Applicative" do
8
+
9
+ example { proc(&f * l).(r).should == proc(&f).(r, proc(&l).(r)) }
10
+ example { proc(&l ** f).(r).should == proc(&f).(r, proc(&l).(r)) }
11
+ example { proc(&l >> g).(r).should == proc(&g).(r) }
12
+ example { proc(&l >> f * l).(r).should == proc(&f).(r, proc(&l).(r)) }
13
+ example { proc(&g << l).(r).should == proc(&g).(r) }
14
+ example { proc(&f * l << l).(r).should == proc(&f).(r, proc(&l).(r)) }
15
+ pending { proc(&h * l * l).(r).should == proc(&f).(r, proc(&l).(r), proc(&l).(r)) }
16
+ end
17
+
18
+ it_behaves_like "an Applicative" do
19
+ let(:r) { nil }
20
+ let(:f) { described_class.new.& }
21
+ let(:g) { !described_class.new }
22
+ end
23
+
24
+
25
+ it_behaves_like "an Applicative" do
26
+ let(:r) { false }
27
+ let(:f) { described_class.new.& }
28
+ let(:g) { !described_class.new }
29
+ end
30
+
31
+ it_behaves_like "an Applicative" do
32
+ let(:r) { true }
33
+ let(:f) { described_class.new.& }
34
+ let(:g) { !described_class.new }
35
+ end
36
+
37
+ it_behaves_like "an Applicative" do
38
+ let(:r) { 0 }
39
+ let(:f) { described_class.new.^ }
40
+ let(:g) { !described_class.new }
41
+ end
42
+
43
+ it_behaves_like "an Applicative" do
44
+ let(:r) { [] }
45
+ let(:f) { described_class.new.+ }
46
+ let(:g) { described_class.new.first }
47
+ end
48
+ end
@@ -0,0 +1,38 @@
1
+ require 'necromancy'
2
+
3
+ describe Necromancy::Control::Arrow do
4
+
5
+ let(:l) { described_class.new }
6
+
7
+ shared_examples_for "an Arrow" do
8
+ example { proc(&l & l).(r).should == [proc(&l).(r), proc(&l).(r)] }
9
+ example { proc(&l & l & l).(r).should == [proc(&l).(r), proc(&l).(r), proc(&l).(r)] }
10
+ example { proc(&l & l & l & l).(r).should == [proc(&l).(r), proc(&l).(r), proc(&l).(r), proc(&l).(r)] }
11
+ example { proc(&l & l > f).(r).should == proc(&f).(proc(&l).(r), proc(&l).(r)) }
12
+ example { proc(&l & l > ga * gb).(r).should == [proc(&ga).(proc(&l).(r)), proc(&gb).(proc(&l).(r))] }
13
+ example { proc(&l & l & l > ga * gb * ga).(r).should == [proc(&ga).(proc(&l).(r)), proc(&gb).(proc(&l).(r)), proc(&ga).(proc(&l).(r))] }
14
+ example { proc(&l & l & l & l > ga * gb * ga * gb).(r).should == [proc(&ga).(proc(&l).(r)), proc(&gb).(proc(&l).(r)), proc(&ga).(proc(&l).(r)), proc(&gb).(proc(&l).(r))] }
15
+ end
16
+
17
+ it_behaves_like "an Arrow" do
18
+ let(:r) { 0 }
19
+ let(:f) { :+ }
20
+ let(:ga) { +l }
21
+ let(:gb) { -l }
22
+ end
23
+
24
+ it_behaves_like "an Arrow" do
25
+ let(:r) { [] }
26
+ let(:f) { :+ }
27
+ let(:ga) { l.first }
28
+ let(:gb) { l.last }
29
+ end
30
+
31
+ it_behaves_like "an Arrow" do
32
+ let(:l) { described_class.new.keys }
33
+ let(:r) { { x: 42 } }
34
+ let(:f) { :+ }
35
+ let(:ga) { described_class.new.first }
36
+ let(:gb) { described_class.new.last }
37
+ end
38
+ end
@@ -0,0 +1,21 @@
1
+ require 'necromancy'
2
+
3
+ describe Necromancy::Control::Category do
4
+
5
+ let(:l) { described_class.new }
6
+
7
+ shared_examples_for 'a Category' do
8
+
9
+ example { proc(&l > f).(r).should == proc(&f).(proc(&l).(r)) }
10
+ example { proc(&f < l).(r).should == proc(&f).(proc(&l).(r)) }
11
+ example { proc(&l > f > g).(r).should == proc(&g).(proc(&f).(proc(&l).(r))) }
12
+ example { proc(&g < f < l).(r).should == proc(&g).(proc(&f).(proc(&l).(r))) }
13
+ end
14
+
15
+ it_behaves_like "a Category" do
16
+
17
+ let(:r) { '42' }
18
+ let(:f) { described_class.new.to_i }
19
+ let(:g) { described_class.new + 1 }
20
+ end
21
+ end
@@ -0,0 +1,38 @@
1
+ require 'necromancy'
2
+
3
+ describe Necromancy::Control::Alternative do
4
+
5
+ let(:l) { described_class.new }
6
+
7
+ example do
8
+ [{ x: 42}, {}].map(&l[:x] | proc{0}).
9
+ should == [{ x: 42}, {}].map {|h| h[:x] || 0 }
10
+ end
11
+
12
+ example do
13
+ i = 1
14
+ (1..5).map(&l.+ * proc{1}).
15
+ should == (1..5).map {|x| x + i if i }
16
+ end
17
+
18
+ example do
19
+ i = nil
20
+ (1..5).map(&l.+ * proc{}).
21
+ should == (1..5).map {|x| x + i if i }
22
+ end
23
+
24
+ example do
25
+ (1..5).map(&l.odd? >> l.succ | l.pred).
26
+ should == (1..5).map {|x| x.odd? ? x.succ : x.pred }
27
+ end
28
+
29
+ example do
30
+ [{x: 1}, {y: 2}].inject(&l.merge << ->(x,y){x&&y}).
31
+ should == [{x: 1}, {y: 2}].inject {|x, y| x.merge(y) if x && y }
32
+ end
33
+
34
+ example do
35
+ [{x: 1}, nil, {y: 2}].inject(&l.merge << ->(x,y){x&&y}).
36
+ should == [{x: 1}, nil, {y: 2}].inject {|x, y| x.merge(y) if x && y }
37
+ end
38
+ end
@@ -0,0 +1,18 @@
1
+ require 'necromancy'
2
+
3
+ describe Necromancy::Control::Applicative do
4
+
5
+ let(:l) { described_class.new }
6
+
7
+ example do
8
+
9
+ %w(foo bar baz).map(&l.+ * :reverse).
10
+ should == %w(foo bar baz).map {|s| s + s.reverse }
11
+ end
12
+
13
+ example do
14
+
15
+ %w(foo bar baz).map(&l.reverse ** :+).
16
+ should == %w(foo bar baz).map {|s| s + s.reverse }
17
+ end
18
+ end
@@ -0,0 +1,26 @@
1
+ require 'necromancy'
2
+
3
+ describe Necromancy::Control::Arrow do
4
+
5
+ let(:l) { described_class.new }
6
+
7
+ example do
8
+ %w(foo bar baz).map(&l.upcase & :capitalize).
9
+ should == %w(foo bar baz).map {|s| [s.upcase, s.capitalize] }
10
+ end
11
+
12
+ example do
13
+ %w(foo bar baz).map(&l.upcase & :capitalize & :reverse).
14
+ should == %w(foo bar baz).map {|s| [s.upcase, s.capitalize, s.reverse] }
15
+ end
16
+
17
+ example do
18
+ %w(foo bar baz).map(&l.upcase & :capitalize > :+).
19
+ should == %w(foo bar baz).map {|s| s.upcase + s.capitalize }
20
+ end
21
+
22
+ example do
23
+ %w(foo bar baz).map(&l.upcase & :capitalize > l.chars.to_a * :to_sym).
24
+ should == %w(foo bar baz).map {|s| [s.upcase.chars.to_a, s.capitalize.to_sym] }
25
+ end
26
+ end
@@ -0,0 +1,12 @@
1
+ require 'necromancy'
2
+
3
+ describe Necromancy::Control::Category do
4
+
5
+ let(:l) { described_class.new }
6
+
7
+ example do
8
+
9
+ %w(0 1 2).map(&l.to_i > "foo".method(:[])).
10
+ should == %w(0 1 2).map {|x| "foo"[x.to_i] }
11
+ end
12
+ end
@@ -0,0 +1,43 @@
1
+ require 'necromancy'
2
+
3
+ describe Necromancy do
4
+
5
+ let(:l) { described_class.new }
6
+
7
+ example do
8
+ [:foo, :bar, :baz].map(&l.to_s . upcase).
9
+ should == ["FOO", "BAR", "BAZ"]
10
+ end
11
+
12
+ example do
13
+ [:foo, :hoge, :bar, :fuga].select(&l.to_s . length > 3).
14
+ should == [:hoge, :fuga]
15
+ end
16
+
17
+ example do
18
+ qstr = "hoge=fuga&foo=bar"
19
+ Hash[qstr.split(?&).map &l.split(?=)].
20
+ should == {"hoge"=>"fuga", "foo"=>"bar"}
21
+ end
22
+
23
+ example do
24
+ (1..5).map(&l ** 2).
25
+ should == [1, 4, 9, 16, 25]
26
+ end
27
+
28
+ example do
29
+ %w[c++ lisp].map(&(l + "er").upcase).
30
+ should == ["C++ER", "LISPER"]
31
+ end
32
+
33
+ example do
34
+ %w[c++ lisp].map(&l.upcase + "er").
35
+ should == ["C++er", "LISPer"]
36
+ end
37
+
38
+ example do
39
+ procs = %w(succ pred odd?).map &l.to_sym . to_proc
40
+ procs.map(&l.call(1)).
41
+ should == [2, 0, true]
42
+ end
43
+ end
@@ -0,0 +1,33 @@
1
+ require 'necromancy'
2
+
3
+ class TestStr < String
4
+
5
+ def f
6
+ TestStr.new upcase
7
+ end
8
+
9
+ def g(x)
10
+ TestStr.new self + x
11
+ end
12
+ end
13
+
14
+ describe Necromancy do
15
+
16
+ let(:l) { described_class.new }
17
+
18
+ shared_examples_for "a Necromancy" do
19
+
20
+ example { proc(&l.f).(r).should == r.f }
21
+ example { proc(&l.g(x)).(r).should == r.g(x) }
22
+ example { proc(&l.f . g(x)).(r).should == r.f.g(x) }
23
+ example { proc(&l.g(x) . f).(r).should == r.g(x).f }
24
+ example { proc(&l.f . f).(r).should == r.f.f }
25
+ example { proc(&l.g(x) . g(x)).(r).should == r.g(x).g(x) }
26
+ end
27
+
28
+ it_behaves_like "a Necromancy" do
29
+
30
+ let(:r) { TestStr.new 'hoge' }
31
+ let(:x) { 'fuga' }
32
+ end
33
+ end
metadata ADDED
@@ -0,0 +1,99 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: necromancy
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - pasberth
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-04-06 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: rspec
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :development
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: '0'
30
+ description: Necromancy conjures up the functional code.
31
+ email: pasberth@gmail.com
32
+ executables: []
33
+ extensions: []
34
+ extra_rdoc_files: []
35
+ files:
36
+ - Gemfile
37
+ - LICENSE
38
+ - README.rst
39
+ - Rakefile
40
+ - VERSION
41
+ - examples/fizzbuzz.rb
42
+ - examples/pythonic-fizzbuzz.rb
43
+ - examples/using-extensions.rb
44
+ - lib/necromancy.rb
45
+ - lib/necromancy/control.rb
46
+ - lib/necromancy/control/alternative.rb
47
+ - lib/necromancy/control/applicative.rb
48
+ - lib/necromancy/control/arrow.rb
49
+ - lib/necromancy/control/category.rb
50
+ - lib/necromancy/necromancy.rb
51
+ - lib/necromancy/version.rb
52
+ - necromancy.gemspec
53
+ - spec/alternative_spec.rb
54
+ - spec/applicative_spec.rb
55
+ - spec/arrow_spec.rb
56
+ - spec/category_spec.rb
57
+ - spec/examples/alternative_spec.rb
58
+ - spec/examples/applicative_spec.rb
59
+ - spec/examples/arrow_spec.rb
60
+ - spec/examples/category_spec.rb
61
+ - spec/examples/slam_spec.rb
62
+ - spec/necromancy_spec.rb
63
+ homepage: https://github.com/pasberth/necromancy
64
+ licenses:
65
+ - MIT
66
+ post_install_message:
67
+ rdoc_options: []
68
+ require_paths:
69
+ - lib
70
+ required_ruby_version: !ruby/object:Gem::Requirement
71
+ none: false
72
+ requirements:
73
+ - - ! '>='
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ required_rubygems_version: !ruby/object:Gem::Requirement
77
+ none: false
78
+ requirements:
79
+ - - ! '>='
80
+ - !ruby/object:Gem::Version
81
+ version: '0'
82
+ requirements: []
83
+ rubyforge_project:
84
+ rubygems_version: 1.8.24
85
+ signing_key:
86
+ specification_version: 3
87
+ summary: experimental release
88
+ test_files:
89
+ - spec/alternative_spec.rb
90
+ - spec/applicative_spec.rb
91
+ - spec/arrow_spec.rb
92
+ - spec/category_spec.rb
93
+ - spec/examples/alternative_spec.rb
94
+ - spec/examples/applicative_spec.rb
95
+ - spec/examples/arrow_spec.rb
96
+ - spec/examples/category_spec.rb
97
+ - spec/examples/slam_spec.rb
98
+ - spec/necromancy_spec.rb
99
+ has_rdoc: