mayak 0.2.1 → 0.2.2

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,226 +0,0 @@
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
- ```