3 ms·
Assuming you're not talking about values, there's really two things which can be passed to a ruby function: normal arguments and blocks. And arguments may be p
by masklinn 3y ago
Assuming you're not talking about values, there's really two things which can be passed to a ruby function: normal arguments and blocks.
And arguments may be passed either positionally or by name.
Blocks are the weird part, they have special support because they're a bunch of syntactic sugar / magic, which doesn't even have to be part of the function's prototype e.g
def foo
yield if block_given?
end
"yield" will invoke the implicit block, "block_given?" is a metamagic kernel method which checks if the current context was passed a block.
I haven't kept up to date with Ruby so I don't know if that style is still en vogue. The equivalent "explicit" version is
def foo &block
block.call if block
end
in which the block — if passed — is reified to a Proc, which you can manipulate normally. If no block is used, then `block` will just be `nil` (as the essay notes, blocks are always optional).
- mdavidn 3y agoThese two forms are equivalent in modern Ruby, but they weren't always. The explicit &block required a heap allocation to close over the block's local scope. This is a "reified" Proc object. The implicit block could remain on the stack, since a block never outlives the stack frame that defined it. Fun fact: Both yield and block.call may never return. The block is free to return from its enclosing method, which effectively pops multiple frames, including foo and possibly more, off the stack.
- shadowbanned4 3y ago> The block is free to return from its enclosing method, which effectively pops multiple frames, including foo and possibly more, off the stack. As an aside, this is one of the key differences between a lambda and a proc. Last I checked, you can pass a lambda as a block, but it is implicitly converted to a proc.
- throwanem 3y agoI'm glad you mentioned that! The existence of three distinct abstractions over the concept of function values is another good example of Ruby's flawed design.
- djur 3y agoUnlike JavaScript, which only has, uh, three (functions, arrow functions, and methods).
- throwanem 3y agoNo, Javascript has functions. That arrow functions handle the "this" binding specially I'll grant as a potential design error, and certainly as a complication; to reiterate, I have not said Javascript is perfect. But classes and methods are sugar over prototype-based objects from 1995, which you are still perfectly free to write if you like.
- masklinn 3y ago> That arrow functions handle the "this" binding specially It’s more the opposite, arrow functions treat `this` as a regular lexical variable, it’s normal functions which special-case `this`.
- vidarh 3y agoThey're not three different abstractions. The distinction between a block and a Proc is an implementation detail - an implementation can choose to always make blocks Procs (my experimental Ruby compiler does just that) and a program shouldn't be able to tell the difference. The distinction between proc and lambda with respect to how returns are handled provides significant utility with very low added implementation complexity that for the most part work together to do what people will expect of them. That is, the most logical reading of the block syntax is that a return will return from the function it is lexically in, so it does, while a lambda looks like a function, and acts accordingly. If anything, to me these are prime examples of what makes Ruby a great language that values developer happiness over pointless exercises in purity.