deterministic 0.14.1 → 0.15.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,3 +1,3 @@
1
1
  module Deterministic
2
- VERSION = "0.14.1"
2
+ VERSION = "0.15.0"
3
3
  end
@@ -0,0 +1,67 @@
1
+ require 'spec_helper'
2
+ require 'deterministic/enum'
3
+
4
+ Amount = Deterministic::enum {
5
+ Due(:amount)
6
+ Paid(:amount)
7
+ Info(:amount)
8
+ }
9
+
10
+ class Amount
11
+ def self.from_f(f)
12
+ f >= 0 ? Amount::Due.new(f) : Amount::Paid.new(-1 * f)
13
+ end
14
+ end
15
+
16
+ Deterministic::impl(Amount) {
17
+ def to_s
18
+ match {
19
+ Due(a) { "%0.2f" % [a] }
20
+ Paid(a) { "-%0.2f" % [a] }
21
+ Info(a) { "(%0.2f)" % [a] }
22
+ }
23
+ end
24
+
25
+ def to_f
26
+ match {
27
+ Info(a) { 0 }
28
+ Due(a) { a }
29
+ Paid(a) { -1 * a }
30
+ }
31
+ end
32
+
33
+ def +(other)
34
+ raise TypeError "Expected other to be an Amount, got #{other.class}" unless other.is_a? Amount
35
+
36
+ Amount.from_f(to_f + other.to_f)
37
+ end
38
+ }
39
+
40
+ describe Amount do
41
+ def Due(a); Amount::Due.new(a); end
42
+ def Paid(a); Amount::Paid.new(a); end
43
+ def Info(a); Amount::Info.new(a); end
44
+
45
+ it "due" do
46
+ amount = Amount::Due.new(100.2)
47
+ expect(amount.to_s).to eq "100.20"
48
+ end
49
+
50
+ it "paid" do
51
+ amount = Amount::Paid.new(100.1)
52
+ expect(amount.to_s).to eq "-100.10"
53
+ end
54
+
55
+ it "paid" do
56
+ amount = Amount::Info.new(100.31)
57
+ expect(amount.to_s).to eq "(100.31)"
58
+ end
59
+
60
+ it "+" do
61
+ expect(Due(10) + Paid(20)).to eq Paid(10)
62
+ expect(Due(10) + Paid(10)).to eq Due(0)
63
+ expect(Due(10) + Due(10)).to eq Due(20)
64
+ expect(Paid(10) + Paid(10)).to eq Paid(20)
65
+ expect(Paid(10) + Due(1) + Info(99)).to eq Paid(9)
66
+ end
67
+ end
@@ -11,8 +11,8 @@ class ElasticSearchConfig
11
11
 
12
12
  def hosts
13
13
  Option.any?(proc_env["RESFINITY_LOG_CLIENT_ES_HOST"]).match {
14
- some { |s| { hosts: s.split(/, */) } }
15
- none { default_hosts }
14
+ Some(s) { { hosts: s.split(/, */) } }
15
+ None() { default_hosts }
16
16
  }
17
17
  end
18
18
 
@@ -31,6 +31,8 @@ private
31
31
  end
32
32
 
33
33
  describe ElasticSearchConfig do
34
+ pending("match exec context must have access to parents block binding context") {
35
+
34
36
  let(:cfg) { ElasticSearchConfig.new(environment, env) }
35
37
  context "test" do
36
38
  let(:environment) { "test" }
@@ -70,4 +72,5 @@ describe ElasticSearchConfig do
70
72
  specify { expect(cfg.hosts).to eq({ hosts: ["acc.resfinity.net:9200"] }) }
71
73
  end
72
74
  end
75
+ }
73
76
  end
@@ -9,7 +9,7 @@ module Deterministic
9
9
  end
10
10
 
11
11
  class BookingController
12
- include Deterministic
12
+ include Deterministic::Prelude::Result
13
13
  include Deterministic::Procify
14
14
 
15
15
  Context = Struct.new(:booking, :ability, :format)
@@ -0,0 +1,183 @@
1
+ require 'deterministic/enum'
2
+
3
+ List = Deterministic::enum {
4
+ Cons(:head, :tail)
5
+ Nil()
6
+ }
7
+
8
+ class List
9
+ def self.[](*ary)
10
+ ary.reverse.inject(Nil.new) { |xs, x| xs.append(x) }
11
+ end
12
+
13
+ def self.empty
14
+ @empty ||= Nil.new
15
+ end
16
+ end
17
+
18
+ Deterministic::impl(List) {
19
+ class EmptyListError < StandardError; end
20
+
21
+ def append(elem)
22
+ List::Cons.new(elem, self)
23
+ end
24
+
25
+ def null?
26
+ is_a? Nil
27
+ end
28
+
29
+ def first
30
+ match {
31
+ Cons(_, _) { |c| c }
32
+ Nil() { |n| n }
33
+ }
34
+ end
35
+
36
+ def last
37
+ match {
38
+ Cons(h, t, where { t.null? }) { |c| return c }
39
+ Cons(_, t) { t.last }
40
+ Nil() { |n| n }
41
+ }
42
+ end
43
+
44
+ def head
45
+ match {
46
+ Cons(h, _) { h }
47
+ Nil() { |n| n }
48
+ }
49
+ end
50
+
51
+ def tail
52
+ match {
53
+ Cons(_, t) { t }
54
+ Nil() { |n| raise EmptyListError }
55
+ }
56
+ end
57
+
58
+ def init
59
+ match {
60
+ Cons(h, t, where { |c| t.tail.null? } ) { |c| Cons.new(h, Nil.new) }
61
+ Cons(h, t) { |c| Cons.new(h, t.init) }
62
+ Nil() { raise EmptyListError }
63
+ }
64
+ end
65
+
66
+ def filter(&pred)
67
+ match {
68
+ Cons(h, t, where { pred.(h) }) { |c| Cons.new(h, t.filter(&pred)) }
69
+ Cons(_, t) { t.filter(&pred) }
70
+ Nil() { |n| n }
71
+ }
72
+ end
73
+
74
+ # The find function takes a predicate and a list and returns the first element in the list matching the predicate,
75
+ # or None if there is no such element.
76
+ def find(&pred)
77
+ match {
78
+ Nil() { Deterministic::Option::None.new }
79
+ Cons(h, t) { if pred.(h) then Deterministic::Option::Some.new(h) else t.find(&pred) end }
80
+ }
81
+ end
82
+
83
+ def length
84
+ match {
85
+ Cons(h, t) { 1 + t.length }
86
+ Nil() { 0 }
87
+ }
88
+ end
89
+
90
+ def map(&fn)
91
+ match {
92
+ Cons(h, t) { Cons.new(fn.(h), t.map(&fn)) }
93
+ Nil() { |n| n }
94
+ }
95
+ end
96
+
97
+ def sum
98
+ foldl(0, &:+)
99
+ end
100
+
101
+ def foldl(start, &fn)
102
+ match {
103
+ Nil() { start }
104
+ # foldl f z (x:xs) = foldl f (f z x) xs
105
+ Cons(h, t) { t.foldl(fn.(start, h), &fn) }
106
+ }
107
+ end
108
+
109
+ def foldl1(&fn)
110
+ match {
111
+ Nil() { raise EmptyListError }
112
+ Cons(h, t) { t.foldl(h, &fn)}
113
+ }
114
+ end
115
+
116
+ def foldr(start, &fn)
117
+ match {
118
+ Nil() { start }
119
+ # foldr f z (x:xs) = f x (foldr f z xs)
120
+ Cons(h, t) { fn.(h, t.foldr(start, &fn)) }
121
+ }
122
+ end
123
+
124
+ def foldr1(&fn)
125
+ match {
126
+ Nil() { raise EmptyListError }
127
+ Cons(h, t, where { t.null? }) { h }
128
+ # foldr1 f (x:xs) = f x (foldr1 f xs)
129
+ Cons(h, t) { fn.(h, t.foldr1(&fn)) }
130
+ }
131
+ end
132
+
133
+ def take(n)
134
+ match {
135
+ Cons(h, t, where { n > 0 }) { Cons.new(h, t.take(n - 1))}
136
+ Cons(_, _) { Nil.new }
137
+ Nil() { raise EmptyListError}
138
+ }
139
+ end
140
+
141
+ def drop(n)
142
+ match {
143
+ Cons(h, t, where { n > 0 }) { t.drop(n - 1) }
144
+ Cons(_, _) { |c| c }
145
+ Nil() { raise EmptyListError}
146
+ }
147
+ end
148
+
149
+ def to_a
150
+ foldr([]) { |x, ary| ary << x }
151
+ end
152
+
153
+ def any?(&pred)
154
+ match {
155
+ Nil() { false }
156
+ Cons(h, t, where { t.null? }) { pred.(h) }
157
+ Cons(h, t) { pred.(h) || t.any?(&pred) }
158
+ }
159
+ end
160
+
161
+ def all?(&pred)
162
+ match {
163
+ Nil() { false }
164
+ Cons(h, t, where { t.null? }) { pred.(h) }
165
+ Cons(h, t) { pred.(h) && t.all?(&pred) }
166
+ }
167
+ end
168
+
169
+ def reverse
170
+ match {
171
+ Nil() { |n| n }
172
+ Cons(_, t, where { t.null? }) { |c| c }
173
+ Cons(h, t) { |c| Cons.new(c.last.head, c.init.reverse) }
174
+ }
175
+ end
176
+
177
+ def to_s(joiner = ", ")
178
+ match {
179
+ Nil() { "Nil" }
180
+ Cons(head, tail) { head.to_s + joiner + tail.to_s }
181
+ }
182
+ end
183
+ }
@@ -0,0 +1,186 @@
1
+ require 'spec_helper'
2
+ require_relative 'list'
3
+
4
+ describe List do
5
+ Nil = List::Nil
6
+ Cons = List::Cons
7
+
8
+ subject(:list) { List[1] }
9
+
10
+ specify { expect(list).to eq Cons.new(1, Nil.new) }
11
+
12
+ context "match" do
13
+ it "catches ignores guards with non-matching clauses" do
14
+ expect(
15
+ list.match {
16
+ Nil() { self }
17
+ Cons(h, t, where { h == 0 }) { h }
18
+ Cons(h, t) { h }
19
+ }).to eq 1
20
+ end
21
+
22
+ it "catches matching guards" do
23
+ expect( # guard catched
24
+ list.match {
25
+ Nil() { raise "unreachable" }
26
+ Cons(h, t, where { h == 1 }) { h + 1 }
27
+ Cons(h, t) { h }
28
+ }).to eq 2
29
+ end
30
+
31
+ it "raises an error when no match was made" do
32
+ expect {
33
+ list.match {
34
+ Cons(_, _, where { true == false }) { 1 }
35
+ Nil(where { true == false }) { 0 }
36
+ }
37
+ }.to raise_error(Deterministic::Enum::MatchError)
38
+ end
39
+
40
+ it "raises an error when the match is not exhaustive" do
41
+ expect {
42
+ list.match {
43
+ Cons(_, _) {}
44
+ }
45
+ }.to raise_error(Deterministic::Enum::MatchError)
46
+ end
47
+ end
48
+
49
+ it "empty" do
50
+ expect(List.empty.object_id).to eq List.empty.object_id
51
+ end
52
+
53
+ it "from_a" do
54
+ expect(List[21, 15, 9].to_s).to eq "21, 15, 9, Nil"
55
+ end
56
+
57
+ it "append" do
58
+ expect(List[1].append(2)).to eq Cons.new(2, Cons.new(1, Nil.new))
59
+ end
60
+
61
+ context "head" do
62
+ specify { expect(list.head).to eq 1 }
63
+ end
64
+
65
+ context "tail" do
66
+ subject(:list) { List[3, 9, 15, 21] }
67
+ specify { expect(list.tail.to_s).to eq "9, 15, 21, Nil" }
68
+ end
69
+
70
+ context "take" do
71
+ subject(:list) { List[3, 9, 15, 21] }
72
+ specify { expect(list.take(2).to_s).to eq "3, 9, Nil" }
73
+ end
74
+
75
+ context "drop" do
76
+ subject(:list) { List[3, 9, 15, 21] }
77
+ specify { expect(list.drop(2).to_s).to eq "15, 21, Nil" }
78
+ end
79
+
80
+ context "null" do
81
+ specify { expect(Nil.new).to be_null }
82
+ specify { expect(Cons.new(1, Nil.new)).not_to be_null }
83
+ end
84
+
85
+ context "length" do
86
+ subject(:list) { List[21, 15, 9, 3] }
87
+ specify { expect(list.length).to eq 4 }
88
+ specify { expect(Nil.new.length).to eq 0 }
89
+ end
90
+
91
+ context "filter" do
92
+ subject(:list) { List[3, 9, 15, 21] }
93
+ specify { expect(list.filter { |n| n < 10 }.to_s).to eq "3, 9, Nil" }
94
+ specify { expect(Nil.new.filter { |n| n < 10 }).to eq Nil.new }
95
+ end
96
+
97
+ context "map" do
98
+ subject(:list) { List[1, 2, 3, 4] }
99
+
100
+ specify { expect(list.map { |h, t| h + 1 }).to eq Cons.new(2, Cons.new(3, Cons.new(4, Cons.new(5, Nil.new)))) }
101
+ end
102
+
103
+ context "first & last" do
104
+ subject(:list) { List[9, 15, 21] }
105
+ specify { expect(list.first.head).to eq 9 }
106
+ specify { expect(list.last.head).to eq 21 }
107
+
108
+ specify { expect(Nil.new.first).to eq Nil.new }
109
+ specify { expect(Nil.new.last).to eq Nil.new }
110
+ end
111
+
112
+ context "init" do
113
+ subject(:list) { List[9, 15, 21] }
114
+ specify { expect(list.init.to_s).to eq "9, 15, Nil" }
115
+ specify { expect { Nil.new.init}.to raise_error EmptyListError }
116
+ end
117
+
118
+ it "foldl :: [a] -> b -> (b -> a -> b) -> b" do
119
+ list = List[21, 15, 9]
120
+ expect(list.foldl(0) { |b, a| a + b }).to eq (((0 + 21) + 15) + 9)
121
+ expect(list.foldl(0) { |b, a| b - a }).to eq (((0 - 21) - 15) - 9)
122
+ expect(Nil.new.foldl(0, &:+)).to eq 0
123
+ end
124
+
125
+ it "foldl1 :: [a] -> b -> (b -> a -> b) -> b" do
126
+ list = List[21, 15, 9]
127
+ expect(list.foldl1 { |b, a| a + b }).to eq ((21 + 15) + 9)
128
+ expect(list.foldl1 { |b, a| b - a }).to eq ((21 - 15) - 9)
129
+ expect { Nil.new.foldl1(&:+) }.to raise_error EmptyListError
130
+ end
131
+
132
+ it "foldr :: [a] -> b -> (b -> a -> b) -> b" do
133
+ list = List[21, 15, 9]
134
+ expect(list.foldr(0) { |b, a| a + b }).to eq (21 + (15 + (9 + 0)))
135
+ expect(list.foldr(0) { |b, a| b - a }).to eq (21 - (15 - (9 - 0)))
136
+ expect(Nil.new.foldr(0, &:+)).to eq 0
137
+ end
138
+
139
+ it "foldr1 :: [a] -> b -> (b -> a -> b) -> b" do
140
+ list = List[21, 15, 9, 3]
141
+ expect(list.foldr1 { |b, a| a + b }).to eq (21 + (15 + (9 + 3)))
142
+ expect(list.foldr1 { |b, a| b - a }).to eq (21 - (15 - (9 - 3)))
143
+ expect { Nil.new.foldr1(&:+) }.to raise_error EmptyListError
144
+ end
145
+
146
+ it "find :: [a] -> (a -> Bool) -> Option a" do
147
+ list = List[21, 15, 9]
148
+ expect(list.find { |a| a == 15 }).to eq Deterministic::Option::Some.new(15)
149
+ expect(list.find { |a| a == 1 }).to eq Deterministic::Option::None.new
150
+ end
151
+
152
+ context "reverse" do
153
+ subject(:list) { List[9, 15, 21] }
154
+ specify { expect(list.reverse.first.head).to eq 21 }
155
+ specify { expect(list.to_s).to eq "9, 15, 21, Nil" }
156
+ specify { expect(list.reverse.to_s).to eq "21, 15, 9, Nil" }
157
+ end
158
+
159
+ context "to_a" do
160
+ subject(:list) { List[9, 15, 21] }
161
+ specify { expect(list.to_a).to eq [21, 15, 9] }
162
+ end
163
+
164
+ context "all?" do
165
+ subject(:list) { List[21, 15, 9] }
166
+ specify { expect(list.all? { |n| n.is_a?(Fixnum) }).to be_truthy }
167
+ end
168
+
169
+ context "any?" do
170
+ subject(:list) { List[21, 15, 9] }
171
+ specify { expect(list.any? { |n| n == 11 }).to be_falsey }
172
+ specify { expect(list.any? { |n| n == 15 }).to be_truthy }
173
+ end
174
+
175
+ it "inspect" do
176
+ list = List[9, 15, 21]
177
+ expect(list.inspect).to eq "Cons(head: 9, tail: Cons(head: 15, tail: Cons(head: 21, tail: Nil)))"
178
+ expect(Nil.new.inspect).to eq "Nil"
179
+ end
180
+
181
+ it "to_s" do
182
+ list = List[9, 15, 21]
183
+ expect(list.to_s).to eq "9, 15, 21, Nil"
184
+ expect(Nil.new.to_s).to eq "Nil"
185
+ end
186
+ end