maxtaco / coffee-script

IcedCoffeeScript
http://maxtaco.github.com/coffee-script
MIT License
727 stars 58 forks source link

`return await foo defer bar` possible? #145

Open vjpr opened 9 years ago

vjpr commented 9 years ago

I am working with streams using the highland.js library, and I want to use ICS to flatten the christmas tree code shape when using flatMap.

I need to return a stream inside each function.

Here is an example:-

  directories = readdirp root: root, depth: 0, entryType: 'directories'
  directories = h(directories).flatMap (entry) ->
    {fullPath} = entry
    findReadme(fullPath).flatMap (file) ->
      file = h fs.createReadStream file.fullPath, 'utf8'
      comment = file.split().take(1)
      await comment.flatMap defer firstLine
      if !_(firstLine).contains("#")
        h [{entry, firstLine}]
      else
        h []

So I would need to do something like the following, where return await would return the return the value of the function (which would only make sense for sync functions i.e. streams/promises).

  return await h(directories).flatMap defer entry
  {fullPath} = entry
  return await findReadme(fullPath).flatMap defer file
  h [file]
  ...
doublerebel commented 9 years ago

I ran up against a similar issue today: A function containing an await block can no longer return a value. For instance, saving the handle to an i/o request in order to abort it. A simplified example:

Regular Coffee

headersHelper = (cb) ->
  options = {url: "https://google.com"}
  return http.request options, (err, res) ->
    cb res.headers

headers = []
addHeaders = (h) -> headers.push h
handles = (headersHelper addHeaders for i in times)

... elsewhere ...
user.on "search.change", -> handle.abort() for handle in handles

Iced

headersHelper = (cb) ->
  options = {url: "https://google.com"}
  await http.request options, defer err, res
  cb res.headers

Could be...?

headersHelper = (cb) ->
  options = {url: "https://google.com"}
  return await http.request options, defer err, res
  cb res.headers

Of course this would only work at the first await block, it's a bit awkward. However, not returning a value at all goes against Coffee's philosophy of every function being an expression.

I understand the reasoning behind "Awaits No Longer Work As Expressions", where previously the async result was returned from the await block. Here we are simply looking for synchronous return from the expression directly following await.

doublerebel commented 9 years ago

Looking deeper at @vjpr's suggestion to flatten a chain of expressions by returning at each await block, seems like if anything should be default behavior. If we consider the regular coffee version of his proposition, the default would be to return the function result unless explicitly designed otherwise:

directories = h(directories).flatMap (entry) ->
    {fullPath} = entry
    findReadme(fullPath).flatMap (file) -> ## IMPLICIT RETURN ##
      file = h fs.createReadStream file.fullPath, 'utf8'
      comment = file.split().take(1)
      comment.flatMap, (firstLine) -> ## IMPLICIT RETURN ##
        if !_(firstLine).contains("#")
          h [{entry, firstLine}]
        else
          h []

So the question in my mind is now, must it be possible to avoid returning a value from await? There could be a vawait ("void await") that returns void (i.e. current behavior).