-
-
Notifications
You must be signed in to change notification settings - Fork 5.5k
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
ruby-like blocks #441
Comments
What do you think of Smalltalk-style alternating |
Good that it generalizes to N blocks, but in our case the problem will be that we're out of ascii characters :) |
I think it's useful to take a couple of motivating examples. One thing Ruby's syntax optimizes for is the case where you have one or more non-block args, and one block arg. For example, a method that opens a path, gives your block the file, and then cleans up when the block is done: File.open("foo", "w") {|file| file.puts("hello world") } In Julia this would currently have to look something like: file_open("foo", "w", file -> write(file, "hello world")) You could imagine some similar convention to Ruby's where if the last arg is a function, it can be broken out into curly braces outside the parens: file_open("foo", "w"){file -> write(file, "hello world")} The other thing Ruby's blocks optimize for is long pipelines; for example, array.map{|x| x * 2}.filter{|x| x > 10}.map{|x| x*x} In Julia, this would look like: map(x -> x*x, filter(x -> x > 10, map(x -> x * 2, array))) It's less clear how to bring the spirit of what Ruby offers over, given the fundamental difference between the single-dispatch message-passing OO style it uses and the generic function style that Julia uses. One possibility would be a pipe operator which takes the result of one function call and splices it in as the last argument of the next, so that you'd get something like this: array | map(x -> x * 2) | filter(x -> x > 10) | map(x -> x*x) But I'm not at all sure that you want to go there... |
I've thought about a "pipe operator" before, but more shell-like. The idea would be to take the producer/consumer business that we already support with coroutines but make it more efficient by allowing the specification of a buffer of some size for produced objects. That would reduce the frequency of context switches, making things run faster. And it would you do to things like this:
Not sure about the details, but it seems a shame to not be able to do shell-style coding in a high-level language. Why not? That, of course, doesn't at all address the Ruby-style block issue. One observations is that we can't really use curly braces because we're already using them for type parameters. ASCII is limited but making core language features non-ASCII strikes me as a very bad idea. |
Ok, so I spent a long time thinking about this (while hanging out an estancia a couple hours outside of Buenos Aires), and settled on this following scheme, which Jeff seems on board with. Barring major objections, I think this is what we should go with. It's very simple and derives quite a lot from Ruby, which, of course, pioneered this blocks-attached-to-function-calls-with-syntax business. This is so anticlimactic:
is just syntax for this:
So yeah, it's a lot like Ruby's block-on-a-method syntax. Which is a good thing. It will be obvious to anyone who's done any Ruby programming what's going on. It's pretty simple too: it just adds a new keyword — which is actually not insignificant, but I think we can spare the identifier The trivial difference is that it doesn't use Ruby's weirdo The major difference from Ruby is that this argument is not a special named argument and it doesn't go at the end. Instead, it's just the first argument to the function being called. This is because most higher-order functions take the function as their first argument. Thus, you can write a complicated map operation like this:
At first I was thinking of having the function block passed as the last parameter for obvious reasons — partly driven by the use case of
This would allow the block calling form like so:
At first I thought that the non-block form of
This opens the file
which is really verbose for something that simple, or to just let the file be closed when gc reclaims the file handle object:
Comparing this with putting the function at the end: The other difference from Ruby, of course, is that there is no |
It's interesting to look at how my contrived "pipeline" example looks with this syntax: map(filter(map(array) do x I can't imagine anyone ever using that. You'd just introduce some local variables instead, I guess. However, for the file-open case, it definitely does work well. |
That's not quite the syntax — there's a bunch of ends missing. Also, I certainly wasn't thinking about having these to be chained. The chaining thing really only works in Ruby for the short block syntax, which we're not going to have, and because Ruby operates on containers by doing |
Another possible addition to this would be support for else blocks as proposed here. The idea would be that something like
would be syntax for this:
The open function could also support this form too and else block could be used to handle failure to open the file instead of wrapping the whole thing in a try/catch construct. In general, this sort of syntactic construct would allow a lot of syntax-like functionality to be implemented just using higher-order functions, which is the goal of this issue. |
I like that a lot. I'm a Smalltalker, and Smalltalk has a very natural syntax for methods that take multiple blocks/functions that it uses for things like if/else, try/catch, etc. I'd say that in 90% of the cases with 2 blocks, one can be thought of as an "else" block. (It would be lovely to see Julia become so consistent around this that if() was just a method, so that it would be if(bool) do ... else ... end; nearly always inlined, obviously) |
This non-breaking feature would address the regex redesign issue #88 as well, so I'm going to close that. |
Won't we want stuf like:
Shortened too? for instance avoiding repeating
And that clause will be executed if that function returns true.
Other stuff can then be added, i guess. I called it The clauses can of course also do stuff like We might also want to allow users to define what the body before the first keyword does, with that it could go as far as 'emulate' |
+1 for the syntax |
I propose this syntax. |
A Python take on code blocks: http://mtomassoli.wordpress.com/2012/04/20/code-blocks-in-python/. |
I wrote a macro that permit notation like
|
@miau, that looks a lot like desugared LINQ/desugared Scala for comprehension/bind chain on the List monad. I'm not sure if this is really the same feature as code blocks anymore, though I do like the idea. I'd be happy to see a macro implementing LINQ-style query expressions (or for comprehensions, or EDIT: On this particular topic, see also #550. |
Huh. That's very cool. I do think this is a very different feature from code blocks, which serve to make coding with closures feel more like syntactic constructs. But this sort of piped programming also has its place. |
Basically piping is function composition.. Ok, not entirely; Anyway guessing one thing we want is to have these things also work nicely outside piping. Like The clauses/ |
This could really benefit from block sytnax a la #441: chdir(dir) do # code to be executed from dir end
Can be used as follows: @chdir dir begin # do stuff from dir end
Re-opening for else-blocks. |
I guess I should allow chaining, as in
? |
Why allow chaining? There's no obvious use case for that. |
If this is used for matching-type thingies, one often wants to try matching a series of patterns in succession. |
Ok, that sounds plausible. Can you give an example so I can get a more concrete sense? |
I'm closing this since we can just open a new issue if we ever want else blocks. |
`evaluate_call_recurse!` calls `maybe_evaluate_builtin`, and if that doesn't return a `Some{Any}`, it uses the output as the new `call_expr`. The next thing it does is look up the args. Consequently, to avoid double-lookup, our expansion of `invoke` should return the non-looked-up arguments. Fixes #441 Closes #440
This has come up a couple of times. I'd like to accumulate syntax suggestions here.
The text was updated successfully, but these errors were encountered: