Skip to content

Shortcuts for applying and for currying #251

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

Closed
matehat opened this issue Mar 11, 2010 · 29 comments
Closed

Shortcuts for applying and for currying #251

matehat opened this issue Mar 11, 2010 · 29 comments

Comments

@matehat
Copy link
Contributor

matehat commented Mar 11, 2010

Hi,

Since some hardcore scripting often requires the use of

somefunction.apply bound, [arguments]

Wouldn't it be nice if we could have it as:

&somefunction bound, [arguments]

The punctiation is really a suggestion for the moment. Ideas, anyone?

@grayrest
Copy link
Contributor

Splats will usually get you what you want: bound.somefunction arguments... though you may or may not want to use arguments because it invokes the special arguments array conversion. The exception is when somefunction isn't on bound and using this won't work.

@matehat
Copy link
Contributor Author

matehat commented Mar 11, 2010

What you suggested compiles to

bound.somefunction.apply(bound, [arguments])

Where I'm looking for

somefunction.apply(bound, [arguments])

Anyway, I just forked the repos and made the necessary changes along with test and everything works. If you guys wanna take a look ...

So I added the following unary operators

&somefunction bound, [arg1, arg2]
&&somefunction bound, arg1, arg2

Compiling to

somefunction.apply(bound, [arg1, arg2])
somefunction.call(bound, arg1, arg2)

@grayrest
Copy link
Contributor

yeah, I re-edited it after I realized the mistake.

@matehat
Copy link
Contributor Author

matehat commented Mar 12, 2010

So, I removed the && unary operator (uselessly confusing) and added another useful shorthand for a common idiom, currying. So the following :

b: somefunction <- (@, [1, 2])

Cleanly compiles to

var __curry = function(func, obj, args) {
  return function() {
    return func.apply(obj || {}, args.concat(__slice.call(arguments)));
  };
}, __slice = Array.prototype.slice, b;
b = __curry(somefunction, this, [1, 2]);

Which is shamelessly ripped from underscore bind() utility function. Proper tests were added and they pass.

@weepy
Copy link

weepy commented Mar 12, 2010

+1 for currying support. not so sure whether the apply is necessary ?

@matehat
Copy link
Contributor Author

matehat commented Mar 12, 2010

Just made it a bit more useful. Now the following lines are equivalent :

F: foo <- (@, "Hello", "World!")
F: foo <- (@, ["Hello", "world"])

F: foo <- (@)
F: foo <- (@, [])

Not much, but it makes things a bit cleaner.

@matehat
Copy link
Contributor Author

matehat commented Mar 12, 2010

Alright, I've splitted the two extensions into two completely seperate branches (curry and apply), each applicable to latest trunk from Jeremy.

The main difference is that the currying operator <- returns a new callable function, permanently bound, where the second, &, calls the bound function. Each have exactly the same argument structure (a bound object and an array of arguments).

I thought the function <- (bound, [args]) construct was nice, since it reminds us of the (args) -> function body construct for building functions, where the arrows points towars the function body, still true with the new addition.

Now, I think the &function construct can be useful, especially when manipulating lots of generic callbacks with arbitrary number of arguments, when a bunch of function.apply() can get less readable. Also, &function barely compiles to function.apply so it can be used without calling in situ, if one can find it useful

@matehat
Copy link
Contributor Author

matehat commented Mar 14, 2010

I was recently trying to implement a construct closer to normal function definition, to make it a bit more intuitive. Something that would set the new function binding clearly apart from the bound arguments. Something like :

somefunction <- newbinding , (arg1 arg2)
somefunction <- (arg1, arg2)
somefunction <- (arg1, arg2...)
somefunction <- newbinding

So rebinding to this would simply imply :

newfunction: somefunction <-@

Or even better, <= could be used to implicitly rebind to this

somefunction <= (arg1, arg2)
newfunction: somefunction <=

but can't seem to get it to work. I know, <= clashes with the usual comparison operator. It's a lot harder than I thought. Would be really nice though...

@StanAngeloff
Copy link
Contributor

I think it would be great if you implement these additions to Coffee as extensions! I don't think we need a new language construct in the core. It may just be me, but if you can do it in one-or-two lines of JavaScript (think .bind), we don't need a hundred-and-more lines of core code added?

@matehat
Copy link
Contributor Author

matehat commented Mar 14, 2010

Many core language features can be implemented in a few lines a code, and currying/applying is really a core features in some functional-oriented languages.

Also, I tried using these constructs in real-world hacking and it does an incredible job at improving the readability in applicable situations.

I will take a look at the extensions, though. It sure should be nice if I can implement them as such.

@weepy
Copy link

weepy commented Mar 14, 2010

isn't there a potential inconsistency with your different currying formats ?

F: foo <- (@, "Hello", "World!")
F: foo <- (@, ["Hello", "world"])
x: [a,b]
F: foo <- (@, x)  # what happens in this case?

Might be best to only have one format to avoid confusion.

@matehat
Copy link
Contributor Author

matehat commented Mar 14, 2010

Well, no. At compile time, the parser doesn't know if the arguments are arrays or not. For the moment, it is compiled as such :

  1. If one argument is passed, it is the bound object
  2. If two arguments are passed, the second is assumed to be an array
  3. If more than two are passed, all except the first one are wrapped into an array.

That's also the reason why I was recently working on another form, the last one I invoked, which is also more consistent.

@weepy
Copy link

weepy commented Mar 14, 2010

ah yes you're right.
regarding the newer form, can u explain it a little bit more - I don't quite understand it.

@matehat
Copy link
Contributor Author

matehat commented Mar 14, 2010

In the new form, everything in the parentheses are arguments (with the familiar splats for arbitrary number of arguments) and the function's new bound object is right before it, apart with a colon (could really be anything). This makes the construct closer to function definition (no arguments in arrays) and allows simple forms for the simpler cases :

function <- @
function <- (args...)

Where, in the first form, only function rebinding occurs. In the second, only arguments currying.

@weepy
Copy link

weepy commented Mar 14, 2010

and how might you do both ?

@matehat
Copy link
Contributor Author

matehat commented Mar 14, 2010

Like that :

function <- @ : (args...)

I'm trying to implement those as an extension, mentionned in #243. Seems promising.

@weepy
Copy link

weepy commented Mar 14, 2010

sorry, but I don't like that very much as the : looks like an assignment.

@matehat
Copy link
Contributor Author

matehat commented Mar 14, 2010

You know that's just arbitrary, it could really be a comma or anything. Nothing's settle for now.

@weepy
Copy link

weepy commented Mar 14, 2010

ah yes a comma would work ok. regarding the argument currying, will you support currying of the arguments to a function from first to last ? I.e.

f: (x,y,z) -> x*y*z
g: f <- (1,2)
ok g(a) == f(1,2,a) # true

@matehat
Copy link
Contributor Author

matehat commented Mar 14, 2010

absolutely, that's exactly the way it'd works!

@jashkenas
Copy link
Owner

matehat: You mentioned that you've found this new syntax to be useful in increasing readability in real-world situations. Mind sharing some examples of how you're currently using it?

@matehat
Copy link
Contributor Author

matehat commented Mar 16, 2010

For instance,

class ResponderPool
    constructor: (elements) ->
        ...
        @push:  Array::push  <- @
        @slice: Array::slice <- @
        @upto:  Array::slice <- @, (0)

        @each:   _.forEach <- (@) // using Underscore
        @select: _.select  <- (@) 

   subset: (other) ->
       @select (responder) -> responder.test(other)
   ...

Again, stuff in parentheses are arguments and the object before is the bound context and both can be used alone. This syntax isn't pushed to my repos yet, still has some stuff to tweak.

@weepy
Copy link

weepy commented Mar 16, 2010

syntax suggestions

Array::slice <- @, (0)
Array::slice & @, (0)
Array::slice << @, (0)
Array::slice ~ @, (0)
Array::slice <-> @, (0)

I quite like the ~ or <<

@matehat
Copy link
Contributor Author

matehat commented Mar 17, 2010

Ah, finally. Here, in this branch:

http://github.com/matehat/coffee-script/tree/curry

I made the proposed syntax work properly. BTW, extensions could not be used here since it involved plugin a little deeper into the lexing process.

weepy: I really don't mind. Any of those can do and could easily be swapped in place with what I've done.

@jashkenas
Copy link
Owner

Hey matehat -- this is probably fodder for a different ticket, but looking at your branch, your patch is a nice clean language addition that touches the lexer, the grammar, and the syntax tree. Do you have any thoughts about the way the compiler is structured for this? Was it easy to extend? Did you run into any counterintuitive bits or get stuck on anything while working on your patch?

@matehat
Copy link
Contributor Author

matehat commented Mar 17, 2010

Well, it wasn't counterintuitive once I convinced myself to go through and understand all the files implied in the compiler. My first patch, a few days ago, was only messing with the grammar and some custom nodes, because I had a limited understanding of the compiler.

I later found myself not being to do most of things I tried, so I read. This is really good stuff, things are pretty straightforward once you read through the whole code. Some comments though:

  • yytext bit in the grammar is really confusing
  • the names of @tag and @value are not expressive of the fact that they look in the previous token, and @token isn't neither of the fact that it pushes a token. That makes some code difficult to read when one is not comfortable with them.

Oh, and that might have helped : I have, in the past, written a compiler for a custom template language and most of the steps here were present then.

@matehat
Copy link
Contributor Author

matehat commented Mar 17, 2010

For the record, my recent addition for arbitrary number of splats in a function call was applied in my curry branch, so one can now do :

somefunction <- @, (1, numbers..., rest...)

@weepy
Copy link

weepy commented Mar 17, 2010

so how do you determine which splat group an argument will fall ?

durh: ignore me ....

@jashkenas
Copy link
Owner

The bind operator is now on master. You can see the test case in test_bind.coffee, but the gist of it is that <- is now identical to Prototype's Function#bind, Underscore's bind, and ECMAScript 5's bind.

Closing the ticket.

This issue was closed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

5 participants