-
Notifications
You must be signed in to change notification settings - Fork 2k
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
pipe to chainable operators/functions #281
Comments
Lol, is it just the example or is it just me, but I much prefer the version without the pipe? In nix environments the pipe could be used to *pass data to another executable where the syntax here is inverted and much harder to understand. Anyway, it is just my opinion -- I am sure someone else would find it rather useful and convenient. |
I'm not sure why you think the syntax here is inverted. E.g grep: (search, data) -> ret: [] for line in data ret.push(line) if line.match search ret file.read("x").split("\n") | grep "cats" # returns a list of lines containing the word "cats" |
Great, it was the example in the original post then! This one makes much more sense and is very practical. |
Weepy -- This is pretty darn nice. But you still need to figure out how to pass the return value from the previous function into the next one -- is it the first argument, or is it the last argument? Or can you pass more than one argument along? Also, it doesn't play nicely with OOP-style calls directly on the object.
How would you call reverse on the array, given that it's not an external function? If you have utilities, where each is stand-alone and stakes a single STDIN and writes to a single STDOUT, piping can be uniform. But with functions attached to objects with arbitrary arguments, it's more complicated. |
It can be either ... both have advantages, but it shouldn't matter too much either way. As far as "passing more arguments along" goes, I'm not quite sure - can the unix pipe do this ? I think your example is missing a (array | map (x) -> x * x).reverse() or perhaps have a naked array | map (x) -> x * x | .reverse() It can trivially support more than one option on the 'utility' function: array | sort_limit 5, "DESC" | .reverse() But it might be difficult to have more than one "input". I'm not arguing that this is better or more applicable or a replacement for standard function calls. It does have limitations (which in fact lead directly to it's strength) and for particular parts of coding it could be useful, particularly for data manipulation. |
That idea is awesome. But it does need to be sorted out. I think a natural and intuitive rule for passing arguments to functions could be that the previous result be used as the first argument and the rest be added to it. This way the reverse example could read :
There could also be a convention to encounter the case where the previous operation did not return a value, like using the previous one or maybe add half-expression :
|
Ok - I think that we're agreed that the inbound argument should be used as the first argument to the function, so map would look something like: map: (A, fn) -> fn(v,i) for v,i in A As far as calling other calling conventions, how about this rule :
|
Alrighy, here is the commit on my pipe branch.
The pipe character can be used to end a line however:
Jeremy, what do you think? Simple enough, no? Can you help with issue 2). I am hitting conflicts in the grammar when using the As for 1) I would not consider that an issue. |
|
Wonderful work Stan |
It might be worth aliasing the standard |
weepy, the following would still produce the expected result:
The new pipe syntax kicks in only if we have an invocation to the right:
I reckon as long as this is documented, it shouldn't be a big issue. I certainly don't do bitwise operations between method calls. EDIT: the new pipe syntax could be really useful for something close to LINQ:
and if we clean it up to have optional parentheses:
|
Ok. Pipes were briefly on master this morning, and we had a spirited debate about them on
So, closing as a |
Ah -- unfortunately I missed the spirited discussion :-(! The trouble with achieving the same through method chaining is that all the methods need to be placed on the objects themselves. This isn't very good for Object and Array as we all know. I was hoping that it could lead to a nice data manipulation syntax for Coffee. It's limitations obviously mean that any functions that use it would have to be specifically designed so (first argument for the data), but I don't think that means they wouldn't be useful enough? |
If you already have to define special functions to use it, then you might as well design those special functions to enable chaining. |
Yes you are right. I suppose the main thrust of what I'm trying to achieve is ruby's Enumerable for Javascript's built in classes, without breaking Array and Object prototype's. Underscore is a similar attempt and it's only downfall is the slightly cumbersome syntax. Since a large portion Coffee/Javascript involves this data manipulation - it seems a shame that we don't have a decent way to address it. What's your position on this? |
If you want to use Ruby-in-JavaScript, the best library for that has always been Prototype.js. I know that it doesn't work on the server-side, but if you're doing a browser project, Prototype should let you write your example from above:
As:
|
There's also: http://github.com/280north/narwhal/blob/master/engines/default/lib/global-es5.js Which adds the ECMAScript5 utility functions in a safer fashion, including
|
That's pretty nice - thanks J |
Hm, that's a good one .. |
Just looking at this code from there. Array.prototype.forEach = function(block, thisObject) { var len = this.length >>> 0; for (var i = 0; i < len; i++) { if (i in this) { block.call(thisObject, this[i], i, this); } } }; What is the |
Does some weird division stuff. |
For positive numbers, anyway. |
I'd like to argue that having pipes would allow us to write new code in a more expressive way. Python and negative slices are dead and having an
The alternative is obvious, but not as expressive.
We are now pretty much used to half-operators and the above should be a nice addition. Rubysim?
So my main argument again is: look at pipes as means for you to write code in a new way. Sure, you can do |
i think it's one of those ideas that would need trying in real code before figuring out whether it's good or not (mainly cos it needs a bit of a Brain Backflip) :-) |
Can we consider using an alternative syntax which should allow anyone using the
|
15 lines of diff to add it in the core? |
I know I'm late to the discussion.
You would write
Would work nicely with anonymous functions:
|
|
Jeremy, I'm not a big fan of 'using'.
Why don't we put an end to that completely?
... could be
... which is pretty close to
|
I think this is a nice way to put it! It makes sense and would certainly be more readable than |
Hugh Jackson's Tap-chain allows us to write calls in the order they will be evaluated.
Parenthesis are needed for all but the last call. |
Like underscore does. For chaining, there's other issues opened |
A lot of maths is based around binary operators e.g,
* + -
etc. They are really just a alternate function definition. E.g.The main reason we tend to use f(a,b,c) type function definitions is because they can take an arbitrary number of arguments, rather than just the two of binary operators.
However as you can see they are much easier to parse or 'stream' due to the way they are set out. Ruby's enumerable functions tend to work like binary operators, which is why they are so nice to use (read chainable). This is also mimicked by libraries like jQuery and Underscore. Also the pipe operator in Unix works on a similar premise.
I've been wondering if it would be possible to provide a way for Coffee to provide operators that mapped to functions. I think it could provide a very nice way of programming.
This is really just a ticket to open a discussion about them, but in terms of syntax/implementation, there is an idea I had: to use the pipe operator to indicate the steam and rewrite the function flow. So for example:
The piped lines would both be interpreted as:
As you can see, the piped versions are much more readable :-D. Additionally using the pipe means that we're not restricted to two parameters; if we take something more general:
This is mapped in reverse or 'inside-out' with the left hand pipe-in being appended as the last argument to the next function call. So this would end up something like:
Obviously we'd need to find an alternative for the current bitwise or
|
, but it could be quite powerful and help make much more readable code, as it flows it a way that's much more brain friendlyThoughts ?
The text was updated successfully, but these errors were encountered: