Closed askucher closed 7 years ago
You could pick a different way of coding to achieve it more naturally, without adding strange syntax. Use Promises and/or async
/ await
.
Something like this should work (untested):
getFrontendData = (data) ->>
totalInUsd = await contract-api.getPresaleTotalInUsd!
totalEth = await contract-api.getPresaleBalanceInEth!
totalSales = await contract-api.getTokenTotalSales!
return 47
(->>
try
ret = await getFrontendData data
do-stuff-with ret
catch err
do-stuff-with err
)()
[edit: changed case-style to reflect OP's]
What's up with the negative reactions? XD
The OP obviously wants to handle any error from any of the executed statements, called in sequence, at the call site rather than individually. This example does that, and as a bonus, any stack traces will be natural, unlike when using the callback-style.
The example of course presumes that the contract-api is Promise.promisify'd (or re-written as async manually).
mostly just use a bit of sugar...
protect = (err-cb, fn, cb) ->
fn (err, result) ->
err-cb err if err?
cb result
export get-frontend-data = (data, cb) ->
presale <-! protect cb, contract-api.get-presale
total <-! protect cb, contract-api.get-total
cb null, total + presale
What about trying /caolan/async?
require! async
getFrontendData = (data, cb) -> # data unused?
steps =
total: contract-api.getPresaleTotalInUsd
balance: contract-api.getPresaleBalanceInEth
sales: contract-api.getTokenTotalSales
async.auto steps, cb
getFrontendData {}, (err, res) ->
if err? then return console.error 'error getting front end data', err
{total, balance, sales} = res
Now that async/await has landed in master, I think that encouraging use of those is much, much better than inventing another syntax quirk that only people staying on the bleeding edge of LS can read, especially if that syntax quirk looks like other unrelated language features (anywhere else, cb!
would mean invoke cb
unconditionally with no arguments). @askucher, do you have problems with Promises?
@rhendric Guys, promise requires to create a memory slot. I am working in blockchain development where we cannot use memory in order to make code look better because it costs GAS. But we still need to handle each error specifically and do not allow process go on.
You have issues in your examples:
+
operator it produces new error.await
feature where it can be omitted. In our case the syntax sugar is a benefit rather than problem.
@askucher, this feature doesn't fit with existing LiveScript functionality. As written, it's a terrible syntax—overloading !
to mean something completely unrelated to ‘return nothing’ or ‘invoke with nothing’ would be a head-scratcher for anyone hearing about this feature who isn't on your team, and the trailing err
is baffling to me—what use is that? Is that a keyword? Is it a variable that you're binding even though you can't use it? My most generous interpretation honestly is that you left it in by accident; even then, making cb! <- foo
mean ‘invoke foo
with a function that tests its first argument and, if truthy, passes it to cb
and returns early’ is far too much extra information content to imbue to one extra character that, again, usually means something completely different.
I recognize that you have a need here that LiveScript isn't addressing; you want to reduce some boilerplate code and eliminate a common source of errors, and you don't want performance to be impacted. If LiveScript had macros, this would be an ideal use for them. Unfortunately, it doesn't, and in this iteration, likely never will (at least before JavaScript gets them). And in the absence of that, when making language design decisions, we can't simply come up with a new, unique shorthand for every possible repeated pattern of code. We have to choose the patterns that are common and cause a lot of pain, yes; but just as importantly, we have to choose shorthands that work well together, that can be composed with each other to deliver far more power than they would separately. LiveScript doesn't have the building blocks for marking the parameter of a function as something that causes early return, or as something that gets passed to another function, or as something that does either of the previous if truthy. If it had two of those three building blocks, I could see a compelling argument for adding the third so that you'd have a good shorthand for your use case. Designing and adding all three is a daunting proposal for a language that gets as little maintenance as LiveScript is likely to for the foreseeable future.
Furthermore, if LiveScript is going to be evolving in any direction, it should evolve with JavaScript, not away from it; and the JavaScript ecosystem is in the process of embracing Promises. In time, Promises will be made more efficient by V8 and other JavaScript engines; they may not be efficient enough now, but it's not forward-looking to assume that they'll never be and instead double down on legacy language features that attempt to solve the same problem with less powerful primitives. So I can't endorse enhancing the backcall operator to something like <-?
to support your use case either; it's chasing the wrong target.
I know this isn't what you and your team want to hear. I'm sorry to disappoint. You've received several quite reasonable compromise suggestions here that could improve your situation. I recommend that you take one—vendethiel's has an easily correctable bug in it, but is otherwise probably the best tradeoff between succinctness and performance. Or, you can try writing a transformer that adapts either await syntax or backcalls to the functionality you want—@bartosz-m has published some interesting hacks in this vein here, or you can always apply the transform in JavaScript using Babel or jscodeshift or the like.
I noticed that it is too often we need to write
I think we need special syntax for
return if
I can pay for this feature
50$