necromancy 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile +2 -0
- data/LICENSE +7 -0
- data/README.rst +141 -0
- data/Rakefile +1 -0
- data/VERSION +1 -0
- data/examples/fizzbuzz.rb +7 -0
- data/examples/pythonic-fizzbuzz.rb +8 -0
- data/examples/using-extensions.rb +6 -0
- data/lib/necromancy/control/alternative.rb +47 -0
- data/lib/necromancy/control/applicative.rb +30 -0
- data/lib/necromancy/control/arrow.rb +23 -0
- data/lib/necromancy/control/category.rb +20 -0
- data/lib/necromancy/control.rb +47 -0
- data/lib/necromancy/necromancy.rb +82 -0
- data/lib/necromancy/version.rb +4 -0
- data/lib/necromancy.rb +12 -0
- data/necromancy.gemspec +16 -0
- data/spec/alternative_spec.rb +89 -0
- data/spec/applicative_spec.rb +48 -0
- data/spec/arrow_spec.rb +38 -0
- data/spec/category_spec.rb +21 -0
- data/spec/examples/alternative_spec.rb +38 -0
- data/spec/examples/applicative_spec.rb +18 -0
- data/spec/examples/arrow_spec.rb +26 -0
- data/spec/examples/category_spec.rb +12 -0
- data/spec/examples/slam_spec.rb +43 -0
- data/spec/necromancy_spec.rb +33 -0
- metadata +99 -0
data/Gemfile
ADDED
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,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
|
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
|
data/necromancy.gemspec
ADDED
@@ -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
|
data/spec/arrow_spec.rb
ADDED
@@ -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,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:
|