Skip to content

Commit

Permalink
Add Type restrictions. Fixes
Browse files Browse the repository at this point in the history
  • Loading branch information
ftarulla committed Dec 29, 2022
1 parent ee92e43 commit d15fc14
Showing 1 changed file with 70 additions and 15 deletions.
85 changes: 70 additions & 15 deletions docs/tutorials/basics/70_blocks.md
Original file line number Diff line number Diff line change
Expand Up @@ -98,12 +98,7 @@ The abstracted structure declared in the `with_array` method is very common and

## Blocks with parameters

Like methods, _blocks_ may receive parameters:

```crystal
some_method do |param1, param2, param3|
end
```
Like methods, _blocks_ may receive parameters declared like this: `| param1, param2, ... |`.

Let's see an example:

Expand All @@ -112,7 +107,7 @@ def other_method
yield 42, "Hello", [1, "Crystal", 3]
end
other_method do |n, s, arr|
other_method do |n, s, arr| # here the block declare 3 parameters
puts "#{s} #{arr[1]}"
puts n
end
Expand All @@ -134,7 +129,9 @@ end

### Unpacking parameters

Let's suppose we have an array of arrays and our _block_ prints each element in each array. One way to implement this would be:
Now let's suppose we have an array of arrays. And we want to print each array in the following way: _the first element followed by a hyphen, followed by the second element_.

One way to implement this would be:

```crystal-play
arr = [["one", 42], ["two", 24]]
Expand All @@ -143,7 +140,7 @@ arr.each do |arg|
end
```

This works, but there is a concise way of writing the same but using parameter unpacking:
This works, but there is a more readable way of writing the same but using parameter unpacking::

```crystal-play
arr = [["one", 42], ["two", 24]]
Expand All @@ -152,13 +149,13 @@ arr.each do |(word, number)|
end
```

**Note:** we use parentheses to unpack the argument in the different block parameters.
**Note:** we use parentheses to unpack the argument into the different block parameters.

Unpacking arguments into parameters works only if the argument's type responds to `[i]` (with `i` an `integer`). In our example `Array` inherits [Indexable#[ ]](https://crystal-lang.org/api/latest/Indexable.html#%5B%5D%28index%3AInt%29-instance-method)

### Splats

When the expected _block_ argument is a [Tuple](../syntax_and_semantics/literals/tuple.html) we can use auto-splatting (see [Splats](../syntax_and_semantics/operators.html#splats)) as a way of destructuring the `tuple` in block parameters (and without the need of parentheses).
When the _block_ parameter is a [Tuple](../syntax_and_semantics/literals/tuple.html) we can use auto-splatting (see [Splats](../syntax_and_semantics/operators.html#splats)) as a way of destructuring the `tuple` in block parameters (and without the need of parentheses).


```crystal-play
Expand All @@ -168,9 +165,9 @@ arr.each do |word, number|
end
```

**Note:** `Tuples` also implements [Tuple#[ ]](https://crystal-lang.org/api/latest/Tuple.html#%5B%5D%28index%3AInt%29-instance-method) meaning that we also can use _unpacking_.
**Note:** `Tuples` also implements [Tuple#[ ]](https://crystal-lang.org/api/latest/Tuple.html#%5B%5D%28index%3AInt%29-instance-method) meaning that we can also use _unpacking_.

### Block's returned value
## Blocks' returned value

A _block_, by default, returns the value of the last expression (the same as a method).

Expand All @@ -185,7 +182,7 @@ with_number(41) do |number|
end
```

#### Returning keywords
### Returning keywords

We can use the `return` keyword ... but, let's see the following example:

Expand Down Expand Up @@ -285,11 +282,69 @@ end
test_number(-1)
```

The ouput is
The output is

```console

Inside `#test_number` method after `#with_number`
```

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.

## Type restrictions

Until now we have been using _blocks_ without any kind of type restrictions, moreover, we did not declare the block as a method parameter (it was implied by the use of `yield`).

So first, we will declare a _block_ as a method parameter: it should be placed last and the parameter's name should be prefixed by `&`. Then we can use `yield` as before.

```crystal-play
def transform_string(word, &block)
block_result = yield word
puts block_result
end
transform_string "crystal" do |word|
word.capitalize
end
```

Now, we can add type restrictions to our method's parameters:

```crystal-play
def transform_string(word : String, &block : String -> String)
block_result = yield word
puts block_result
end
transform_string "crystal" do |word|
word.capitalize
end
```

Let's focus on the block's type `&block : String -> String`. What we are saying is that the block will receive a `String` and return a `String`.

So, if the block tries to return an `Int` the compiler will say:

```crystal
def transform_string(word : String, &block : String -> String)
block_result = yield word
puts block_result
end
transform_string "crystal" do |word|
42 # Error: expected block to return String, not Int32
end
```

Or if we try to give an `Array(String)` as an input to the block, the compiler will say:

```crystal
def transform_string(word : String, &block : String -> String)
block_result = yield [word] # Error: argument #1 of yield expected to be String, not Array(String)
puts block_result
end
transform_string "crystal" do |word|
word.capitalize
end
```

0 comments on commit d15fc14

Please sign in to comment.