-
-
Notifications
You must be signed in to change notification settings - Fork 248
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add Blocks section to Tutorials #655
base: master
Are you sure you want to change the base?
Add Blocks section to Tutorials #655
Conversation
✅ Deploy Preview for crystal-book ready!
To edit notification comments on pull requests, go to your Netlify site settings. |
b5afae2
to
6524049
Compare
6524049
to
1ae7b88
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I didn't check it entirely because I have a high-level point that probably needs addressing. I find the example a bit convoluted, probably because it's artificial. Maybe consider using the loop
toplevel method? This could be good to explain when to skip (next
) and when to break
.
For instance:
# print even numbers from 0 to 10
i = 0
loop do
i += 1
next if i % 2 == 1
p i
break if i == 10
end
The example doesn't need to be the same from top to bottom. The important bit is that it can be described easily as I did above, to help understanding that it does (and why is important to learn).
This content is quite a lot and hardly fits into the Methods lesson (which doubles in size). I'd suggest to introduce it as a separate lesson called Blocks in order to keep every single one at a reasonable length. Regarding blocks, there are also some more aspects that need to be addressed. Not all of them are essential to be fully fleshed out right away, but should be covered eventually.
The language specification already covers a lot of that and we can copy ideas from there, adapted to a tutorial format. |
2f2015d
to
1f98817
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Don't be alarm by the amount of comments, there are mostly nitpickings :-)
def with_42 | ||
yield | ||
end | ||
|
||
with_42 do | ||
puts 42 | ||
end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since there's nothing in the method that suggest a 42, what about this name instead?
def with_42 | |
yield | |
end | |
with_42 do | |
puts 42 | |
end | |
def run | |
yield | |
end | |
run do | |
puts 42 | |
end |
def three_times | ||
yield | ||
yield | ||
yield | ||
end | ||
|
||
three_times do | ||
puts 42 | ||
end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just if the previous comment is accepted:
def three_times | |
yield | |
yield | |
yield | |
end | |
three_times do | |
puts 42 | |
end | |
def run_three_times | |
yield | |
yield | |
yield | |
end | |
run_three_times do | |
puts 42 | |
end |
|
||
Let's see another examples with a common use scenario for *blocks*: collections. | ||
|
||
Here are two examples: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A real nitpick.
Here are two examples: | |
Consider the following two examples: |
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/Indexable.html#%5B%5D%28index%3AInt%29-instance-method) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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/Indexable.html#%5B%5D%28index%3AInt%29-instance-method) | |
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/Indexable.html#%5B%5D%28index%3AInt%29-instance-method). Of course, it will raise an exception if we try to unpack more elements than existing. You can try it yourself, by adding a parameter or by removing an element. |
``` | ||
|
||
NOTE: | ||
`Tuples` also implements [Tuple#[ ]](https://crystal-lang.org/api/Tuple.html#%5B%5D%28index%3AInt%29-instance-method) meaning that we can also use *unpacking*. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
`Tuples` also implements [Tuple#[ ]](https://crystal-lang.org/api/Tuple.html#%5B%5D%28index%3AInt%29-instance-method) meaning that we can also use *unpacking*. | |
`Tuples` also implements [Tuple#[ ]](https://crystal-lang.org/api/Tuple.html#%5B%5D%28index%3AInt%29-instance-method) meaning that we can also use *unpacking*. But there's a difference in how unpacking or auto-splatting works with tuples when there aren't sufficient elements. Try it, can you spot the difference? |
|
||
Great! Let's explain it step by step: | ||
|
||
First we split the block parameter using the *short one-parameter syntax* `&.split`. Then, we chained the result applying `map` and using again the *short one-parameter syntax* `.map(&.capitalize)`. And finally we join the `array` of `strings` using `join(' ')`. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
First we split the block parameter using the *short one-parameter syntax* `&.split`. Then, we chained the result applying `map` and using again the *short one-parameter syntax* `.map(&.capitalize)`. And finally we join the `array` of `strings` using `join(' ')`. | |
First we split the block parameter using the *short one-parameter syntax* `&.split`. Then, we chained the result applying `map` and using again the short one-parameter syntax `.map(&.capitalize)`. And finally we join the `array` of `strings` using `join(' ')`. |
|
||
First we split the block parameter using the *short one-parameter syntax* `&.split`. Then, we chained the result applying `map` and using again the *short one-parameter syntax* `.map(&.capitalize)`. And finally we join the `array` of `strings` using `join(' ')`. | ||
|
||
Now, what if we want to parameterize the string we transform. Can we still use the *short one-parameter syntax*? The answer is yes! Let's see: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Now, what if we want to parameterize the string we transform. Can we still use the *short one-parameter syntax*? The answer is yes! Let's see: | |
Now, what if we want to parameterize the string we transform. Can we still use the short one-parameter syntax? The answer is yes! Let's see: |
|
||
## Under the hood | ||
|
||
Before finishing this section of the tutorial, it would be a good idea to see how *blocks* work under the hood. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Before finishing this section of the tutorial, it would be a good idea to see how *blocks* work under the hood. | |
Before finishing this section of the tutorial, it would be a good idea to see how blocks work under the hood. |
|
||
Before finishing this section of the tutorial, it would be a good idea to see how *blocks* work under the hood. | ||
|
||
First, it's important to note that in this section we have only seen *blocks* that we use with the keyword `yield`. There is [another kind of block](../../syntax_and_semantics/capturing_blocks.md), but we will leave it for later in the tutorial. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
First, it's important to note that in this section we have only seen *blocks* that we use with the keyword `yield`. There is [another kind of block](../../syntax_and_semantics/capturing_blocks.md), but we will leave it for later in the tutorial. | |
First, it's important to note that in this section we have only seen blocks that we use with the keyword `yield`. There is [another kind of block](../../syntax_and_semantics/capturing_blocks.md), but we will leave it for later in the tutorial. |
|
||
First, it's important to note that in this section we have only seen *blocks* that we use with the keyword `yield`. There is [another kind of block](../../syntax_and_semantics/capturing_blocks.md), but we will leave it for later in the tutorial. | ||
|
||
In a method that receives a *block*, when we write `yield`, the compiler will inline the *block of code*, which means that this: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In a method that receives a *block*, when we write `yield`, the compiler will inline the *block of code*, which means that this: | |
In a method that receives a block, when we write `yield`, the compiler will inline the block of code, which means that this: |
In this PR we add an intro to Blocks in the Methods section and continue in a
Blocks
section.We talked about: