You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Now the compiler can show us exactly where the problem is originated. As we can see, providing type information is really useful for finding errors at compile time.
166
+
167
+
## Blocks
168
+
169
+
Methods may receive a `block of code` (or simply a `block`) as an argument. And using the keyword `yield` indicates the place in the method's body where we want to "place" said `block`:
170
+
171
+
```crystal-play
172
+
def with_42
173
+
yield
174
+
end
175
+
176
+
with_42 do
177
+
puts 42
178
+
end
179
+
```
180
+
181
+
### Blocks with parameters
182
+
183
+
We can parameterize `blocks` like this:
184
+
185
+
```crystal
186
+
some_method do |param1, param2|
187
+
end
188
+
```
189
+
190
+
And using *curly braces* notation would be:
191
+
192
+
```crystal
193
+
some_method { |param1, param2| }
194
+
```
195
+
196
+
We can rewrite our previous example, so that the `block` receives the number `42` as an argument.
197
+
198
+
```crystal-play
199
+
def with_42
200
+
yield 42
201
+
end
202
+
203
+
with_42 do |number|
204
+
puts number
205
+
end
206
+
```
207
+
208
+
And we can go a little further parameterizing also the method `#with_42` to receive not only the `block` but also the `number`:
209
+
210
+
```crystal-play
211
+
def with_number(n : Int32)
212
+
yield n
213
+
end
214
+
215
+
with_number(42) do |number|
216
+
puts number
217
+
end
218
+
219
+
with_number(24) do |number|
220
+
puts number
221
+
end
222
+
```
223
+
224
+
### Blocks' returned value
225
+
226
+
A `block`, by default, returns the value of the last expression (the same as a method).
227
+
228
+
```crystal-play
229
+
def with_number(n : Int32)
230
+
result = yield n
231
+
puts result
232
+
end
233
+
234
+
with_number(41) do |number|
235
+
number + 1
236
+
end
237
+
```
238
+
239
+
#### Returning keywords
240
+
241
+
We can use the `return` keyword ... but, let's see the following example:
242
+
243
+
```crystal-play
244
+
245
+
def with_number(n : Int32)
246
+
result = yield n
247
+
puts result
248
+
end
249
+
250
+
def test_number(n)
251
+
with_number(n) do |number|
252
+
# this block returns if the number is negative
253
+
return number if number.negative?
254
+
number + 1
255
+
end
256
+
257
+
puts "Inside `#test_number` method after `#with_number`"
258
+
end
259
+
260
+
test_number(42)
261
+
```
262
+
263
+
Outputs:
264
+
265
+
```console
266
+
267
+
43
268
+
Inside `#test_number` method after `#with_number`
269
+
```
270
+
271
+
And if we want to `test_number(-1)` we would expect:
272
+
273
+
```console
274
+
275
+
-1
276
+
Inside `#test_number` method after `#with_number`
277
+
```
278
+
279
+
Let's see ...
280
+
281
+
```crystal-play
282
+
283
+
def with_number(n : Int32)
284
+
result = yield n
285
+
puts result
286
+
end
287
+
288
+
def test_number(n)
289
+
290
+
with_number(n) do |number|
291
+
# this block returns if the number is negative
292
+
return number if number.negative?
293
+
number + 1
294
+
end
295
+
296
+
puts "Inside `#test_number` method after `#with_number`"
297
+
end
298
+
299
+
test_number(-1)
300
+
```
301
+
302
+
The output is empty! This is because Crystal implements *full closures*, meaning that using `return` inside the block will return, not only from the `block` itself but, from the method where the `block` is defined (`#test_number` in the above example).
303
+
304
+
If we want to return only from the `block` then we can use the `next` keyword:
305
+
306
+
```crystal-play
307
+
308
+
def with_number(n : Int32)
309
+
result = yield n
310
+
puts result
311
+
end
312
+
313
+
def test_number(n)
314
+
with_number(n) do |number|
315
+
# this block returns if the number is negative
316
+
next number if number.negative?
317
+
number + 1
318
+
end
319
+
320
+
puts "Inside `#test_number` method after `#with_number`"
321
+
end
322
+
323
+
test_number(-1)
324
+
```
325
+
326
+
The last keyword for returning from a `block` is `break`. Let's see how it behaves:
327
+
328
+
```crystal-play
329
+
330
+
def with_number(n : Int32)
331
+
result = yield n
332
+
puts result
333
+
end
334
+
335
+
def test_number(n)
336
+
with_number(n) do |number|
337
+
# this block returns if the number is negative
338
+
break number if number.negative?
339
+
number + 1
340
+
end
341
+
342
+
puts "Inside `#test_number` method after `#with_number`"
343
+
end
344
+
345
+
test_number(-1)
346
+
```
347
+
348
+
The ouput is
349
+
350
+
```console
351
+
352
+
Inside `#test_number` method after `#with_number`
353
+
```
354
+
355
+
As we can see the behaviour is something between using `return` and `next`. With `break` we return from the `block` and from the method yielding the `block` (`#with_number` in this example) but not from the method where the `block` is defined.
356
+
357
+
### Non-captured blocks
358
+
359
+
Up to here we've been using `non-captured blocks`, meaning that **the `non-captured block` is inlined** in the place where we use `yield`. So this code:
360
+
361
+
```crystal-play
362
+
def with_number(n : Int32)
363
+
result = yield n
364
+
puts result
365
+
end
366
+
367
+
with_number(41) do |number|
368
+
number + 1
369
+
end
370
+
```
371
+
372
+
it's the same as:
373
+
374
+
```crystal-play
375
+
def with_number(n : Int32)
376
+
number = n
377
+
result = number + 1
378
+
puts result
379
+
end
380
+
381
+
with_number(41)
382
+
```
383
+
384
+
### Captured blocks
385
+
386
+
When a `block` is captured then a [Proc](https://crystal-lang.org/reference/latest/syntax_and_semantics/literals/proc.html) is created with its `context` (ie. its [closure](https://crystal-lang.org/reference/latest/syntax_and_semantics/closures.html)). This means that **the `captured block` is not inlined**.
387
+
388
+
To capture a `block` we need to add it as a parameter, specifying its name and type.
0 commit comments