mayak 0.0.15 → 0.2.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.
@@ -12,8 +12,8 @@ module Mayak
12
12
 
13
13
  include ::Mayak::Encoder
14
14
 
15
- ResponseEntity = type_member
16
- ResponseType = type_member {{ fixed: Mayak::Http::Response }}
15
+ In = type_member
16
+ Out = type_member {{ fixed: Mayak::Http::Response }}
17
17
 
18
18
  class IdentityEncoder
19
19
  extend T::Sig
@@ -22,36 +22,15 @@ module Mayak
22
22
 
23
23
  include ::Mayak::Http::Encoder
24
24
 
25
- ResponseEntity = type_member {{ fixed: ::Mayak::Http::Response }}
26
- ResponseType = type_member {{ fixed: Mayak::Http::Response }}
25
+ In = type_member {{ fixed: ::Mayak::Http::Response }}
26
+ Out = type_member {{ fixed: Mayak::Http::Response }}
27
27
 
28
- sig { override.params(entity: ResponseEntity).returns(ResponseType) }
28
+ sig { override.params(entity: In).returns(Out) }
29
29
  def encode(entity)
30
30
  entity
31
31
  end
32
32
  end
33
33
 
34
- class FromFunction
35
- extend T::Sig
36
- extend T::Generic
37
- extend T::Helpers
38
-
39
- include ::Mayak::Http::Encoder
40
-
41
- ResponseEntity = type_member
42
- ResponseType = type_member {{ fixed: Mayak::Http::Response }}
43
-
44
- sig { params(function: T.proc.params(response: ResponseEntity).returns(ResponseType)).void }
45
- def initialize(&function)
46
- @function = T.let(function, T.proc.params(response: ResponseEntity).returns(ResponseType))
47
- end
48
-
49
- sig { override.params(entity: ResponseEntity).returns(ResponseType) }
50
- def encode(entity)
51
- @function.call(entity)
52
- end
53
- end
54
-
55
34
  class FromHashSerializableJson < T::Struct
56
35
  extend T::Sig
57
36
  extend T::Generic
@@ -62,10 +41,10 @@ module Mayak
62
41
  const :default_status, Integer
63
42
  const :default_headers, T::Hash[String, String]
64
43
 
65
- ResponseEntity = type_member {{ fixed: ::Mayak::HashSerializable }}
66
- ResponseType = type_member {{ fixed: Mayak::Http::Response }}
44
+ In = type_member {{ fixed: ::Mayak::HashSerializable }}
45
+ Out = type_member {{ fixed: Mayak::Http::Response }}
67
46
 
68
- sig { override.params(entity: ResponseEntity).returns(ResponseType) }
47
+ sig { override.params(entity: In).returns(Out) }
69
48
  def encode(entity)
70
49
  Mayak::Http::Response.new(
71
50
  status: default_status,
@@ -14,29 +14,8 @@ module Mayak
14
14
 
15
15
  include ::Mayak::Encoder
16
16
 
17
- ResponseEntity = type_member
18
- ResponseType = type_member {{ fixed: ::Mayak::Json::JsonType }}
19
-
20
- class FromFunction
21
- extend T::Sig
22
- extend T::Generic
23
- extend T::Helpers
24
-
25
- include ::Mayak::Encoder
26
-
27
- ResponseEntity = type_member
28
- ResponseType = type_member {{ fixed: ::Mayak::Json::JsonType }}
29
-
30
- sig { params(function: T.proc.params(response: ResponseEntity).returns(ResponseType)).void }
31
- def initialize(&function)
32
- @function = T.let(function, T.proc.params(response: ResponseEntity).returns(ResponseType))
33
- end
34
-
35
- sig { override.params(entity: ResponseEntity).returns(ResponseType) }
36
- def encode(entity)
37
- @function.call(entity)
38
- end
39
- end
17
+ In = type_member
18
+ Out = type_member {{ fixed: ::Mayak::Json::JsonType }}
40
19
  end
41
20
  end
42
21
  end
@@ -0,0 +1,226 @@
1
+ # Lazy
2
+
3
+ `Lazy` classs represents a value that evaluates only during it's access, and evaluates only once
4
+ during the first access. Basically, `Lazy` wraps a block of code (thunk) that returns a value (`Lazy` has single type parameter of a value), and executes only when the value accessed for the first time
5
+ and then stores it afterward.
6
+
7
+ In order to build `Lazy` a type parameter of value holded should be provided as well as a block that computes a value of the type.
8
+ Note that the block is not executed right away.
9
+
10
+ ```ruby
11
+ lazy1 = ::Mayak::Lazy[Integer].new { 1 }
12
+
13
+ buffer = []
14
+ lazy2 = ::Mayak::Lazy[Integer].new do
15
+ buffer << 1
16
+ 1
17
+ end
18
+ buffer
19
+ #> []
20
+ ```
21
+
22
+ To access the value call `#value`. If the value is not yet computed, provided block will be executed and its result will be stored. Further invokations
23
+ of this method won't execute the block again.
24
+
25
+ ```ruby
26
+ buffer = []
27
+ lazy = ::Mayak::Lazy[Integer].new do
28
+ buffer << 1
29
+ 1
30
+ end
31
+ buffer
32
+ #> []
33
+
34
+ # Will execute the block and return the computed value.
35
+ lazy.value
36
+ #> 1
37
+ buffer
38
+ #> [1]
39
+
40
+ # Will return the memoized value, but won't call the block again
41
+ lazy.value
42
+ #> 1
43
+ buffer
44
+ #> [1]
45
+ ```
46
+
47
+ `Lazy` can be used in situations, when we want to inject some dependency into some class or method, but it may not be used, and the computation or aacquisition of the dependency may be cosftul. In this cases, it's acquisitation may be wrapped in lazy.
48
+
49
+ In more imperative style
50
+ ```ruby
51
+ sig { params(env_variable: String, file_content: ::Mayak::Lazy[String], default: String).returns(String) }
52
+ def fetch_config(env_variable, file_content, default)
53
+ from_environment = ENV[env_variable]
54
+ if env.empty?
55
+ file_config = ::Core::Json.parse(file_content.value).success_or({})
56
+ from_file = file_config["configuration"]
57
+ if from_file.empty?
58
+ default
59
+ else
60
+ from_file
61
+ end
62
+ else
63
+ from_environment
64
+ end
65
+ end
66
+ ```
67
+
68
+ Using Mayak monads:
69
+ ```ruby
70
+ include ::Mayak::Monads::Maybe::Mixin
71
+
72
+ sig { params(env_variable: String, file_content: ::Mayak::Lazy[String], default: String).returns(String) }
73
+ def fetch_config(env_variable, file_content, default)
74
+ Maybe(ENV[env_variable])
75
+ .recover_with_maybe(::Core::Json.parse(file_content.value).to_maybe)
76
+ .flat_map { |json| Maybe(json["configuration"]) }
77
+ .value_or(default)
78
+ end
79
+ ```
80
+
81
+ This method receives name of environment variable, and file content as lazy value. The method
82
+ tries to read the environment variable, and if it's not present and reads the file content to find the configuration.
83
+ `Lazy` allows to incapsulate behaviour of reading from file, so it can be passed as dependency, method `#fetch_config` doesn't
84
+ know anything about reading from file, but because of usage of `lazy` we can postpone it's execution thus avoiding unnecessary work.
85
+
86
+ `Lazy` can be transformed via methods `#map` and `#flat_map`.
87
+
88
+ Method `#map` allows to transform value inside `Lazy` without triggering executing. Note that `#map` returns
89
+ a new instance without mutating previous `Lazy`.
90
+ ```ruby
91
+ int_lazy = ::Mayak::Lazy[Integer].new do
92
+ puts("On initialize")
93
+ 1
94
+ end
95
+ string_lazy = int_lazy.map do |int|
96
+ puts("On mapping")
97
+ int.to_s
98
+ end
99
+ int_lazy.value # 1
100
+ #> On initialize
101
+
102
+ string_lazy.value # "1"
103
+ #> On initialize
104
+ #> On mapping
105
+
106
+ sig { params(file_content: ::Mayak::Lazy[String]).returns(::Mayak::Lazy[Maybe[String]]) }
107
+ def file_content_config(file_content)
108
+ file_content.map do |file_content|
109
+ ::Core::Json
110
+ .parse(file_content.value)
111
+ .to_maybe
112
+ .flat_map { |json| Maybe(json["configuration"]) }
113
+ end
114
+ end
115
+ ```
116
+
117
+ Method `#flat_map` allows to chain lazy computations. It receives a block, that builds a new `Lazy` value from the value of original
118
+ `Lazy` and returns a new instance of `Lazy`.
119
+
120
+ ```ruby
121
+ sig { params(env_name: String).returns(::Mayak::Lazy[String]) }
122
+ def lazy_env(env_name)
123
+ ::Mayak::Lazy[String].new { ENV[env_name] }
124
+ end
125
+
126
+ env_variable_name = ::Mayak::Lazy[String].new { "VARIABLE" }
127
+ env_variable = env_variable_name.flat_map { |env_name| lazy_env(env_name) }
128
+ ```
129
+
130
+ This may be useful when want to perform a lazy computation based on result of some other lazy computation without enforcing the evaluation.
131
+
132
+ For example we have a file that contains list of file names. We can build a lazy computation that read all lines from this code.
133
+ ```ruby
134
+ sig { params(file_name: String).returns(::Mayak::Lazy[T::Array[String]]) }
135
+ def read_file_lines(file_name)
136
+ ::Mayak::Lazy[T::Array[String]].new { File.read(file_name).split }
137
+ end
138
+ ```
139
+
140
+ Let's we want to read all filenames from the root file, and then read the first file lazily. In this cases, the lazy computation can be chained via `#flat_map`:
141
+
142
+ ```ruby
143
+ sig { params(file_name: String).returns(::Mayak::Lazy[T::Array[String]]) }
144
+ def read_first_file(file_name)
145
+ read_file_lines(file_name).flat_map do |file_names|
146
+ Maybe(file_names.first)
147
+ .filter(&:empty?)
148
+ .map { |file| read_file_lines(file) }
149
+ .value_or(::Mayak::Lazy[T::Array[String]].new { [] })
150
+ end
151
+ end
152
+ ```
153
+
154
+ In order to combine two lazies of different types into a single one, method `#combine` can be used.
155
+ This method receives another lazy (it can be lazy of different type), and a block
156
+ and returns a lazy containing result of applying passed blocked to values calculated by lazies.
157
+
158
+ ```ruby
159
+ class ConfigFiles < T::Struct
160
+ const :database_config_file, ::File
161
+ const :server_config_file, ::File
162
+ end
163
+
164
+ sig { returns(::Mayak::Lazy[File]) }
165
+ def database_config_file
166
+ ::Mayak::Lazy[File].new { File.new(DATABASE_CONFIG_FILE_NAME, "r") }
167
+ end
168
+
169
+ sig { returns(::Mayak::Lazy[File]) }
170
+ def server_config_file
171
+ ::Mayak::Lazy[File].new { File.new(SERVER_CONFIG_FILE_NAME, "r") }
172
+ end
173
+
174
+ sig { returns(::Mayak::Lazy[ConfigFiles]) }
175
+ def config_files
176
+ database_config_file.combine(server_config_file) do |db_file, server_file|
177
+ ConfigFiles.new(
178
+ database_config_file: database_config_file,
179
+ server_config_file: server_file
180
+ )
181
+ end
182
+ end
183
+ ```
184
+
185
+ The same behaviour can be achieved with a method `.combine_two`:
186
+
187
+ ```ruby
188
+ sig { returns(::Mayak::Lazy[ConfigFiles]) }
189
+ def config_files
190
+ ::Mayak::Lazy.combine_two(database_config_file, server_config_file) do |db_file, server_file|
191
+ ConfigFiles.new(
192
+ database_config_file: database_config_file,
193
+ server_config_file: server_file
194
+ )
195
+ end
196
+ end
197
+ ```
198
+
199
+ There are also methods `.combine_three`, `.combine_four` upto `.combine_sevel` to combine multiple lazies of diffent types.
200
+
201
+ If you need to combined multiple lazies containing the same value, you can use `.combine_many`. It works
202
+ as `Array#reduce`: receives an array of lazies containing the same type, initial value of result type, and a block
203
+ receiving accumulator value of result type, and value of next lazy.
204
+
205
+ ```ruby
206
+ sig { returns(::Mayak::Lazy[Integer]) }
207
+ def lazy
208
+ ::Mayak::Lazy.combine_many(
209
+ [::Mayak::Lazy[Integer].new(1), ::Mayak::Lazy[Integer].new(2), ::Mayak::Lazy[Integer].new(3)],
210
+ 0
211
+ ) { |acc, value| acc + value }
212
+ end
213
+
214
+ lazy.value # 10
215
+ ```
216
+
217
+ If you need to transform array of lazies of some value into lazy of array of the value, you can use `.sequence` method.
218
+
219
+ ```ruby
220
+ sig { returns(::Mayak::Lazy[T::Array[Integer]]) }
221
+ def lazy
222
+ ::Mayak::Lazy.sequence([::Mayak::Lazy[Integer].new(1), ::Mayak::Lazy[Integer].new(2), ::Mayak::Lazy[Integer].new(3)])
223
+ end
224
+
225
+ lazy.value # [1, 2, 3]
226
+ ```
data/lib/mayak/lazy.rb ADDED
@@ -0,0 +1,307 @@
1
+ # frozen_string_literal: true
2
+ # typed: strict
3
+
4
+ require "json"
5
+
6
+ module Mayak
7
+ class Lazy
8
+ extend T::Sig
9
+ extend T::Generic
10
+
11
+ Value = type_member
12
+
13
+ sig { params(blk: T.proc.returns(Value)).void }
14
+ def initialize(&blk)
15
+ @thunk = T.let(blk, T.proc.returns(Value))
16
+ @value = T.let(nil, T.nilable(Value))
17
+ @forced = T.let(false, T::Boolean)
18
+ end
19
+
20
+ sig { returns(Value) }
21
+ def value
22
+ if @forced
23
+ T.must(@value)
24
+ else
25
+ @forced = true
26
+ @value = @thunk.call
27
+ @value
28
+ end
29
+ end
30
+
31
+ sig {
32
+ type_parameters(
33
+ :NewValue
34
+ ).params(
35
+ blk: T.proc.params(arg0: Value).returns(T.type_parameter(:NewValue))
36
+ ).returns(
37
+ ::Mayak::Lazy[T.type_parameter(:NewValue)]
38
+ )
39
+ }
40
+ def map(&blk)
41
+ ::Mayak::Lazy.new { blk.call(value) }
42
+ end
43
+
44
+ sig {
45
+ type_parameters(
46
+ :NewValue
47
+ ).params(
48
+ blk: T.proc.params(arg0: Value).returns(::Mayak::Lazy[T.type_parameter(:NewValue)])
49
+ ).returns(
50
+ ::Mayak::Lazy[T.type_parameter(:NewValue)]
51
+ )
52
+ }
53
+ def flat_map(&blk)
54
+ ::Mayak::Lazy.new { blk.call(value).value }
55
+ end
56
+
57
+ sig {
58
+ type_parameters(
59
+ :AnotherValue,
60
+ :NewValue
61
+ ).params(
62
+ another: ::Mayak::Lazy[T.type_parameter(:AnotherValue)],
63
+ blk: T.proc.params(arg0: Value, arg1: T.type_parameter(:AnotherValue)).returns(T.type_parameter(:NewValue))
64
+ ).returns(
65
+ ::Mayak::Lazy[T.type_parameter(:NewValue)]
66
+ )
67
+ }
68
+ def combine(another, &blk)
69
+ ::Mayak::Lazy[T.type_parameter(:NewValue)].new do
70
+ blk.call(value, another.value)
71
+ end
72
+ end
73
+
74
+ sig {
75
+ type_parameters(
76
+ :Value
77
+ ).params(
78
+ lazies: T::Array[::Mayak::Lazy[T.type_parameter(:Value)]]
79
+ ).returns(
80
+ ::Mayak::Lazy[T::Array[T.type_parameter(:Value)]]
81
+ )
82
+ }
83
+ def self.sequence(lazies)
84
+ ::Mayak::Lazy[T::Array[T.type_parameter(:Value)]].new { lazies.map(&:value) }
85
+ end
86
+
87
+ sig {
88
+ type_parameters(
89
+ :FirstValue,
90
+ :SecondValue,
91
+ :ResultValue
92
+ ).params(
93
+ first: ::Mayak::Lazy[T.type_parameter(:FirstValue)],
94
+ second: ::Mayak::Lazy[T.type_parameter(:SecondValue)],
95
+ blk: T.proc.params(
96
+ arg0: T.type_parameter(:FirstValue),
97
+ arg1: T.type_parameter(:SecondValue)
98
+ ).returns(
99
+ T.type_parameter(:ResultValue)
100
+ )
101
+ ).returns(
102
+ ::Mayak::Lazy[T.type_parameter(:ResultValue)]
103
+ )
104
+ }
105
+ def self.combine_two(first, second, &blk)
106
+ ::Mayak::Lazy[T.type_parameter(:ResultValue)].new do
107
+ blk.call(first.value, second.value)
108
+ end
109
+ end
110
+
111
+ sig {
112
+ type_parameters(
113
+ :FirstValue,
114
+ :SecondValue,
115
+ :ThirdValue,
116
+ :ResultValue
117
+ ).params(
118
+ first: ::Mayak::Lazy[T.type_parameter(:FirstValue)],
119
+ second: ::Mayak::Lazy[T.type_parameter(:SecondValue)],
120
+ third: ::Mayak::Lazy[T.type_parameter(:ThirdValue)],
121
+ blk: T.proc.params(
122
+ arg0: T.type_parameter(:FirstValue),
123
+ arg1: T.type_parameter(:SecondValue),
124
+ arg2: T.type_parameter(:ThirdValue)
125
+ ).returns(
126
+ T.type_parameter(:ResultValue)
127
+ )
128
+ ).returns(
129
+ ::Mayak::Lazy[T.type_parameter(:ResultValue)]
130
+ )
131
+ }
132
+ def self.combine_three(first, second, third, &blk)
133
+ ::Mayak::Lazy[T.type_parameter(:ResultValue)].new do
134
+ blk.call(first.value, second.value, third.value)
135
+ end
136
+ end
137
+
138
+ sig {
139
+ type_parameters(
140
+ :FirstValue,
141
+ :SecondValue,
142
+ :ThirdValue,
143
+ :FourthValue,
144
+ :ResultValue
145
+ ).params(
146
+ first: ::Mayak::Lazy[T.type_parameter(:FirstValue)],
147
+ second: ::Mayak::Lazy[T.type_parameter(:SecondValue)],
148
+ third: ::Mayak::Lazy[T.type_parameter(:ThirdValue)],
149
+ fourth: ::Mayak::Lazy[T.type_parameter(:FourthValue)],
150
+ blk: T.proc.params(
151
+ arg0: T.type_parameter(:FirstValue),
152
+ arg1: T.type_parameter(:SecondValue),
153
+ arg2: T.type_parameter(:ThirdValue),
154
+ arg3: T.type_parameter(:FourthValue)
155
+ ).returns(
156
+ T.type_parameter(:ResultValue)
157
+ )
158
+ ).returns(
159
+ ::Mayak::Lazy[T.type_parameter(:ResultValue)]
160
+ )
161
+ }
162
+ def self.combine_four(first, second, third, fourth, &blk)
163
+ ::Mayak::Lazy[T.type_parameter(:ResultValue)].new do
164
+ blk.call(first.value, second.value, third.value, fourth.value)
165
+ end
166
+ end
167
+
168
+ sig {
169
+ type_parameters(
170
+ :FirstValue,
171
+ :SecondValue,
172
+ :ThirdValue,
173
+ :FourthValue,
174
+ :FifthValue,
175
+ :ResultValue
176
+ ).params(
177
+ first: ::Mayak::Lazy[T.type_parameter(:FirstValue)],
178
+ second: ::Mayak::Lazy[T.type_parameter(:SecondValue)],
179
+ third: ::Mayak::Lazy[T.type_parameter(:ThirdValue)],
180
+ fourth: ::Mayak::Lazy[T.type_parameter(:FourthValue)],
181
+ fifth: ::Mayak::Lazy[T.type_parameter(:FifthValue)],
182
+ blk: T.proc.params(
183
+ arg0: T.type_parameter(:FirstValue),
184
+ arg1: T.type_parameter(:SecondValue),
185
+ arg2: T.type_parameter(:ThirdValue),
186
+ arg3: T.type_parameter(:FourthValue),
187
+ arg4: T.type_parameter(:FifthValue)
188
+ ).returns(
189
+ T.type_parameter(:ResultValue)
190
+ )
191
+ ).returns(
192
+ ::Mayak::Lazy[T.type_parameter(:ResultValue)]
193
+ )
194
+ }
195
+ def self.combine_five(first, second, third, fourth, fifth, &blk)
196
+ ::Mayak::Lazy[T.type_parameter(:ResultValue)].new do
197
+ blk.call(first.value, second.value, third.value, fourth.value, fifth.value)
198
+ end
199
+ end
200
+
201
+ sig {
202
+ type_parameters(
203
+ :FirstValue,
204
+ :SecondValue,
205
+ :ThirdValue,
206
+ :FourthValue,
207
+ :FifthValue,
208
+ :SixthValue,
209
+ :ResultValue
210
+ ).params(
211
+ first: ::Mayak::Lazy[T.type_parameter(:FirstValue)],
212
+ second: ::Mayak::Lazy[T.type_parameter(:SecondValue)],
213
+ third: ::Mayak::Lazy[T.type_parameter(:ThirdValue)],
214
+ fourth: ::Mayak::Lazy[T.type_parameter(:FourthValue)],
215
+ fifth: ::Mayak::Lazy[T.type_parameter(:FifthValue)],
216
+ sixth: ::Mayak::Lazy[T.type_parameter(:SixthValue)],
217
+ blk: T.proc.params(
218
+ arg0: T.type_parameter(:FirstValue),
219
+ arg1: T.type_parameter(:SecondValue),
220
+ arg2: T.type_parameter(:ThirdValue),
221
+ arg3: T.type_parameter(:FourthValue),
222
+ arg4: T.type_parameter(:FifthValue),
223
+ arg5: T.type_parameter(:SixthValue)
224
+ ).returns(
225
+ T.type_parameter(:ResultValue)
226
+ )
227
+ ).returns(
228
+ ::Mayak::Lazy[T.type_parameter(:ResultValue)]
229
+ )
230
+ }
231
+ def self.combine_six(first, second, third, fourth, fifth, sixth, &blk)
232
+ ::Mayak::Lazy[T.type_parameter(:ResultValue)].new do
233
+ blk.call(first.value, second.value, third.value, fourth.value, fifth.value, sixth.value)
234
+ end
235
+ end
236
+
237
+ sig {
238
+ type_parameters(
239
+ :FirstValue,
240
+ :SecondValue,
241
+ :ThirdValue,
242
+ :FourthValue,
243
+ :FifthValue,
244
+ :SixthValue,
245
+ :SeventhValue,
246
+ :ResultValue
247
+ ).params(
248
+ first: ::Mayak::Lazy[T.type_parameter(:FirstValue)],
249
+ second: ::Mayak::Lazy[T.type_parameter(:SecondValue)],
250
+ third: ::Mayak::Lazy[T.type_parameter(:ThirdValue)],
251
+ fourth: ::Mayak::Lazy[T.type_parameter(:FourthValue)],
252
+ fifth: ::Mayak::Lazy[T.type_parameter(:FifthValue)],
253
+ sixth: ::Mayak::Lazy[T.type_parameter(:SixthValue)],
254
+ seventh: ::Mayak::Lazy[T.type_parameter(:SeventhValue)],
255
+ blk: T.proc.params(
256
+ arg0: T.type_parameter(:FirstValue),
257
+ arg1: T.type_parameter(:SecondValue),
258
+ arg2: T.type_parameter(:ThirdValue),
259
+ arg3: T.type_parameter(:FourthValue),
260
+ arg4: T.type_parameter(:FifthValue),
261
+ arg5: T.type_parameter(:SixthValue),
262
+ arg6: T.type_parameter(:SeventhValue),
263
+ ).returns(
264
+ T.type_parameter(:ResultValue)
265
+ )
266
+ ).returns(
267
+ ::Mayak::Lazy[T.type_parameter(:ResultValue)]
268
+ )
269
+ }
270
+ def self.combine_seven(first, second, third, fourth, fifth, sixth, seventh, &blk)
271
+ ::Mayak::Lazy[T.type_parameter(:ResultValue)].new do
272
+ blk.call(first.value, second.value, third.value, fourth.value, fifth.value, sixth.value, seventh.value)
273
+ end
274
+ end
275
+
276
+ sig {
277
+ type_parameters(
278
+ :Value,
279
+ :Result
280
+ ).params(
281
+ lazies: T::Array[::Mayak::Lazy[T.type_parameter(:Value)]],
282
+ initial: T.type_parameter(:Result),
283
+ blk: T.proc.params(arg0: T.type_parameter(:Result), arg1: T.type_parameter(:Value)).returns(T.type_parameter(:Result))
284
+ ).returns(
285
+ ::Mayak::Lazy[T.type_parameter(:Result)]
286
+ )
287
+ }
288
+ def self.combine_many(lazies, initial, &blk)
289
+ ::Mayak::Lazy[T.type_parameter(:Result)].new do
290
+ lazies.reduce(initial) { |acc, element| blk.call(acc, element.value) }
291
+ end
292
+ end
293
+
294
+ sig {
295
+ type_parameters(
296
+ :Value
297
+ ).params(
298
+ lazies: T::Array[::Mayak::Lazy[T.type_parameter(:Value)]]
299
+ ).returns(
300
+ ::Mayak::Lazy[T::Array[T.type_parameter(:Value)]]
301
+ )
302
+ }
303
+ def self.sequence(lazies)
304
+ combine_many(lazies, []) { |acc, element| acc.concat([element]) }
305
+ end
306
+ end
307
+ end