-
Notifications
You must be signed in to change notification settings - Fork 107
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
(Extended proposal?) Ghost Methods #13
Comments
Creating an ad-hoc function that sends a method to its argument is a very common need. In the allong.es library, for example, there is a const send = allong.es.send;
fetchPlayers()
.then( games => Promise.all(games) )
|> catchError( ProcessError, err => [] )
|> send('then', send('forEach', x => console.log(x)) A first-class syntactic alternative to using a library function is obviously "sweet." This is a special case of partial application. It's kind of the inverse of JavaScript's One thing that is going to come up when exploring your syntactic sugar (beyond the question of whether this is compatible with JavaScript's existing parsing rules) is the question of why have a special rule just for the receiver of a method? If this is just partial application, why can't we leave whatever we want 'blank' to be filled in later? In some languages, there is a "hole" character that turns any expression into a template function. It's usually fetchPlayers()
.then( games => Promise.all(games) )
|> catchError( ProcessError, err => [] )
|> ⬛️.then(⬛️.forEach(console.log(⬛️))) This transforms trivially into: fetchPlayers()
.then( games => Promise.all(games) )
|> catchError( ProcessError, err => [] )
|> __hole1__ => __hole1__.then(__hole2__ => __hole2__.forEach(__hole3__ => console.log(__hole3__))) It's a little more flexible than a special syntax for the "Thrush," because it can handle a missing argument or arguments in any position. Underscore implements a variation of this feature for partial application: |
This is a really useful construct, and something I really miss since moving away from LiveScript, which has this (as |
|
Also, you can't call |
Heh, I was being brief for the sake of clarity. (Node's |
Well, we don't really need a new syntax for this right?
So the first example would become
Or am I totally missing the point here? |
Smaller 'syntax' would be
|
Absolutely, that's more-or-less exactly what |
@raganwald Ahh dang, should have read your comment more thoroughly :) |
Yes, but it's far more elegant in ECMAScript 2015. Win-win! |
FTR, this is how bind operator and arrows kinda solve this. fetchPlayers()
::Promise.prototype.then( games => Promise.all(games) )
::Promise.prototype.catch( ProcessError, err => [] )
::Promise.prototype.then( players =>
players::Array.prototype.forEach( x => console.log( x ) )
) @raganwald it's worth pointing out that arrows & the spread operator allow you to be much more export default (methodName, ...args) =>
(receiver, ...remainingArgs) =>
receiver[ methodName ]( ...args, ...remainingArgs) But really, arrows and spread make a lot of the low-level utilities defined in allong.es redundant. Javascript Allongé was an illuminating read — easily my favourite programming book — but using allong.es as a library didn't sit right for me: I always wanted to invoke the useful patterns inline rather than referencing the pre-defined functions. ES6 makes it incredibly easy to do that. |
I rewrote JavaScript Allongé for ES6, and were I to write |
Something like this was implemented as a sweet.js macro in lambda-chop, using
|
Update: in the new, ES6-compatible version 1.0 of SweetJS, such a lambda macro could be implemented as follows:
... I'm sorry to say One benefit of this macro is you no longer need to think about ensuring you keep using differently named variables so as to prevent name clashes; the macro hygiene takes care of this for you. Edit: in this new version of Sweet, the pipeline operator itself cannot currently be implemented yet. This would require infix operators, which it seems are on the current todo-list. |
const λ = new Proxy({}, {
get: function(r, prop){
return function(...myArgs){
return function (object) {
return object[prop].apply(object, myArgs)
}
}
}
})
λ.map(_ => _ + 1)([1,2,3]) This works for |
How about a simple helper to just extract all prototype methods for an object. // this could probably be re-written
const thisify = (prototype) =>
Object.keys(prototype)
.filter(key => typeof prototype[key] === 'function')
.map(key => ([key]: (...args) => (ctx) => prototype[key].apply(ctx, args)))
.reduce((result, current) => Object.assign(result, current), {});
/*
fetchPlayers()
.then( games => Promise.all(games) )
|> catchError( ProcessError, err => [] )
|> .then( .forEach(g => console.log(g)) )
*/
Promise._ = thisify(Promise.prototype);
Array._ = thisify(Array.prototype);
fetchPlayers()
.then( games => Promise.all(games) )
|> Promise._.catch( err => [] )
|> Promise._.then( Array._.forEach(g => console.log(g)) ) |
You wouldn't want to mutate the globals, though; even in example code. |
Would it be OK to leave this for a follow-on proposal, or is it important to address this use case in this proposal? |
We should hold off and see what happens with the partial application proposal first. |
Using the partial expression proposal, the |
So I got this idea while fixing the promises example in response to #10. In short, here's a truncated example of the code I was editing:
I noticed the bottom boilerplate functions had something in common – they both received a parameter, just to call that parameter on a future object. Then I thought, what if you could do this concisely?
In other words,
.then(x)
is equivalent toobj => obj.then(x)
.Is this crazy? Useful? Grammatically possible?
Just had to throw it out there.
The text was updated successfully, but these errors were encountered: