smullyan 0.1.0 → 0.1.1
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.
- checksums.yaml +4 -4
- data/README.md +2 -2
- data/Rakefile +7 -7
- data/benchmark/combinators_benchmark.rb +133 -0
- data/benchmark/configurable_benchmark.rb +85 -0
- data/lib/smullyan/birds/bluebird.rb +12 -11
- data/lib/smullyan/birds/cardinal.rb +13 -12
- data/lib/smullyan/birds/finch.rb +12 -0
- data/lib/smullyan/birds/identity.rb +2 -2
- data/lib/smullyan/birds/kestrel.rb +4 -4
- data/lib/smullyan/birds/lark.rb +13 -12
- data/lib/smullyan/birds/mockingbird.rb +12 -11
- data/lib/smullyan/birds/starling.rb +3 -3
- data/lib/smullyan/birds/warbler.rb +13 -12
- data/lib/smullyan/birds/why.rb +3 -10
- data/lib/smullyan/configurable_birds.rb +55 -0
- data/lib/smullyan/configuration.rb +55 -0
- data/lib/smullyan/version.rb +2 -2
- data/lib/smullyan.rb +15 -12
- metadata +12 -7
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 5c03c1b73d0e4b82d767b8edcff5cdd4b3746b8f25f0584095826c12aede049e
|
|
4
|
+
data.tar.gz: 1e760c61bd407b69b4f283ecee9aa468c107964fef7d97d72734a8ec298e9ba3
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: f90e78e8f5bb3cdf6293fc09ec9062ae52a36992e656de9b66606e92a260bc7a8462e6d55fefb85e3e77192b73d6d8fa846645ce11fab1b849f8cb6e4234f2aa
|
|
7
|
+
data.tar.gz: d59cc2c3de4d99b1216005319855bc926580bc3d50045a6a4e9983d543d8529b050515bdd83ee230ca2c7927440fb9182d4365dd3c4e5c3d5497a70665733e04
|
data/README.md
CHANGED
|
@@ -23,7 +23,7 @@ Or install it yourself as:
|
|
|
23
23
|
```ruby
|
|
24
24
|
require 'smullyan'
|
|
25
25
|
|
|
26
|
-
# Using
|
|
26
|
+
# Using SKI combinator names
|
|
27
27
|
s = Smullyan::Birds::S
|
|
28
28
|
k = Smullyan::Birds::K
|
|
29
29
|
i = Smullyan::Birds::I
|
|
@@ -64,4 +64,4 @@ Bug reports and pull requests are welcome on GitHub at https://github.com/philcr
|
|
|
64
64
|
|
|
65
65
|
## License
|
|
66
66
|
|
|
67
|
-
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
|
67
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
data/Rakefile
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require
|
|
4
|
-
require
|
|
3
|
+
require 'bundler/gem_tasks'
|
|
4
|
+
require 'rake/testtask'
|
|
5
5
|
|
|
6
6
|
Rake::TestTask.new(:test) do |t|
|
|
7
|
-
t.libs <<
|
|
8
|
-
t.libs <<
|
|
9
|
-
t.test_files = FileList[
|
|
7
|
+
t.libs << 'test'
|
|
8
|
+
t.libs << 'lib'
|
|
9
|
+
t.test_files = FileList['test/**/*_test.rb', 'test/**/test_*.rb']
|
|
10
10
|
end
|
|
11
11
|
|
|
12
|
-
require
|
|
12
|
+
require 'rubocop/rake_task'
|
|
13
13
|
|
|
14
14
|
RuboCop::RakeTask.new
|
|
15
15
|
|
|
16
|
-
task default: %i[test rubocop]
|
|
16
|
+
task default: %i[test rubocop]
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'benchmark/ips'
|
|
4
|
+
require_relative '../lib/smullyan'
|
|
5
|
+
|
|
6
|
+
# Test functions
|
|
7
|
+
identity = ->(x) { x }
|
|
8
|
+
double = ->(x) { x * 2 }
|
|
9
|
+
add_one = ->(x) { x + 1 }
|
|
10
|
+
const_forty_two = ->(_x) { 42 }
|
|
11
|
+
|
|
12
|
+
puts 'Benchmarking Combinator Implementations'
|
|
13
|
+
puts '=' * 50
|
|
14
|
+
puts
|
|
15
|
+
|
|
16
|
+
# NOTE: I combinator doesn't have a direct implementation since it's already minimal
|
|
17
|
+
|
|
18
|
+
# Benchmark B combinator
|
|
19
|
+
puts 'B combinator (Bluebird - composition):'
|
|
20
|
+
Benchmark.ips do |x|
|
|
21
|
+
x.report('B (derived)') do
|
|
22
|
+
Smullyan::Birds::B.call(double).call(add_one).call(5)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
x.report('B_direct') do
|
|
26
|
+
Smullyan::Birds::B_direct.call(double).call(add_one).call(5)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
x.compare!
|
|
30
|
+
end
|
|
31
|
+
puts
|
|
32
|
+
|
|
33
|
+
# Benchmark C combinator
|
|
34
|
+
puts 'C combinator (Cardinal - flip):'
|
|
35
|
+
Benchmark.ips do |x|
|
|
36
|
+
subtract = ->(x) { ->(y) { x - y } }
|
|
37
|
+
|
|
38
|
+
x.report('C (derived)') do
|
|
39
|
+
Smullyan::Birds::C.call(subtract).call(10).call(3)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
x.report('C_direct') do
|
|
43
|
+
Smullyan::Birds::C_direct.call(subtract).call(10).call(3)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
x.compare!
|
|
47
|
+
end
|
|
48
|
+
puts
|
|
49
|
+
|
|
50
|
+
# Benchmark W combinator
|
|
51
|
+
puts 'W combinator (Warbler - duplication):'
|
|
52
|
+
Benchmark.ips do |x|
|
|
53
|
+
# W needs a function that can take itself as argument
|
|
54
|
+
pair = ->(x) { ->(y) { [x, y] } }
|
|
55
|
+
|
|
56
|
+
x.report('W (derived)') do
|
|
57
|
+
Smullyan::Birds::W.call(pair).call(21)
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
x.report('W_direct') do
|
|
61
|
+
Smullyan::Birds::W_direct.call(pair).call(21)
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
x.compare!
|
|
65
|
+
end
|
|
66
|
+
puts
|
|
67
|
+
|
|
68
|
+
# Benchmark M combinator
|
|
69
|
+
puts 'M combinator (Mockingbird - self-application):'
|
|
70
|
+
Benchmark.ips do |x|
|
|
71
|
+
x.report('M (derived)') do
|
|
72
|
+
Smullyan::Birds::M.call(const_forty_two)
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
x.report('M_direct') do
|
|
76
|
+
Smullyan::Birds::M_direct.call(const_forty_two)
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
x.compare!
|
|
80
|
+
end
|
|
81
|
+
puts
|
|
82
|
+
|
|
83
|
+
# Benchmark L combinator
|
|
84
|
+
puts 'L combinator (Lark):'
|
|
85
|
+
Benchmark.ips do |x|
|
|
86
|
+
x.report('L (derived)') do
|
|
87
|
+
Smullyan::Birds::L.call(identity).call(const_forty_two)
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
x.report('L_direct') do
|
|
91
|
+
Smullyan::Birds::L_direct.call(identity).call(const_forty_two)
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
x.compare!
|
|
95
|
+
end
|
|
96
|
+
puts
|
|
97
|
+
|
|
98
|
+
# Complex composition benchmark
|
|
99
|
+
puts 'Complex composition (B B B pattern):'
|
|
100
|
+
Benchmark.ips do |x|
|
|
101
|
+
triple = ->(x) { x * 3 }
|
|
102
|
+
|
|
103
|
+
x.report('B (derived) composed') do
|
|
104
|
+
b = Smullyan::Birds::B
|
|
105
|
+
b.call(b.call(triple).call(double)).call(add_one).call(2)
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
x.report('B_direct composed') do
|
|
109
|
+
b = Smullyan::Birds::B_direct
|
|
110
|
+
b.call(b.call(triple).call(double)).call(add_one).call(2)
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
x.compare!
|
|
114
|
+
end
|
|
115
|
+
puts
|
|
116
|
+
|
|
117
|
+
# Y combinator benchmark (careful - recursive!)
|
|
118
|
+
# Note: Y and Z don't have separate direct implementations since they're already optimized for Ruby
|
|
119
|
+
puts 'Y combinator (limited recursion):'
|
|
120
|
+
Benchmark.ips do |x|
|
|
121
|
+
# Simple recursive function that terminates quickly
|
|
122
|
+
sum_to_n = ->(f) { ->(n) { n <= 0 ? 0 : n + f.call(n - 1) } }
|
|
123
|
+
|
|
124
|
+
x.report('Y combinator') do
|
|
125
|
+
Smullyan::Birds::Y.call(sum_to_n).call(10)
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
x.report('Z combinator') do
|
|
129
|
+
Smullyan::Birds::Z.call(sum_to_n).call(10)
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
x.compare!
|
|
133
|
+
end
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'benchmark/ips'
|
|
4
|
+
require_relative '../lib/smullyan'
|
|
5
|
+
|
|
6
|
+
# Test functions
|
|
7
|
+
identity = ->(x) { x }
|
|
8
|
+
double = ->(x) { x * 2 }
|
|
9
|
+
add_one = ->(x) { x + 1 }
|
|
10
|
+
const_forty_two = ->(_x) { 42 }
|
|
11
|
+
|
|
12
|
+
puts 'Benchmarking Configurable Combinator Implementations'
|
|
13
|
+
puts '=' * 50
|
|
14
|
+
puts
|
|
15
|
+
|
|
16
|
+
# Benchmark B combinator with configuration switching
|
|
17
|
+
puts 'B combinator (Bluebird - composition) via ConfigurableBirds:'
|
|
18
|
+
Benchmark.ips do |x|
|
|
19
|
+
# Reset to derived
|
|
20
|
+
Smullyan.configuration.use_direct(:b, value: false)
|
|
21
|
+
|
|
22
|
+
x.report('B (config: derived)') do
|
|
23
|
+
Smullyan::ConfigurableBirds.B.call(double).call(add_one).call(5)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# Switch to direct
|
|
27
|
+
Smullyan.configuration.use_direct(:b, value: true)
|
|
28
|
+
|
|
29
|
+
x.report('B (config: direct)') do
|
|
30
|
+
Smullyan::ConfigurableBirds.B.call(double).call(add_one).call(5)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
x.compare!
|
|
34
|
+
end
|
|
35
|
+
puts
|
|
36
|
+
|
|
37
|
+
# Benchmark all combinators with direct implementation
|
|
38
|
+
puts 'All combinators with direct implementation enabled:'
|
|
39
|
+
Smullyan.configure do |config|
|
|
40
|
+
config.use_all_direct(value: true)
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
Benchmark.ips do |x|
|
|
44
|
+
x.report('B direct') do
|
|
45
|
+
Smullyan::ConfigurableBirds.B.call(double).call(add_one).call(5)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
x.report('C direct') do
|
|
49
|
+
subtract = ->(x) { ->(y) { x - y } }
|
|
50
|
+
Smullyan::ConfigurableBirds.C.call(subtract).call(10).call(3)
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
x.report('W direct') do
|
|
54
|
+
pair = ->(x) { ->(y) { [x, y] } }
|
|
55
|
+
Smullyan::ConfigurableBirds.W.call(pair).call(21)
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
x.report('M direct') do
|
|
59
|
+
Smullyan::ConfigurableBirds.M.call(const_forty_two)
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
x.report('L direct') do
|
|
63
|
+
Smullyan::ConfigurableBirds.L.call(identity).call(const_forty_two)
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
puts
|
|
67
|
+
|
|
68
|
+
# Compare configuration overhead
|
|
69
|
+
puts 'Configuration overhead comparison:'
|
|
70
|
+
Smullyan.configuration.use_direct(:b, false)
|
|
71
|
+
|
|
72
|
+
Benchmark.ips do |x|
|
|
73
|
+
x.report('B via ConfigurableBirds') do
|
|
74
|
+
Smullyan::ConfigurableBirds.B.call(double).call(add_one).call(5)
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
x.report('B direct constant') do
|
|
78
|
+
Smullyan::Birds::B.call(double).call(add_one).call(5)
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
x.compare!
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
# Reset configuration
|
|
85
|
+
Smullyan.reset_configuration!
|
|
@@ -1,20 +1,21 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require_relative
|
|
4
|
-
require_relative
|
|
3
|
+
require_relative 'starling'
|
|
4
|
+
require_relative 'kestrel'
|
|
5
5
|
|
|
6
6
|
module Smullyan
|
|
7
7
|
module Birds
|
|
8
8
|
# The Bluebird - function composition
|
|
9
9
|
# Bluebird x y z = x (y z)
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
10
|
+
|
|
11
|
+
# Derived implementation: B = S (K S) K
|
|
12
|
+
B_derived = S.call(K.call(S)).call(K)
|
|
13
|
+
|
|
13
14
|
# Direct implementation for comparison/efficiency
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
#
|
|
17
|
-
|
|
18
|
-
|
|
15
|
+
B_direct = ->(x) { ->(y) { ->(z) { x.call(y.call(z)) } } }
|
|
16
|
+
|
|
17
|
+
# Default to derived implementation for backward compatibility
|
|
18
|
+
Bluebird = B_derived
|
|
19
|
+
B = Bluebird
|
|
19
20
|
end
|
|
20
|
-
end
|
|
21
|
+
end
|
|
@@ -1,21 +1,22 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require_relative
|
|
4
|
-
require_relative
|
|
5
|
-
require_relative
|
|
3
|
+
require_relative 'starling'
|
|
4
|
+
require_relative 'bluebird'
|
|
5
|
+
require_relative 'kestrel'
|
|
6
6
|
|
|
7
7
|
module Smullyan
|
|
8
8
|
module Birds
|
|
9
9
|
# The Cardinal - flips the order of arguments
|
|
10
10
|
# Cardinal x y z = x z y
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
11
|
+
|
|
12
|
+
# Derived implementation: C = S (B B S) (K K)
|
|
13
|
+
C_derived = S.call(B.call(B).call(S)).call(K.call(K))
|
|
14
|
+
|
|
14
15
|
# Direct implementation for comparison/efficiency
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
#
|
|
18
|
-
|
|
19
|
-
|
|
16
|
+
C_direct = ->(x) { ->(y) { ->(z) { x.call(z).call(y) } } }
|
|
17
|
+
|
|
18
|
+
# Default to derived implementation for backward compatibility
|
|
19
|
+
Cardinal = C_derived
|
|
20
|
+
C = Cardinal
|
|
20
21
|
end
|
|
21
|
-
end
|
|
22
|
+
end
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
module Smullyan
|
|
2
|
+
module Birds
|
|
3
|
+
# The Finch bird (F combinator)
|
|
4
|
+
# The finch applies its third argument
|
|
5
|
+
# to the first and second arguments in reverse order.
|
|
6
|
+
# Finch x y z = z y x
|
|
7
|
+
Finch = ->(x) { ->(y) { ->(z) { z.call(y).call(x) } } }
|
|
8
|
+
|
|
9
|
+
# Traditional combinator name
|
|
10
|
+
F = Finch # F combinator
|
|
11
|
+
end
|
|
12
|
+
end
|
|
@@ -5,9 +5,9 @@ module Smullyan
|
|
|
5
5
|
# The Identity bird - returns its argument unchanged
|
|
6
6
|
# Identity x = x
|
|
7
7
|
Identity = ->(x) { x }
|
|
8
|
-
|
|
8
|
+
|
|
9
9
|
# Traditional combinator names
|
|
10
10
|
I = Identity # I combinator
|
|
11
11
|
Idiot = Identity # Alternative name from Smullyan's book
|
|
12
12
|
end
|
|
13
|
-
end
|
|
13
|
+
end
|
|
@@ -4,9 +4,9 @@ module Smullyan
|
|
|
4
4
|
module Birds
|
|
5
5
|
# The Kestrel - returns the first of two arguments
|
|
6
6
|
# Kestrel x y = x
|
|
7
|
-
Kestrel = ->(x) { ->(
|
|
8
|
-
|
|
7
|
+
Kestrel = ->(x) { ->(_y) { x } }
|
|
8
|
+
|
|
9
9
|
# Traditional combinator name
|
|
10
|
-
K = Kestrel
|
|
10
|
+
K = Kestrel # K combinator
|
|
11
11
|
end
|
|
12
|
-
end
|
|
12
|
+
end
|
data/lib/smullyan/birds/lark.rb
CHANGED
|
@@ -1,21 +1,22 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require_relative
|
|
4
|
-
require_relative
|
|
5
|
-
require_relative
|
|
3
|
+
require_relative 'cardinal'
|
|
4
|
+
require_relative 'bluebird'
|
|
5
|
+
require_relative 'mockingbird'
|
|
6
6
|
|
|
7
7
|
module Smullyan
|
|
8
8
|
module Birds
|
|
9
9
|
# The Lark - applies first argument to result of second argument applied to itself
|
|
10
10
|
# Lark x y = x (y y)
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
11
|
+
|
|
12
|
+
# Derived implementation: L = C B M
|
|
13
|
+
L_derived = C.call(B).call(M)
|
|
14
|
+
|
|
14
15
|
# Direct implementation for comparison/efficiency
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
#
|
|
18
|
-
|
|
19
|
-
|
|
16
|
+
L_direct = ->(x) { ->(y) { x.call(y.call(y)) } }
|
|
17
|
+
|
|
18
|
+
# Default to derived implementation for backward compatibility
|
|
19
|
+
Lark = L_derived
|
|
20
|
+
L = Lark
|
|
20
21
|
end
|
|
21
|
-
end
|
|
22
|
+
end
|
|
@@ -1,20 +1,21 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require_relative
|
|
4
|
-
require_relative
|
|
3
|
+
require_relative 'starling'
|
|
4
|
+
require_relative 'identity'
|
|
5
5
|
|
|
6
6
|
module Smullyan
|
|
7
7
|
module Birds
|
|
8
8
|
# The Mockingbird - self-application
|
|
9
9
|
# Mockingbird x = x x
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
10
|
+
|
|
11
|
+
# Derived implementation: M = S I I
|
|
12
|
+
M_derived = S.call(I).call(I)
|
|
13
|
+
|
|
13
14
|
# Direct implementation for comparison/efficiency
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
#
|
|
17
|
-
|
|
18
|
-
|
|
15
|
+
M_direct = ->(x) { x.call(x) }
|
|
16
|
+
|
|
17
|
+
# Default to derived implementation for backward compatibility
|
|
18
|
+
Mockingbird = M_derived
|
|
19
|
+
M = Mockingbird
|
|
19
20
|
end
|
|
20
|
-
end
|
|
21
|
+
end
|
|
@@ -5,8 +5,8 @@ module Smullyan
|
|
|
5
5
|
# The Starling - distributes its third argument to both its first and second
|
|
6
6
|
# Starling x y z = x z (y z)
|
|
7
7
|
Starling = ->(x) { ->(y) { ->(z) { x.call(z).call(y.call(z)) } } }
|
|
8
|
-
|
|
8
|
+
|
|
9
9
|
# Traditional combinator name
|
|
10
|
-
S = Starling
|
|
10
|
+
S = Starling # S combinator
|
|
11
11
|
end
|
|
12
|
-
end
|
|
12
|
+
end
|
|
@@ -1,21 +1,22 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require_relative
|
|
4
|
-
require_relative
|
|
5
|
-
require_relative
|
|
3
|
+
require_relative 'cardinal'
|
|
4
|
+
require_relative 'starling'
|
|
5
|
+
require_relative 'identity'
|
|
6
6
|
|
|
7
7
|
module Smullyan
|
|
8
8
|
module Birds
|
|
9
9
|
# The Warbler - duplicates its argument
|
|
10
10
|
# Warbler x y = x y y
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
11
|
+
|
|
12
|
+
# Derived implementation: W = C S I
|
|
13
|
+
W_derived = C.call(S).call(I)
|
|
14
|
+
|
|
14
15
|
# Direct implementation for comparison/efficiency
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
#
|
|
18
|
-
|
|
19
|
-
|
|
16
|
+
W_direct = ->(x) { ->(y) { x.call(y).call(y) } }
|
|
17
|
+
|
|
18
|
+
# Default to derived implementation for backward compatibility
|
|
19
|
+
Warbler = W_derived
|
|
20
|
+
W = Warbler
|
|
20
21
|
end
|
|
21
|
-
end
|
|
22
|
+
end
|
data/lib/smullyan/birds/why.rb
CHANGED
|
@@ -5,21 +5,14 @@ module Smullyan
|
|
|
5
5
|
# The Why bird (Y combinator) - fixed-point combinator
|
|
6
6
|
# Why f = f (Why f)
|
|
7
7
|
# Here's a practical implementation that works in applicative-order languages like Ruby
|
|
8
|
-
Why =
|
|
8
|
+
Why = lambda { |f|
|
|
9
9
|
->(x) { f.call(->(v) { x.call(x).call(v) }) }.call(
|
|
10
10
|
->(x) { f.call(->(v) { x.call(x).call(v) }) }
|
|
11
11
|
)
|
|
12
12
|
}
|
|
13
|
-
|
|
14
|
-
# Alternative Z combinator (strict fixed-point) that's more suitable for eager evaluation
|
|
15
|
-
Z = ->(f) {
|
|
16
|
-
->(x) { f.call(->(v) { x.call(x).call(v) }) }.call(
|
|
17
|
-
->(x) { f.call(->(v) { x.call(x).call(v) }) }
|
|
18
|
-
)
|
|
19
|
-
}
|
|
20
|
-
|
|
13
|
+
|
|
21
14
|
# Traditional combinator names
|
|
22
15
|
Y = Why # Y combinator (fixed-point)
|
|
23
16
|
Sage = Why # Y combinator (alternative name)
|
|
24
17
|
end
|
|
25
|
-
end
|
|
18
|
+
end
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Smullyan
|
|
4
|
+
# Module for getting configurable combinator implementations
|
|
5
|
+
module ConfigurableBirds
|
|
6
|
+
# rubocop:disable Naming/MethodName
|
|
7
|
+
class << self
|
|
8
|
+
def B
|
|
9
|
+
if Smullyan.configuration.direct?(:b)
|
|
10
|
+
Birds::B_direct
|
|
11
|
+
else
|
|
12
|
+
Birds::B_derived
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
alias Bluebird B
|
|
16
|
+
|
|
17
|
+
def C
|
|
18
|
+
if Smullyan.configuration.direct?(:c)
|
|
19
|
+
Birds::C_direct
|
|
20
|
+
else
|
|
21
|
+
Birds::C_derived
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
alias Cardinal C
|
|
25
|
+
|
|
26
|
+
def W
|
|
27
|
+
if Smullyan.configuration.direct?(:w)
|
|
28
|
+
Birds::W_direct
|
|
29
|
+
else
|
|
30
|
+
Birds::W_derived
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
alias Warbler W
|
|
34
|
+
|
|
35
|
+
def M
|
|
36
|
+
if Smullyan.configuration.direct?(:m)
|
|
37
|
+
Birds::M_direct
|
|
38
|
+
else
|
|
39
|
+
Birds::M_derived
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
alias Mockingbird M
|
|
43
|
+
|
|
44
|
+
def L
|
|
45
|
+
if Smullyan.configuration.direct?(:l)
|
|
46
|
+
Birds::L_direct
|
|
47
|
+
else
|
|
48
|
+
Birds::L_derived
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
alias Lark L
|
|
52
|
+
end
|
|
53
|
+
# rubocop:enable Naming/MethodName
|
|
54
|
+
end
|
|
55
|
+
end
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# The Smullyan module provides implementations of combinators from
|
|
4
|
+
# Raymond Smullyan's "To Mock a Mockingbird" and related works.
|
|
5
|
+
module Smullyan
|
|
6
|
+
# Configuration for selecting combinator implementations
|
|
7
|
+
class Configuration
|
|
8
|
+
attr_accessor :use_direct_implementation
|
|
9
|
+
|
|
10
|
+
def initialize
|
|
11
|
+
@use_direct_implementation = {
|
|
12
|
+
b: false, # Bluebird
|
|
13
|
+
c: false, # Cardinal
|
|
14
|
+
w: false, # Warbler
|
|
15
|
+
m: false, # Mockingbird
|
|
16
|
+
l: false # Lark
|
|
17
|
+
}
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
# Configure specific combinators
|
|
21
|
+
def use_direct(combinator, value: true)
|
|
22
|
+
key = combinator.to_s.downcase.to_sym
|
|
23
|
+
raise ArgumentError, "Unknown combinator: #{combinator}" unless @use_direct_implementation.key?(key)
|
|
24
|
+
|
|
25
|
+
@use_direct_implementation[key] = value
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# Configure all combinators at once
|
|
29
|
+
def use_all_direct(value: true)
|
|
30
|
+
@use_direct_implementation.each_key do |key|
|
|
31
|
+
@use_direct_implementation[key] = value
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# Check if a combinator should use direct implementation
|
|
36
|
+
def direct?(combinator)
|
|
37
|
+
key = combinator.to_s.downcase.to_sym
|
|
38
|
+
@use_direct_implementation[key] || false
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
class << self
|
|
43
|
+
def configuration
|
|
44
|
+
@configuration ||= Configuration.new
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def configure
|
|
48
|
+
yield(configuration)
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def reset_configuration!
|
|
52
|
+
@configuration = Configuration.new
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
data/lib/smullyan/version.rb
CHANGED
data/lib/smullyan.rb
CHANGED
|
@@ -1,20 +1,23 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require_relative
|
|
4
|
-
require_relative
|
|
5
|
-
require_relative
|
|
6
|
-
require_relative
|
|
7
|
-
require_relative
|
|
8
|
-
require_relative
|
|
9
|
-
require_relative
|
|
10
|
-
require_relative
|
|
11
|
-
require_relative
|
|
12
|
-
require_relative
|
|
3
|
+
require_relative 'smullyan/version'
|
|
4
|
+
require_relative 'smullyan/configuration'
|
|
5
|
+
require_relative 'smullyan/birds/identity'
|
|
6
|
+
require_relative 'smullyan/birds/kestrel'
|
|
7
|
+
require_relative 'smullyan/birds/starling'
|
|
8
|
+
require_relative 'smullyan/birds/bluebird'
|
|
9
|
+
require_relative 'smullyan/birds/cardinal'
|
|
10
|
+
require_relative 'smullyan/birds/warbler'
|
|
11
|
+
require_relative 'smullyan/birds/mockingbird'
|
|
12
|
+
require_relative 'smullyan/birds/why'
|
|
13
|
+
require_relative 'smullyan/birds/lark'
|
|
14
|
+
require_relative 'smullyan/birds/finch'
|
|
15
|
+
require_relative 'smullyan/configurable_birds'
|
|
13
16
|
|
|
14
17
|
module Smullyan
|
|
15
18
|
class Error < StandardError; end
|
|
16
|
-
|
|
19
|
+
|
|
17
20
|
# All combinators are now in separate files
|
|
18
21
|
module Birds
|
|
19
22
|
end
|
|
20
|
-
end
|
|
23
|
+
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: smullyan
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.1.
|
|
4
|
+
version: 0.1.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Phil Crissman
|
|
@@ -10,33 +10,33 @@ cert_chain: []
|
|
|
10
10
|
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
11
|
dependencies:
|
|
12
12
|
- !ruby/object:Gem::Dependency
|
|
13
|
-
name:
|
|
13
|
+
name: minitest
|
|
14
14
|
requirement: !ruby/object:Gem::Requirement
|
|
15
15
|
requirements:
|
|
16
16
|
- - "~>"
|
|
17
17
|
- !ruby/object:Gem::Version
|
|
18
|
-
version: '
|
|
18
|
+
version: '5.18'
|
|
19
19
|
type: :development
|
|
20
20
|
prerelease: false
|
|
21
21
|
version_requirements: !ruby/object:Gem::Requirement
|
|
22
22
|
requirements:
|
|
23
23
|
- - "~>"
|
|
24
24
|
- !ruby/object:Gem::Version
|
|
25
|
-
version: '
|
|
25
|
+
version: '5.18'
|
|
26
26
|
- !ruby/object:Gem::Dependency
|
|
27
|
-
name:
|
|
27
|
+
name: rake
|
|
28
28
|
requirement: !ruby/object:Gem::Requirement
|
|
29
29
|
requirements:
|
|
30
30
|
- - "~>"
|
|
31
31
|
- !ruby/object:Gem::Version
|
|
32
|
-
version: '
|
|
32
|
+
version: '13.0'
|
|
33
33
|
type: :development
|
|
34
34
|
prerelease: false
|
|
35
35
|
version_requirements: !ruby/object:Gem::Requirement
|
|
36
36
|
requirements:
|
|
37
37
|
- - "~>"
|
|
38
38
|
- !ruby/object:Gem::Version
|
|
39
|
-
version: '
|
|
39
|
+
version: '13.0'
|
|
40
40
|
- !ruby/object:Gem::Dependency
|
|
41
41
|
name: rubocop
|
|
42
42
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -77,9 +77,12 @@ files:
|
|
|
77
77
|
- LICENSE
|
|
78
78
|
- README.md
|
|
79
79
|
- Rakefile
|
|
80
|
+
- benchmark/combinators_benchmark.rb
|
|
81
|
+
- benchmark/configurable_benchmark.rb
|
|
80
82
|
- lib/smullyan.rb
|
|
81
83
|
- lib/smullyan/birds/bluebird.rb
|
|
82
84
|
- lib/smullyan/birds/cardinal.rb
|
|
85
|
+
- lib/smullyan/birds/finch.rb
|
|
83
86
|
- lib/smullyan/birds/identity.rb
|
|
84
87
|
- lib/smullyan/birds/kestrel.rb
|
|
85
88
|
- lib/smullyan/birds/lark.rb
|
|
@@ -87,6 +90,8 @@ files:
|
|
|
87
90
|
- lib/smullyan/birds/starling.rb
|
|
88
91
|
- lib/smullyan/birds/warbler.rb
|
|
89
92
|
- lib/smullyan/birds/why.rb
|
|
93
|
+
- lib/smullyan/configurable_birds.rb
|
|
94
|
+
- lib/smullyan/configuration.rb
|
|
90
95
|
- lib/smullyan/version.rb
|
|
91
96
|
homepage: https://github.com/philcrissman/smullyan
|
|
92
97
|
licenses:
|