mayak 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/README.md +33 -0
- data/lib/mayak/cache.rb +35 -0
- data/lib/mayak/caching/README.md +100 -0
- data/lib/mayak/caching/lru_cache.rb +76 -0
- data/lib/mayak/caching/unbounded_cache.rb +51 -0
- data/lib/mayak/collections/README.md +62 -0
- data/lib/mayak/collections/priority_queue.rb +155 -0
- data/lib/mayak/collections/queue.rb +73 -0
- data/lib/mayak/function.rb +46 -0
- data/lib/mayak/http/README.md +105 -0
- data/lib/mayak/http/client.rb +17 -0
- data/lib/mayak/http/codec.rb +23 -0
- data/lib/mayak/http/decoder.rb +43 -0
- data/lib/mayak/http/encoder.rb +55 -0
- data/lib/mayak/http/request.rb +110 -0
- data/lib/mayak/http/response.rb +29 -0
- data/lib/mayak/http/verb.rb +20 -0
- data/lib/mayak/json.rb +33 -0
- data/lib/mayak/monads/README.md +1409 -0
- data/lib/mayak/monads/maybe.rb +449 -0
- data/lib/mayak/monads/result.rb +574 -0
- data/lib/mayak/monads/try.rb +619 -0
- data/lib/mayak/numeric.rb +44 -0
- data/lib/mayak/predicates/rule.rb +74 -0
- data/lib/mayak/random.rb +15 -0
- data/lib/mayak/version.rb +6 -0
- data/lib/mayak/weak_ref.rb +37 -0
- data/lib/mayak.rb +35 -0
- data/mayak.gemspec +21 -0
- metadata +127 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: c11b0f5c9288055fd9f0ca907a38e257b06b78fdd0f4e1185fafa8beae4fdcc2
|
4
|
+
data.tar.gz: f49566c5b0d830610843288c56ae001513a72b34b5beb75a9cb764998c0e6abd
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: b24805002d39350e5d6fa0f4b8bc501b6c576b34368bc90eca7af794f820f191e7c4e35c1487cb3083ddabd18e2dbb1929822801cd934eebeac298c4bb5d34d4
|
7
|
+
data.tar.gz: 87275eb153d98c62d264c20a51db6e99109ec519eb957fbf4728f3b0612686aa41f76f728090d332d72323d16e7a954751fb17ad23b1bf9bff3cf99eba20fef8
|
data/README.md
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
# Mayak
|
2
|
+
|
3
|
+
### Overview
|
4
|
+
|
5
|
+
Mayak is a library which aims to provide abstractions for well typed programming in Ruby using Sorbet type checker. Mayak provides generic interfaces and utility classes for various applications, and as a foundation for other libraries.
|
6
|
+
|
7
|
+
### Installation
|
8
|
+
|
9
|
+
In order to use the library, add the following line to your `Gemfile`:
|
10
|
+
|
11
|
+
```ruby
|
12
|
+
gem 'mayak'
|
13
|
+
```
|
14
|
+
or install it via the following command:
|
15
|
+
```ruby
|
16
|
+
gem install 'mayak'
|
17
|
+
```
|
18
|
+
|
19
|
+
### Documentation
|
20
|
+
|
21
|
+
Mayak consists from separate classes and interfaces as well as separate modules for specific domains.
|
22
|
+
|
23
|
+
#### Caching
|
24
|
+
|
25
|
+
[Documentation](./lib/mayak/caching/README.md)
|
26
|
+
|
27
|
+
#### Monads
|
28
|
+
|
29
|
+
[Documentation](./lib/mayak/monads/README.md)
|
30
|
+
|
31
|
+
#### HTTP
|
32
|
+
|
33
|
+
[Documentation](./lib/mayak/http/README.md)
|
data/lib/mayak/cache.rb
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
# typed: strict
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module Mayak
|
5
|
+
module Cache
|
6
|
+
extend T::Sig
|
7
|
+
extend T::Generic
|
8
|
+
extend T::Helpers
|
9
|
+
|
10
|
+
interface!
|
11
|
+
|
12
|
+
Key = type_member
|
13
|
+
Value = type_member
|
14
|
+
|
15
|
+
sig { abstract.params(key: Key).returns(T.nilable(Value)) }
|
16
|
+
def read(key)
|
17
|
+
end
|
18
|
+
|
19
|
+
sig { abstract.params(key: Key, value: Value).void }
|
20
|
+
def write(key, value)
|
21
|
+
end
|
22
|
+
|
23
|
+
sig { abstract.params(key: Key, blk: T.proc.returns(Value)).returns(Value) }
|
24
|
+
def fetch(key, &blk)
|
25
|
+
end
|
26
|
+
|
27
|
+
sig { abstract.void }
|
28
|
+
def clear
|
29
|
+
end
|
30
|
+
|
31
|
+
sig { abstract.params(key: Key).void }
|
32
|
+
def delete(key)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,100 @@
|
|
1
|
+
# Caching
|
2
|
+
|
3
|
+
Caching modules constist from `Mayak::Cache` and several implementations in `Mayak::Caching` module. `Mayak::Caching` provides in-memory caches using regular ruby hashes: an unbounded cache and a cache using LRU eviction policy.
|
4
|
+
|
5
|
+
Usage of unbounded cache:
|
6
|
+
```ruby
|
7
|
+
unbounded_cache = Mayak::Caching::UnboundedCache[String, Integer].new
|
8
|
+
unbounded_cache.write("foo", 10)
|
9
|
+
unbounded_cache.write("bar", 20)
|
10
|
+
|
11
|
+
unbounded_cache.read("foo") # 10
|
12
|
+
unbounded_cache.read("bar") # 20
|
13
|
+
unbounded_cache.read("baz") # nil
|
14
|
+
|
15
|
+
unbounded_cache.delete("bar")
|
16
|
+
unbounded_cache.read("bar") # nil
|
17
|
+
|
18
|
+
unbounded_cache.fetch("foo") { 100 } # 10
|
19
|
+
unbounded_cache.fetch("bar") { 100 } # 100
|
20
|
+
unbounded_cache.fetch("bar") { 200 } # 100
|
21
|
+
|
22
|
+
unbounded_cache.clear # 100
|
23
|
+
unbounded_cache.read("foo") # nil
|
24
|
+
unbounded_cache.read("bar") # nil
|
25
|
+
```
|
26
|
+
|
27
|
+
LRU cache has limited size: when the cache is full and a new element is added, some element is evicted using least recently used policy:
|
28
|
+
|
29
|
+
```ruby
|
30
|
+
lru_cache = Mayak::Caching::LRUCache[String, Integer].new(max_size: 3)
|
31
|
+
|
32
|
+
lru_cache.write("key1", 1)
|
33
|
+
lru_cache.write("key2", 2)
|
34
|
+
lru_cache.write("key3", 3)
|
35
|
+
|
36
|
+
lru_cache.read("key1") # 1
|
37
|
+
lru_cache.read("key2") # 2
|
38
|
+
lru_cache.read("key3") # 3
|
39
|
+
|
40
|
+
lru_cache.write("key4", 4)
|
41
|
+
lru_cache.read("key4") # 4
|
42
|
+
lru_cache.read("key1") # nil
|
43
|
+
```
|
44
|
+
|
45
|
+
You can implement `Mayak::Cache` interface using a different store (for example default Rails cache) and use different implementations interchangeably:
|
46
|
+
|
47
|
+
```ruby
|
48
|
+
class RailsCache < T::Struct
|
49
|
+
extend T::Sig
|
50
|
+
extend T::Generic
|
51
|
+
extend T::Helpers
|
52
|
+
|
53
|
+
include Mayak::Cache
|
54
|
+
|
55
|
+
Key = type_member
|
56
|
+
Value = type_member
|
57
|
+
|
58
|
+
const :converter, T.proc.params(value: T.untyped).returns(Value)
|
59
|
+
|
60
|
+
sig { override.params(key: Key).returns(T.nilable(Value)) }
|
61
|
+
def read(key)
|
62
|
+
converter.call(Rails.cache.read(key))
|
63
|
+
end
|
64
|
+
|
65
|
+
sig { override.params(key: Key, value: Value).void }
|
66
|
+
def write(key, value)
|
67
|
+
Rails.cache.write(key, value)
|
68
|
+
end
|
69
|
+
|
70
|
+
sig { override.params(key: Key, blk: T.proc.returns(Value)).returns(Value) }
|
71
|
+
def fetch(key, &blk)
|
72
|
+
converter.call(Rails.cache.fetch(key, &blk))
|
73
|
+
end
|
74
|
+
|
75
|
+
sig { override.void }
|
76
|
+
def clear
|
77
|
+
Rails.cache.clear
|
78
|
+
end
|
79
|
+
|
80
|
+
sig { override.params(key: Key).void }
|
81
|
+
def delete(key)
|
82
|
+
Rails.cache.delete(key)
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
class Service < T::Struct
|
87
|
+
extend T::Sig
|
88
|
+
|
89
|
+
const :cache, Mayak::Cache[String, String]
|
90
|
+
end
|
91
|
+
|
92
|
+
in_memory = Service.new(
|
93
|
+
cache: Mayak::Caching::UnboundedCache[String, String].new
|
94
|
+
)
|
95
|
+
rails_cache = Service.new(
|
96
|
+
cache: RailsCache[String, String].new(
|
97
|
+
converter: -> (value) { value.to_s }
|
98
|
+
)
|
99
|
+
)
|
100
|
+
```
|
@@ -0,0 +1,76 @@
|
|
1
|
+
# typed: strict
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module Mayak
|
5
|
+
module Caching
|
6
|
+
class LRUCache
|
7
|
+
extend T::Sig
|
8
|
+
extend T::Generic
|
9
|
+
extend T::Helpers
|
10
|
+
|
11
|
+
include Mayak::Cache
|
12
|
+
|
13
|
+
Key = type_member
|
14
|
+
Value = type_member
|
15
|
+
|
16
|
+
sig { returns(Integer) }
|
17
|
+
attr_reader :max_size
|
18
|
+
|
19
|
+
sig { params(max_size: Integer).void }
|
20
|
+
def initialize(max_size:)
|
21
|
+
@storage = T.let({}, T::Hash[Key, Value])
|
22
|
+
if max_size <= 0
|
23
|
+
raise ArgumentError.new("max_size should be a positive integer")
|
24
|
+
end
|
25
|
+
@max_size = T.let(max_size, Integer)
|
26
|
+
end
|
27
|
+
|
28
|
+
sig { override.params(key: Key).returns(T.nilable(Value)) }
|
29
|
+
def read(key)
|
30
|
+
@storage[key]
|
31
|
+
end
|
32
|
+
|
33
|
+
sig { override.params(key: Key, value: Value).void }
|
34
|
+
def write(key, value)
|
35
|
+
if @storage.size == max_size && !@storage.key?(key)
|
36
|
+
evict!
|
37
|
+
elsif @storage.key?(key)
|
38
|
+
@storage.delete(key)
|
39
|
+
end
|
40
|
+
@storage[key] = value
|
41
|
+
end
|
42
|
+
|
43
|
+
sig { override.params(key: Key, blk: T.proc.returns(Value)).returns(Value) }
|
44
|
+
def fetch(key, &blk)
|
45
|
+
return T.must(@storage[key]) if @storage.has_key?(key)
|
46
|
+
|
47
|
+
value = blk.call
|
48
|
+
write(key, value)
|
49
|
+
value
|
50
|
+
end
|
51
|
+
|
52
|
+
sig { override.void }
|
53
|
+
def clear
|
54
|
+
@storage.clear
|
55
|
+
end
|
56
|
+
|
57
|
+
sig { override.params(key: Key).void }
|
58
|
+
def delete(key)
|
59
|
+
@storage.delete(key)
|
60
|
+
end
|
61
|
+
|
62
|
+
private
|
63
|
+
|
64
|
+
sig { void }
|
65
|
+
def evict!
|
66
|
+
first_key = @storage.first&.first
|
67
|
+
case first_key
|
68
|
+
when NilClass
|
69
|
+
return
|
70
|
+
else
|
71
|
+
@storage.delete(first_key)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
# typed: strict
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module Mayak
|
5
|
+
module Caching
|
6
|
+
class UnboundedCache
|
7
|
+
extend T::Sig
|
8
|
+
extend T::Generic
|
9
|
+
extend T::Helpers
|
10
|
+
|
11
|
+
include Mayak::Cache
|
12
|
+
|
13
|
+
Key = type_member
|
14
|
+
Value = type_member
|
15
|
+
|
16
|
+
sig { void }
|
17
|
+
def initialize
|
18
|
+
@storage = T.let({}, T::Hash[Key, Value])
|
19
|
+
end
|
20
|
+
|
21
|
+
sig { override.params(key: Key).returns(T.nilable(Value)) }
|
22
|
+
def read(key)
|
23
|
+
@storage[key]
|
24
|
+
end
|
25
|
+
|
26
|
+
sig { override.params(key: Key, value: Value).void }
|
27
|
+
def write(key, value)
|
28
|
+
@storage[key] = value
|
29
|
+
end
|
30
|
+
|
31
|
+
sig { override.params(key: Key, blk: T.proc.returns(Value)).returns(Value) }
|
32
|
+
def fetch(key, &blk)
|
33
|
+
return T.must(@storage[key]) if @storage.has_key?(key)
|
34
|
+
|
35
|
+
value = blk.call
|
36
|
+
@storage[key] = value
|
37
|
+
value
|
38
|
+
end
|
39
|
+
|
40
|
+
sig { override.void }
|
41
|
+
def clear
|
42
|
+
@storage.clear
|
43
|
+
end
|
44
|
+
|
45
|
+
sig { override.params(key: Key).void }
|
46
|
+
def delete(key)
|
47
|
+
@storage.delete(key)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
# Collections
|
2
|
+
|
3
|
+
Set of performant type safe collections written in ruby.
|
4
|
+
|
5
|
+
### Queue
|
6
|
+
|
7
|
+
Regular parameterized FIFO queue with O(1) enqueue and dequeue operations. The difference between Mayak's Queue and standard library Queue is that former is plain data struture that doesn't block, while the latter is a synchronization multi-producer, multi-consumer queue.
|
8
|
+
|
9
|
+
Usage:
|
10
|
+
```ruby
|
11
|
+
queue = Mayak::Collections::Queue[Integer].new
|
12
|
+
|
13
|
+
# adds an element to the queue
|
14
|
+
queue.enqueue(1)
|
15
|
+
queue.enqueue(2)
|
16
|
+
queue.enqueue(3)
|
17
|
+
|
18
|
+
# returns first queue element without updating the queue
|
19
|
+
queue.peak # 1
|
20
|
+
|
21
|
+
# returns an element and remove it from collection
|
22
|
+
queue.dequeue # 1
|
23
|
+
queue.dequeue # 2
|
24
|
+
queue.dequeue # 3
|
25
|
+
queue.dequeue # nil
|
26
|
+
|
27
|
+
# checks whether collection is empty
|
28
|
+
queue.empty? # true
|
29
|
+
queue.enqueue(1)
|
30
|
+
queue.empty? # false
|
31
|
+
```
|
32
|
+
|
33
|
+
### Priority Queue
|
34
|
+
|
35
|
+
Implements a queue where aach element has an associated priority. Elements with high priority are served before elements with low priority. The priority queue is parameterized with both element and priority types. Priority queue is initialized with a comparator function that returns a boolean value (true if a first argument is larger that a second).
|
36
|
+
|
37
|
+
Usage:
|
38
|
+
```ruby
|
39
|
+
pqueue = Mayak::Collections::PriorityQueue[String, Integer].new { |a, b| a > b }
|
40
|
+
|
41
|
+
# adds an element with a priority
|
42
|
+
pqueue.enqueue("second", 8)
|
43
|
+
pqueue.enqueue("third", 5)
|
44
|
+
pqueue.enqueue("first", 10)
|
45
|
+
# returns a pair of value and priority and remove it from the queue
|
46
|
+
pqueue.dequeue # ["first", 10]
|
47
|
+
pqueue.dequeue # ["second", 8]
|
48
|
+
pqueue.dequeue # ["third", 5]
|
49
|
+
|
50
|
+
# returns a first pair without removing it from the queue
|
51
|
+
pqueue.peak # nil
|
52
|
+
pqueue.enqueue("first", 10)
|
53
|
+
pqueue.peak # ["first", 10]
|
54
|
+
pqueue.dequeue # ["first", 10]
|
55
|
+
|
56
|
+
pqueue.enqueue("second", 8)
|
57
|
+
pqueue.enqueue("third", 5)
|
58
|
+
pqueue.enqueue("first", 10)
|
59
|
+
|
60
|
+
# returns an array containing all pairs preserving internal heap structure
|
61
|
+
pqueue.to_a # [["first", 10], ["third", 5], ["second", 8]]
|
62
|
+
```
|
@@ -0,0 +1,155 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
# typed: strict
|
3
|
+
|
4
|
+
module Mayak
|
5
|
+
module Collections
|
6
|
+
class PriorityQueue
|
7
|
+
extend T::Sig
|
8
|
+
extend T::Generic
|
9
|
+
|
10
|
+
Element = type_member
|
11
|
+
Priority = type_member
|
12
|
+
|
13
|
+
sig { returns(Integer) }
|
14
|
+
attr_reader :size
|
15
|
+
|
16
|
+
sig { params(compare: T.proc.params(arg0: Priority, arg2: Priority).returns(T::Boolean)).void }
|
17
|
+
def initialize(&compare)
|
18
|
+
@array = T.let([], T::Array[T.nilable([Element, Priority])])
|
19
|
+
@compare = T.let(compare, T.proc.params(arg0: Priority, arg2: Priority).returns(T::Boolean))
|
20
|
+
@size = T.let(0, Integer)
|
21
|
+
end
|
22
|
+
|
23
|
+
sig { params(element: Element, priority: Priority).void }
|
24
|
+
def enqueue(element, priority)
|
25
|
+
@array[@size] = [element, priority]
|
26
|
+
@size += 1
|
27
|
+
sift_up(size - 1)
|
28
|
+
end
|
29
|
+
|
30
|
+
sig { returns(T.nilable([Element, Priority])) }
|
31
|
+
def dequeue
|
32
|
+
element_index = 0
|
33
|
+
result = @array[element_index]
|
34
|
+
|
35
|
+
if @size > 1
|
36
|
+
@size -= 1
|
37
|
+
@array[element_index] = @array[@size]
|
38
|
+
sift_down(element_index)
|
39
|
+
else
|
40
|
+
@size = 0
|
41
|
+
end
|
42
|
+
|
43
|
+
@array[@size] = nil
|
44
|
+
|
45
|
+
result
|
46
|
+
end
|
47
|
+
|
48
|
+
sig { returns(T.nilable([Element, Priority])) }
|
49
|
+
def peak
|
50
|
+
@array.first
|
51
|
+
end
|
52
|
+
|
53
|
+
sig { void }
|
54
|
+
def clear
|
55
|
+
@array = []
|
56
|
+
@size = 0
|
57
|
+
end
|
58
|
+
|
59
|
+
sig { returns(T::Array[[Element, Priority]]) }
|
60
|
+
def to_a
|
61
|
+
@array.compact
|
62
|
+
end
|
63
|
+
|
64
|
+
sig { returns(T::Boolean) }
|
65
|
+
def empty?
|
66
|
+
size == 0
|
67
|
+
end
|
68
|
+
|
69
|
+
private
|
70
|
+
|
71
|
+
sig { params(element_index: Integer).void }
|
72
|
+
def sift_up(element_index)
|
73
|
+
index = element_index
|
74
|
+
|
75
|
+
while !root?(index) && compare(index, parent_index(index))
|
76
|
+
swap(index, parent_index(index))
|
77
|
+
index = parent_index(index)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
sig { params(element_index: Integer).void }
|
82
|
+
def sift_down(element_index)
|
83
|
+
index = element_index
|
84
|
+
|
85
|
+
loop do
|
86
|
+
left_index = left_child_index(index)
|
87
|
+
right_index = right_child_index(index)
|
88
|
+
|
89
|
+
if has_left_child(index) && compare(left_index, index)
|
90
|
+
swap(index, left_index)
|
91
|
+
index = left_index
|
92
|
+
elsif has_right_child(index) && compare(right_index, index)
|
93
|
+
swap(index, right_index)
|
94
|
+
index = right_index
|
95
|
+
else
|
96
|
+
break
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
sig { params(index1: Integer, index2: Integer).void }
|
102
|
+
def swap(index1, index2)
|
103
|
+
@array[index1], @array[index2] = @array[index2], @array[index1]
|
104
|
+
end
|
105
|
+
|
106
|
+
sig { params(index1: Integer, index2: Integer).returns(T::Boolean) }
|
107
|
+
def compare(index1, index2)
|
108
|
+
value1 = @array[index1]
|
109
|
+
value2 = @array[index2]
|
110
|
+
|
111
|
+
raise StandardError.new("index out of bound") if value1.nil? || value2.nil?
|
112
|
+
|
113
|
+
_, priority1 = value1
|
114
|
+
_, priority2 = value2
|
115
|
+
|
116
|
+
@compare.call(priority1, priority2)
|
117
|
+
end
|
118
|
+
|
119
|
+
sig { params(index: Integer).returns(T::Boolean) }
|
120
|
+
def has_parent(index)
|
121
|
+
index >= 1
|
122
|
+
end
|
123
|
+
|
124
|
+
sig { params(index: Integer).returns(Integer) }
|
125
|
+
def parent_index(index)
|
126
|
+
((index - 1) / 2).floor
|
127
|
+
end
|
128
|
+
|
129
|
+
sig { params(index: Integer).returns(T::Boolean) }
|
130
|
+
def has_left_child(index)
|
131
|
+
left_child_index(index) < @size
|
132
|
+
end
|
133
|
+
|
134
|
+
sig { params(index: Integer).returns(Integer) }
|
135
|
+
def left_child_index(index)
|
136
|
+
index * 2 + 1
|
137
|
+
end
|
138
|
+
|
139
|
+
sig { params(index: Integer).returns(T::Boolean) }
|
140
|
+
def has_right_child(index)
|
141
|
+
right_child_index(index) < @size
|
142
|
+
end
|
143
|
+
|
144
|
+
sig { params(index: Integer).returns(Integer) }
|
145
|
+
def right_child_index(index)
|
146
|
+
index * 2 + 2
|
147
|
+
end
|
148
|
+
|
149
|
+
sig { params(index: Integer).returns(T::Boolean) }
|
150
|
+
def root?(index)
|
151
|
+
index == 0
|
152
|
+
end
|
153
|
+
end
|
154
|
+
end
|
155
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
# typed: strict
|
3
|
+
|
4
|
+
module Mayak
|
5
|
+
module Collections
|
6
|
+
class Queue
|
7
|
+
extend T::Sig
|
8
|
+
extend T::Helpers
|
9
|
+
extend T::Generic
|
10
|
+
|
11
|
+
Value = type_member
|
12
|
+
|
13
|
+
class Node < T::Struct
|
14
|
+
extend T::Sig
|
15
|
+
extend T::Generic
|
16
|
+
|
17
|
+
Value = type_member
|
18
|
+
|
19
|
+
const :value, Value
|
20
|
+
prop :next, T.nilable(Node[Value])
|
21
|
+
end
|
22
|
+
private_constant :Node
|
23
|
+
|
24
|
+
sig { returns(Integer) }
|
25
|
+
attr_reader :size
|
26
|
+
|
27
|
+
sig { params(initial: T::Array[Value]).void }
|
28
|
+
def initialize(initial: [])
|
29
|
+
@head = T.let(nil, T.nilable(Node[Value]))
|
30
|
+
@tail = T.let(nil, T.nilable(Node[Value]))
|
31
|
+
@size = T.let(0, Integer)
|
32
|
+
initial.each { |element| enqueue(element) }
|
33
|
+
end
|
34
|
+
|
35
|
+
sig { params(element: Value).void }
|
36
|
+
def enqueue(element)
|
37
|
+
if @head.nil?
|
38
|
+
@head = Node[Value].new(value: element, next: nil)
|
39
|
+
@tail = @head
|
40
|
+
@size += 1
|
41
|
+
else
|
42
|
+
T.must(@tail).next = Node[Value].new(value: element, next: nil)
|
43
|
+
@tail = T.must(@tail).next
|
44
|
+
@size += 1
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
sig { returns(T.nilable(Value)) }
|
49
|
+
def peak
|
50
|
+
return if @head.nil?
|
51
|
+
|
52
|
+
@head.value
|
53
|
+
end
|
54
|
+
|
55
|
+
sig { returns(T.nilable(Value)) }
|
56
|
+
def dequeue
|
57
|
+
return if @size == 0
|
58
|
+
return if @head.nil?
|
59
|
+
|
60
|
+
element = @head.value
|
61
|
+
@head = @head.next
|
62
|
+
@size -= 1
|
63
|
+
@tail = nil if @size == 0
|
64
|
+
element
|
65
|
+
end
|
66
|
+
|
67
|
+
sig { returns(T::Boolean) }
|
68
|
+
def empty?
|
69
|
+
@size == 0
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
# typed: strict
|
3
|
+
|
4
|
+
module Mayak
|
5
|
+
class Function
|
6
|
+
extend T::Sig
|
7
|
+
extend T::Helpers
|
8
|
+
extend T::Generic
|
9
|
+
|
10
|
+
Input = type_member
|
11
|
+
Output = type_member
|
12
|
+
|
13
|
+
sig { returns(T.proc.params(input: Input).returns(Output)) }
|
14
|
+
attr_reader :blk
|
15
|
+
|
16
|
+
sig { params(blk: T.proc.params(input: Input).returns(Output)).void }
|
17
|
+
def initialize(&blk)
|
18
|
+
@blk = T.let(blk, T.proc.params(input: Input).returns(Output))
|
19
|
+
end
|
20
|
+
|
21
|
+
sig { params(input: Input).returns(Output) }
|
22
|
+
def call(input)
|
23
|
+
blk.call(input)
|
24
|
+
end
|
25
|
+
|
26
|
+
sig {
|
27
|
+
type_parameters(:Output2)
|
28
|
+
.params(another: Mayak::Function[Output, T.type_parameter(:Output2)])
|
29
|
+
.returns(Mayak::Function[Input, T.type_parameter(:Output2)])
|
30
|
+
}
|
31
|
+
def and_then(another)
|
32
|
+
Mayak::Function[Input, T.type_parameter(:Output2)].new { |a| another.call(blk.call(a)) }
|
33
|
+
end
|
34
|
+
alias >> and_then
|
35
|
+
|
36
|
+
sig {
|
37
|
+
type_parameters(:Input2)
|
38
|
+
.params(another: Mayak::Function[T.type_parameter(:Input2), Input])
|
39
|
+
.returns(Mayak::Function[T.type_parameter(:Input2), Output])
|
40
|
+
}
|
41
|
+
def compose(another)
|
42
|
+
Mayak::Function[T.type_parameter(:Input2), Output].new { |a| blk.call(another.call(a)) }
|
43
|
+
end
|
44
|
+
alias << compose
|
45
|
+
end
|
46
|
+
end
|