deterministic 0.14.1 → 0.15.0

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.
@@ -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