make-range from: 10 to: 20 calss (make-range.from:to 10 20) or something similar. Free nanodsls
For the uninitiated among us, what does that do and what's so magic about it? make-range from: 10 to: 20 calss (make-range.from:to 10 20) or something similar. Free nanodsls
For the uninitiated among us, what does that do and what's so magic about it? 1 to: 10.
Sends the "to:" message to the number 1 with the parameter 10. Returns a range representing the numbers from 1 to 10. You can then iterate that range with by sending it the "do:" message with a block argument. 1 to: 10 do:[ :i | <do something with i> ].
And so on. Looks a lot like syntax for a for-loop, but is just message sends to collections with keyword syntax. Every type of collection implements do: (and collect:, select:, reject: etc.) Conditionals work the same way: 1 > 0 ifTrue: [ <do something here> ]
Sends the ifTrue: message to the result of 1 >0. This will be the true object, which is the sole instance of the True class (subclass of Boolean). The True class defines ifTrue: so that it executes the block. Conversely, the False class defines ifTrue: so that it does nothing, ignoring the block.The fact that what is usually syntax is just message sends means that you can define your own messaging protocols in libraries that also look like syntax.
Dan Ingalls explains: https://youtu.be/P2mh92d-T3Y?t=574
Also, the Grace language expands on this concept by allowing effectively regular expressions of these keyword parameters. Really quite nifty.
1 to: 10 do:[ :i | <do something with i> ].
would be a single message `to:do:`, not a chain of messages. (1 to: 10) do: [ :i | Transcript show: i ; cr].
also works in Pharo. Both are valid because:"to:do:" is a message understood by the Number class
"to:" is a message also understood by the Number class that yields an Interval object, which understands the "do:" message.
I didn't say you could not build a message chain to do that, only that to:do: as shown in mpweiher's original comment is a single message ;)
As the other respondent wrote, it still works the same way but with parentheses, and the to:do: exists as a convenience.
In Objective-Smalltalk[1], I added | so you can do this without parenthesis:
(1 to:10) do: [ :i | stdout println:i. ].
can become: 1 to:10 | do: [ :i | stdout println:i ].
This is nice when you are constructing a statement interactively, for example on a command line and don't want to go back. It also makes things less convoluted when the chain gets longer. It also "fixes" the asymmetry that chaining works with unary messages but not with keyword messages.Of course, the whole thing becomes even more elegant, IMHO, with a Higher Order Message (HOM)[2]:
stdout do println: (1 to:10) each.
YMMV.From APIs to languages: generalising method names
https://dl.acm.org/citation.cfm?doid=2936313.2816708
pdf: https://michael.homer.nz/Publications/DLS2015/paper.pdf
morning paper: https://blog.acolyer.org/2015/11/09/from-apis-to-languages-g...
obj.do_thing("string", with_factor=8, log_to=logger)
obj do_thing: "string" with_factor: 8 log_to: logger
One of those reads as a sentence a little better. But to be fair python learned this from languages that had already taken this idea from smalltalk.Like most older languages smalltalk's implementation is more powerful than the bastardization that other languages learned from it. Library interfaces in smalltalk are built around this syntax feature. Note how the function call looks the same as the keyword argument. This shared syntax makes the language read better. It makes function calls and keywords look like a normal infix language (e.g. the math expressions in most languages). Where parens are used like any other infix language (rather than be overloaded for function calls):
(obj do_thing: "string" with_factor: 8) log_to: logger
would then call ".log_to(logger)" on the result of the parenthesized expression. The same way in (2 + 4) * 5 multiplies the result of the parens by 5. A single syntax structure for all these concepts - rather than mixing multiple syntax - allows it to express new ideas in the same syntax as everything else. Like lisp does.> This shared syntax makes the language read better. It makes function calls and keywords look like a normal infix language (e.g. the math expressions in most languages).
Ironically, since Smalltalk also applies this uniformity to arithmetic operators, they don't behave how most people would expect: a + b * c - d is executed like (((a + b) * c) - d).
That’s a fairly trivial example but it demonstrates how readability is improved. You can imagine how this becomes even more useful with methods that take many parameters.
An interesting document to look at regarding this is Swift’s guidelines on naming methods.
Is that something like keyword arguments in Lisp?
The method name would be "rangeFrom:to:withStep:" and that means you call it exactly like that, with arguments after the colon.
Objective C is the same.
The syntax you show is Swift, which is a weird mash-up of C/C++ style function call/method call syntax with keywords jammed into them. To me, actual Smalltalk syntax is just so much cleaner:
0 to: 10.
It manages the "reads like english" trick without trying to be english-like or natural-language-like. (See Applescript for the disaster that results from that: "Many of the current problems in AppleScript can be traced to the use of syntax based on natural language" http://www.cs.utexas.edu/~wcook/Drafts/2006/ashopl.pdf).You have to write it in Applescript, but you get to read it only in transliterated Perl.