Skip to content
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

Please add supports to "async await" keywords #683

Closed
xiaorong61 opened this issue Mar 11, 2015 · 40 comments
Closed

Please add supports to "async await" keywords #683

xiaorong61 opened this issue Mar 11, 2015 · 40 comments
Labels

Comments

@xiaorong61
Copy link

Examples:

es7 async await keywords:(I can use babeljs to compile es7 es6 to es5)

do async -> console.log (await read \./file)

should compile to:

(async function(){
  return console.log(await read('./file'));
})()

not:

(async(function(){
  return console.log(await(read('./file')));
})()
@xiaorong61 xiaorong61 changed the title Please add supports to "JSX" and "async await keywords"! Please add supports to "JSX" and "async await keywords" Mar 11, 2015
@askucher
Copy link

Long time ago I opened question about async callbacks. So I vote - Yes we need it. But I have concerns about async await syntax. I think we should use promises

@dead-claudia
Copy link
Contributor

I disagree about JSX, since it's just as easy to use the traditional LiveScript syntax, anyways. And it's quite nice.

# Taken from an example from the second link
{table, thead, tr, th, tbody} = React.DOM
product-table = React.create-class do
  render: ->
    last-category = null
    table null,
      thead null,
        tr null,
          th null, 'Name'
          th null, 'Price'
      tbody null,
        @props.products |> map ->
          if it.category isnt last-category
            product-category-row do
              category: it.category
              key: it.category
          else
            last-category = it.category
            product-row do
              product: it
              key: it.name

@blvz
Copy link
Contributor

blvz commented Mar 17, 2015

The JSX compiler should allow one to write code in LiveScript (or CoffeeScript etc.) instead of JavaScript, not the other way around.

And it's better to put different requests in different issues.

@xiaorong61 xiaorong61 changed the title Please add supports to "JSX" and "async await keywords" Please add supports to "async await keywords" Mar 17, 2015
@xiaorong61 xiaorong61 changed the title Please add supports to "async await keywords" Please add supports to "async await" keywords Mar 17, 2015
@dead-claudia
Copy link
Contributor

My point was that plain LiveScript doesn't necessarily need such syntax for
React, and thus I don't see why it's needed.

Also, keep in mind that CoffeeScript's JSX compiler is a completely
separate project from the main compiler, and compiles to vanilla
CoffeeScript, if I recall correctly.
On Mar 17, 2015 8:35 AM, "Rafael Belvederese" [email protected]
wrote:

The JSX compiler should allow one to write code in LiveScript (or
CoffeeScript etc.) instead of JavaScript, not the other way around.

And it's better to put different requests in different issues.


Reply to this email directly or view it on GitHub
#683 (comment).

@blvz
Copy link
Contributor

blvz commented Mar 17, 2015

@IMPinball My answer was intended for the OP. Sorry for not making that clear. I agree with you.

@dead-claudia
Copy link
Contributor

@blvz No problem.

@gkz gkz added the feature label Apr 16, 2015
@gkz
Copy link
Owner

gkz commented Apr 18, 2015

What syntax do you propose when the function has parameters?

@yazla
Copy link

yazla commented Jun 22, 2015

maybe let it simply be closer to the es7 syntax(make 'async, await' a reserved words):

fn = async (arg1, arg2) ->
    do_whatever()
    result = await call_server(arg1, arg2)
    return result

what do you think?

@phanimahesh
Copy link

👍

@dead-claudia
Copy link
Contributor

Keep in mind making it a non-contextual reserved keyword like that mightwill break a lot of code for people using caolan/async. That's something worth mentioning. If there's a way to make it a little more contextual, or even an alternate syntax (that is probably best avoided)

@yazla
Copy link

yazla commented Jun 24, 2015

@IMPinball , you are right, "async" should be only treated as a keyword only if it is located before the function declaration. It will probably solve this problem.

@dead-claudia
Copy link
Contributor

Maybe treat it initially similar to yield, but throw if it doesn't validate
as a function. As for await, the syntax is identical to yield, anyways, and
can return similar results.

Although I do caution in that the whole thing is still yet to be
solidified. And especially for other ES7 features, the semantics for most
of them are very much a work in progress. (Decorators, one of the more
hyped syntax proposals, is a prime example of this: the currently specced
syntax itself is ambiguous when applied to computed class methods.)

Thankfully, async-await appears to be stabilizing, but I don't think it'll
advance to stage 2 anytime soon, because engines are already rushing to
implement the new standard. Only recently have the main implementations
reached the halfway point. Half of the changes themselves are in syntax. A
third of the existing built-ins had new semantics added. Half of the new
standard library didn't exist in the last standard.

Just a few notes worth mentioning. (I'm okay with this specific case.)

On Wed, Jun 24, 2015, 03:33 Yuriy Yazlovytskyy [email protected]
wrote:

@IMPinball https://github.com/impinball , you are right, "async" should
be only treated as a keyword only if it is located before the function
declaration. It will probably solve this problem.


Reply to this email directly or view it on GitHub
#683 (comment).

@ghost
Copy link

ghost commented Jul 4, 2015

I'm looking forward this feature!
👍

@kkirby
Copy link

kkirby commented Jul 10, 2015

I have a branch up that implemented this feature. I was unable to incorporate a method to determine if the async keyword was an identifier or not. The branch can be seen here: https://github.com/kkirby/LiveScript/tree/async-await

@cades
Copy link

cades commented Oct 5, 2015

+1

@garyyeap
Copy link

garyyeap commented Oct 5, 2015

+100

@dead-claudia
Copy link
Contributor

At least caolan/async doesn't have a default export, so the idea of prefixing wouldn't be impractical.

But, I have a question: in a language with backcalls, function composition, and concise function syntax, why couldn't we do something like this? JavaScript needs async/await, but I'm not sure LiveScript needs it. I just thought of this, and it's likely faster than nearly every Promise implementation. The await function always assumes the first argument takes an error-first callback, and the rest are directly applied to it. Adapting DOM callbacks to be error-first is relatively straightforward. I've made it as a requireable utility function module (+ JS version if you're concerned about the speed) in this gist under the ISC license. The first comment is a little bit of documentation on how to use.

/**
 * It does return the return value of the function, but forces async calling of
 * the callback and async handling of errors after the first async call.
 * 
 * f = async (await, ...args, next) !->
 *   if args.length == 1 => throw new Error 'bad!' # throw sync error
 *   v <-! await fs.readFile \foo, \utf8 # do an async call
 *   if isBad v => throw new Error 'bad!' # throw async error
 *   next v # return the value
 *
 * f (err, data) ->
 *   process.exit 1 if err?
 *   console.log data
 * 
 * You can even make methods out of this. You just have to remember
 * to bind each backcall.
 * 
 * class Reader
 *   (@file) ->
 *   read: async (await, ...args, next) !->
 *     v <~! await fs.readFile @file, \utf8 # do an async call
 *     next v # return the value
 */

# Example utility combinator
async-reduce = curry async (await, f, start, xs, next) !->
  unless next?
    [next, xs] = [start, xs]
    [start, ...xs] = xs
  switch xs.length
    # Fast path simple cases
    | 0 => throw new TypeError 'Reduce of empty array with no initial value'
    | 1 => await f, start, next
    # The meat
    | _ =>
      i = 0
      await f, start, xs[0], ret = !->
        | i++ == gs.length => next null, res
        | otherwise        => ret |> await f, res, xs[i]

# `readdir` example
zip-index = -> [[x, i] for x, i in it]
cons = (a, b) --> [a, b]

readdir = async (await, dir, next) !->
  next |> await fs.readdir, dir,
    (map -> path.join dir, it)
    >> (cons next)
    >> apply async-reduce do
      (acc, file, next) !-> await fs.stat, file, (stat) !->
        | stat.isDirectory! => await readdir, file, next << (++)
        | otherwise         => next acc ++ [file]
      []

listing <-! readdir path.resolve 'path/to/dir'
console.log listing

# CSON config example
read-config = async (await, file, next) !->
  contents <-! await fs.readFile file, \utf8
  config = CSON.parse contents
  config.ignore = config.ignores # alias
  next config

config <-! read-config \file.cson

And yes, with that, I just made the error-first convention effectively a monadic Future/Either. I don't think LiveScript actually needs this feature. Since I figured this out, -1 for me on the whole idea, as this basically negates the whole point. I also felt it was 100% worth sharing. It's a lot like Haskell's do monad syntax.

Also works well with LiveScript's function composition operators, as I used in the readdir example. It looks like a whole different language.

@dead-claudia
Copy link
Contributor

If you would like, I can see about publishing an npm module for it (using the JS version).

@igl
Copy link
Contributor

igl commented May 24, 2016

+1000. I bet async funcs will land in v8 before TCO does

https://chromium.googlesource.com/v8/v8.git/+/d08c0304c5779223d6c468373af4815ec3ccdb84

@vendethiel
Copy link
Contributor

We'd need to bikeshed on the syntax a bit. I'm not against it at all, but I want te ES6 version (sorry @kkirby)

@igl
Copy link
Contributor

igl commented May 24, 2016

async >> ->> +> ><((((°>
await << <<- <+ <^((((><

@kkirby
Copy link

kkirby commented May 24, 2016

@vendethiel what do you mean by "The ES6 version"?

@vendethiel
Copy link
Contributor

We should compile to literal async/await syntax, not generate promise code ourselves

@dead-claudia
Copy link
Contributor

@vendethiel Plus, it's already been shipping in Edge, and V8 has also implemented it IIRC (although it's not shipping yet). I don't see why it would need compiled, and if it does for some reason, there's Babel among others that can do it.

@dead-claudia
Copy link
Contributor

@igl

👎 for >> and << to represent async functions. I use those for composition, and prefer them over the f . g syntax.

Also, 👎 for +> and <+, since they're so awkward to type... I also am leary about precedence with a<+x: currently it's a < +x, but it's completely ambiguous if we go with that. I really don't like ill-defined and unclear precedence.

@igl
Copy link
Contributor

igl commented May 26, 2016

ok fish it is then.

@dead-claudia
Copy link
Contributor

Here's my ideas:

# normal definition
readConfig = (config) >->
  validate <!> readFile config

# back call
do ->
  doSomething!
  file <-< iterate files
  validate <!> readFile file

# bound definition
class Reader
  readAll: ->
    Promise.all <| @files |> map (file) >~>
      @validate <!> readFile file

# curried definition
readFrom = (dir, file) >-->
  validate <!> readFile path.resolve dir

Annotated JS translation:

// # normal definition
// readConfig = (config) >->
//   validate <!> readFile config
let readConfig = async function (config) {
  return validate(await readFile(config));
};

// # back call
// do ->
//   doSomething!
//   file <-< iterate files
//   validate <!> readFile file
;(function () {
  doSomething();
  iterate(files, async function (file) {
    return validate(await readFile(config));
  });
})();

// # bound definition
// class Reader
//   readAll: ->
//     Promise.all <| @files |> map (file) >~>
//       @validate <!> readFile file
class Reader {
  readAll() {
    return Promise.all(map(file => {
      return this.validate(await readFile(config));
    })(this.files));
  }
}

// # curried definition
// readFrom = (dir, file) >-->
//   validate <!> readFile path.resolve dir
let readFrom = curry$(async function (config) {
  return validate(await readFile(config));
});

@vendethiel
Copy link
Contributor

<!> really sounds like a binary op, and it sounds like it's related to haskell's <*>/<$>.

@igl
Copy link
Contributor

igl commented May 26, 2016

I vote for the async/await keywords. There are too much arrows already.
There are enough breaking changes on the radar and features waiting to be removed.

I don't even use certain features of LS which only seem to exist to bring terse readable code into the terse-as-fuck-fizz-buzz-code-golfing-world-cup league.

@dead-claudia
Copy link
Contributor

@vendethiel I thought about that already... I couldn't think of anything else right off, but I'm open to alternative ideas.

@kkirby
Copy link

kkirby commented May 26, 2016

@vendethiel Oh, then I don't understand the context of your apology. If I recall correctly, my branch only adds async/await keywords to the resulting code. In my promise generator branch, I do actually generate the code on the LS side, but my async/await branch does not. Regardless, no apology needed!

@dead-claudia
Copy link
Contributor

@igl

I don't even use certain features of LS which only seem to exist to bring terse readable code into the terse-as-fuck-fizz-buzz-code-golfing-world-cup league.

Like what?

If you're referring to things like implicit switches from functions, that's idiomatic among a large number of users. If you're referring to partial application, partially applied operators, etc., those are also common. I've also used other similar things as well. They're useful in smaller projects and utilities, where the simple is even simpler, but I wouldn't recommend some of them everywhere, as it doesn't scale well, just like how point-free style eventually becomes pointless after a while.

# This is one of my favorite functions when using vdom libraries.
# I can just use indentation for vdom work.
a = -> [.. for &]

m '.god', a do
  m '.parent', a do
    m '.child'
    m '.child'
    m '.child'
  m '.parent', a do
    m '.child'
    m '.child'
    m '.child'

# Without this function, you'd have to do this:
m '.god', [
  m '.parent', [
    m '.child'
    m '.child'
    m '.child'
  ]
  m '.parent', [
    m '.child'
    m '.child'
    m '.child'
  ]
]

# Let's make our Gulp tasks a bulleted list
run = -> [.. for &].reduce (a, b) -> a.pipe b

require! {
  gulp
  'gulp-livescript': livescript
  'gulp-uglify': uglify
}

gulp.task 'compile', ->
  run do
    * gulp.src 'src/**/*.ls'
    * livescript bare: true
    * uglify do
        compress:
          global_defs: {-DEBUG}
    * gulp.dest 'dist'

I will also note that there is a place for highly terse, borderline-obscure languages that look halfway to line noise. There are three general-purpose languages, APL, J, and K, which are general-purpose languages that are stupidly fast at processing arrays and large amounts of data, and the extreme terseness makes them extraordinarily powerful, albeit with a very high barrier of entry (J and K are descendants of APL). Regular expressions are also a very terse, powerful DSL, but it's easy to shoot yourself in the foot if you're not careful.

@WreckedAvent
Copy link

Have to agree with @vendethiel, <!> and >-> look too much like haskell sigils. In particular, >-> looks like >=> to me, which could be understood as a compositional form of >>= (bind).

Would strongly prefer we just use async and await. I don't think this is an area where it's very productive to find more sigils to use.

@vendethiel
Copy link
Contributor

yeah, >~> also looks like Kleisli composition.

I'm in general against adding more symbol stuff.

@dead-claudia
Copy link
Contributor

I'm starting to fall more in favor of new keywords for this, too. It's harder to come up with something symbolic without either looking obscure and/or appearing to clash with other symbols.

@gkz
Copy link
Owner

gkz commented Jun 1, 2016

Yeah, we have enough crazy symbols I think

@dk00 dk00 mentioned this issue Dec 28, 2016
@summivox
Copy link
Contributor

summivox commented Feb 1, 2017

I'm surprised no one brought up #836 .

@dead-claudia
Copy link
Contributor

dead-claudia commented Feb 1, 2017 via email

@willin
Copy link

willin commented Aug 29, 2017

async/await is in the standards now

@rhendric
Copy link
Collaborator

rhendric commented Sep 30, 2017

#978 is merged!

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

No branches or pull requests