Hacker Newsnew | comments | show | ask | jobs | submitlogin
Kal – a clean JavaScript alternative without callbacks (rzimmerman.github.io)
249 points by rzimmerman 590 days ago | 125 comments



It's remarkable how similar this code looks to the monadic way of doing it.

For this Kal code...

    task getUserFriends(userName)
      wait for user from db.users.findOne {name:userName}
      wait for friends from db.friends.find {userId:user.id}
      if user.type is 'power user'
        for parallel friend in friends
          wait for friendsOfFriend from db.friends.find friend
          for newFriend in friendsOfFriend
            friends.push newFriend unless newFriend in friends
      return friends
Here's a pseudo-ish implementation in pseudo-ish Haskell.

  getUserFriends userName = do
    user <- findUser userName
    friends <- findFriends user
    if (User.type user) == "power user"
      then friends ++ parMap rdeepseq $ (getSecondNodes friends) friends
      else friends
		
  getSecondNodes firstNodes friend = do
    secondNodes <- findFriends friend
    diff firstNodes secondNodes


I like the concept of Kal, and I look forward to seeing what people do with it. Regarding syntax, I have to admit that I share the opinions of a few others in terms of preferring symbols over so many keywords, but that's a minor nitpick. Great job!

-----


Thanks! I had not thought to make the comparison to Haskell and monads. That's pretty interesting.

A lot of people prefer symbols over keywords, and I think that's mainly a readability issue. I personally prefer more keywords with good syntax highlighting, so that's what I went with (and why I almost immediately made a .tmbundle).

-----


Coming from a Python background I appreciate the verbosity. There are times when symbols and being concise may be necessary, but I find a non-expert human readable default to be very appealing.

Thanks for your hard work!

-----


If you are looking for a syntax closer to Haskell's, you can take a look at LiveScript (http://livescript.net/)

eg.

    getUserFriends = (userName) ->
      user <- findUser userName
      friends <- findFriends user
      ...
Specifically, the section on "backcalls": http://livescript.net/#backcalls

-----


If you are looking for Haskell, you can take a look at Fay (https://github.com/faylang/fay/wiki).

-----


What about Haste? https://github.com/valderman/haste-compiler

-----


Back calls look like array programming gone even crazier. Was live script inspired by APL?

-----


This is beautiful work! I see small shades of inspiration from the .Net TPL[1] and the more recent async-await paradigm[2] in C#. But, this is way more concise and beautiful implementation :-)

[1] - http://msdn.microsoft.com/en-us/library/vstudio/system.threa...

[2] - http://msdn.microsoft.com/en-us/library/vstudio/hh191443.asp... and http://msdn.microsoft.com/en-us/library/vstudio/hh156528.asp...

-----


Thanks! I was wondering if someone would make that comparison. I saw async-await a little after I started working on this and felt it was good confirmation that this was actually a reasonable idea.

-----


And, of course, the Haskell do-notation gets de-sugared into tons of call-backs by the compiler.

-----


This is a project I've been working on in my spare time for a while now and I've finally decided to throw it out there and get some feedback. Do you think the 'wait for' callback syntax is useful? What big features do you think are missing?

-----


The async handlers looks great, specially parallel and series iterators. I'm not sold on

    wait for friends from db.friends.find()
    //vs
    friends = await db.friends.find()
it's a bit hard to follow despite the natural language.

Is this a fork of the CoffeeScript compiler? Will it benefit from upstream changes/features?

-----


I also vote for 'await' here. The 'wait for friends from' construct looks too much like BASIC for my liking. It's not clear which terms are built into the language ('wait', 'for', 'from') and which are not ('friends'). Also, since the eventual result of that line is an assignment to 'friends', using the existing = operator makes the language more consistent.

-----


Thanks for the feedback. It seems like a lot of people would prefer a more normal looking assignment.

If you get a chance (and use TextMate or Sublime Text), please give syntax highlighting a try (https://github.com/rzimmerman/kal.tmbundle)!

-----


I'll stick up for the "wait for" syntax here. It seems a lot cleaner to me than trying to shoehorn asynchronous handling into assignment, with all the edge cases that entails.

-----


It's more than assignments looking normal. "await x" is better since it's an expression and not a statement. That is, "await x" will be a placeholder for a value (after the appropriate CPS, of course), while the assignment syntax forces you to use a temporary variable, even if you don't need it.

-----


Unfortunately I don't use those editors — maybe you can write a Vim highlighter next? Might also be worth adding syntax highlighting to the website (even if you have to do it manually for now), if you're set on the current syntax and think highlighting will make that much of a difference.

-----


Although I cannot agree more with you and csheid, you really should give sublimetext a try. I've been quite impressed.

-----


Have you tried converting it in: http://coloration.ku1ik.com

-----


Would it be hard to support both syntaxes?

-----


It's not a fork of CoffeeScript (though there are a lot of similarities). I'd initially tried doing that but I really couldn't get things to work the way I wanted. Mostly, it's a pretty major rearchitecture to get coffee to parse a function block that way.

I think syntax highlighting helps a lot in this case, so try out the tmbundle in Sublime or TextMate (https://github.com/rzimmerman/kal.tmbundle) if you can.

-----


Looks pretty good! I like it. To be honest I will not use it in my projects. I prefer using ES6 syntax for this problem when it's available.

-----


what's the (equivalent?) ES6 syntax? I'm genuinely curious.

-----


I'd be more interested in performance than features. If you use tasks when they're not needed, how much more JavaScript code does it take and how fast does it run? Also, what do stack traces look like?

-----


Are you aware of the ToffeeScript project? Can you make them a website and start promoting it? Because you are missing all if the features in ToffeeScript/CoffeeScripy and ToffesScript needs a shiny website because that's how people judge languages now is how shiny their website is apparently based on the fact that no one seems to care about ToffeeScript. BTW no I'm not the author of ToffeeScript.

-----


This is a fantastic spare time project. At the very least, it will be a beautiful feather in your cap :)

-----


(Don't mean to divert this thread from Kal, but this is on the topic of "callbacks are bad" etc.)

For some time now, I've been accumulating async patterns I've needed when writing JS code in my monadic IO.js library[1]. The aim of IO.js is to provide higher levels of thinking about sequences of actions without worrying about whether they are async, and with a rich set of error management tools (for ex, exceptions are turned into recoverable conditions in IO.js).

The crux of the "callback hell" problem is, contrary to what many have claimed, is not the nesting that results when the callbacks need to be called in temporal order. That much is straightforward to deal with. The "hell" rears its head when you need to coordinate multiple such sequences that are running concurrently. The composable actions you get from a monadic treatment combined with CSP-style channels, are an expressive framework to build abstractions on (tldr - Haskell's implementation is awesome!).

For illustration, the framework in IO.js is flexible enough to implement a node.js web server that can express PG's "Arc challenge" concisely (though that challenge is practically obsolete). Take a look at [2].

For a simpler example, `IO.trace` is a straight forward way to generate a trace dump to console of a sequence of asynchronous actions. You don't need to "enable trace" for an entire app. You can choose to trace only a particular sequence. This is pretty neat when debugging. Stack traces are useless when dealing with async processes anyway.

[1]: https://github.com/srikumarks/IO.js [2]: https://github.com/srikumarks/IO.js/blob/master/examples/arc...

-----


>The "hell" rears its head when you need to coordinate multiple such sequences that are running concurrently.

This situation is why I wrote DelayedOp: https://github.com/osuushi/DelayedOp . Of all the little reusable code bits I've written, this one has ended up in more of my projects than any other.

Basically, a DelayedOp is a lightweight manager that runs a callback after its "wait" and "ok" calls have balanced. It ends up being equivalent to _.after(), but it's more natural to use than manually counting your concurrent operations, and it has some conveniences for debugging, so that if a callback never fires, you can find out why.

-----


Excellent work. Especially useful since it appears to throw exceptions. That means it's a step up from Iced Coffeescript.

The syntax is a little verbose and might be shortened to something like C#: `user = await db.users.findOne {name:userName}`. Would be a lot more clear to me.

-----


As far as I can tell from reading the readme, it does NOT support throwing exceptions from within callbacks.

Because, of course, there's no way to technically do that in anything that is JavaScript at its core -- at least as far as I'm aware of. Correct me if I'm wrong?

It says "This includes error handling via callbacks." -- not error handling via exceptions. Elsewhere it says "Any errors reported by fs.readFile (returned via callback) will be thrown automatically.", so maybe it converts certain callback error functions to exceptions? But in any case, that's not the same as throwing an exception inside of a callback, and having it "bubble up". Unless I'm misunderstanding (which would be great!).

-----


You can do it using monadic augmentation. Or aspect oriented programming.

In plain english, make a function that takes two functions makeADoThingFunctionThatHandlesErrors(doThing, handleError)

and returns a new version of the function that is internally wrapped in a try catch block with the handleError function getting called from the "catch" block. This is relatively straightforward to accomplish using closures/high order functions. This is one utility function you write once.

Finally you can do this with a whole "monadic" api such as jquery- create a function that loops through all the methods on an object prototype and wraps them in an error handler, returns a new version of the prototype you can inherit from.

Or instead of doing all that, you just use some library with promises (such as Q, or jQuery) that implements all the above, and all you need to do is write a single error handler function, and any error that gets thrown by any function in a promise chain gets caught and sent to that function.

-----


if you do something like:

  wait for data from fs.readfile 'test.txt'
  print data.toString
and readFile calls back with an error (the first argument is non-null), the error will be thrown just before the print statement. So print will not execute and you'll get a stack trace. The cool thing is that you can do these async calls within try blocks:

  try
    wait for data from fs.readfile 'test.txt'
    print data.toString()
    if data.length > 0
      wait for data2 from fs.readfile 'test2.txt'
      print data2.toString()
    else
      print 'too short'
  catch e
    print 'there was an error'
  wait for resp from db.save()
Will do what you expect - abort after either wait for if it fails and run the catch clause. Think about how you'd do that in JS:

  var error = null;
  var data, data2, resp;
  fs.readFile('test.txt', function (err, data) {
    if (err) return handleErr(err);
    console.log(data.toString())
    if (data.length > 0) {
      return fs.readFile('test2.txt', function (err, data2) {
        if (err) return handleErr(err);
        console.log(data2.toString());
        return closeout();
      });
    } else {
      console.log('too short');
      return closeout();
    }
  });
  function handleErr(err) {
    console.log('there was an error');
    return closeout();
  }
  function closeout() {
    return db.save(function (err, r) {
      if (err) throw err;
      resp = r;
    });
  }

-----


Definitely play with the exception handling. Latent bugs aside, asynchronous statements (wait fors) work in any crazy horrible nested combination of ifs, fors (parallel or series), and trys that you can think of. Take a look at the JS output with:

kal -o output.js -f beautify input.kal

to see the sausages being made. The http server demo is a good example (https://github.com/rzimmerman/kal/blob/master/examples/async...)

-----


A side-by-side line-by-line interactive display would be wonderful in writing Kal. Reading the JS explains what it's doing quite well, except that the Kal compiler uses way too many compound comma-separated statements instead of semicolons.

-----


That's actually a side effect of uglify.js's beautify, I believe. I'm going to try to get rid of that in upcoming releases.

-----


I suggest to use ..= (or ...=) in place of the keywords 'wait for', and 'from'. Less words, and '...' traditionally means 'to be continued', or 'wait for a while'.

For this Kal code:

  task getUserFriends (userName)
    wait for user from db.users.findOne {name:userName}
    wait for friends from db.friends.find {userId:user.id}
    return friends
would become:

  task getUserFriends (userName)
    user ..= db.users.findOne {name:userName}
    friends ..= db.friends.find {userId:user.id}
    return friends

-----


The title is kinda funny to the russian ear because kal means feces in Russian :)

-----


That is really unfortunate...

I chose it because it in Hebrew it roughly means something like easy/simple/BASIC.

edit: More importantly, does that make you more or less likely to use it?

-----


I'm Russian and I didn't blink reading the title. I never stopped to contemplate the fece-ness of iCal either, until today (thank you, 10098). Anyway, keep the name. It's a fun easter egg as far as I'm concerned.

Also, note that kal is mere `faeces` in Russian, not `shit`. I don't remember when I've last used the word outside of a hospital... or inside, for that matter.

-----


Actually, doesn't affect the likelihood of me using it :)

-----


It's kinda fun and doesn't affect chances that I'll be using it, but I'd imagine it would be funny when someone will try to convince boss to use Kal :)

-----


`kal` means `stay` in Turkish, quite fittingly :)

-----


Well, even worse is the Portuguese name Rui.

I was once told it also sounds like something bad in Russian, so it can be a bit inconvenient introducing anyone with such name in Russian if not aware of this issue.

-----


For the curious, the equivalent example from the OP using promises[1]:

  function getUserFriends(userName) {
    return db.users.findOne({ name: userName }).then(function (user) {
      return db.friends.find({ userId: user.id });
    });
  }
[1]: http://promises-aplus.github.io/promises-spec/

-----


Promises + CoffeeScript really hits the sweet spot for me:

    getUserFriends = (userName) ->
      db.users.findOne(name: userName).then (user) ->
        db.friends.find userId: user.id
Or prezjordan's example:

    getUserFriends = (userName) ->
      db.users.findOne(name: userName)
        .then (user) ->
          db.friends.find userId: user.id
        .then(function (friends) ->
          somethingAsync(friends)
The concise function syntax and implicit "return" really helps.

If that's not good enough you can implement higher level control flow abstractions much more nicely than with raw callbacks, e.x. https://github.com/tlrobinson/q-step

    getUserFriends = (userName) ->
      QStep(
        ->
          db.users.findOne name: userName
        (user) ->
          db.friends.find userId: user.id
        (friends) ->
          somethingAsync friends
      )

-----


It's more obvious if you have multiple levels.

  function getUserFriends(userName) {
    return db.users.findOne({ name: userName })
      .then(function (user) {
        return db.friends.find({ userId: user.id });
      })
      .then(function (friends) {
        return somethingAsync(friends);
      });
  }

-----


I don't ever write code like this personally. The multiple levels make it more difficult to reason about, IMO.

-----


Thanks for the comparison, I should probably add promise syntax to the landing page to cover more bases.

I think a useful case would be the trickier stuff where Kal really shines. For example, doing async stuff inside of loops, conditional calls (where some paths need async waits and others don't) and error handling. I also think the Kal syntax might appeal to people who are less experienced with JavaScript and it's more eclectic features.

-----


Promises are a completely sufficient solution to handle asynchronous code in my opinion and there are already several great libraries for this in JavaScript. I don't get it why would I want to switch to a new language just because of that one feature.

-----


https://github.com/kriskowal/q solves this today without needing to cross compile. http://koush.com/post/yield-await-v8 will address this natively however unlike Q or Kal you can't start using it today.

-----


There is no need for hacks, just run node with the `--harmony` flag to get yield support today.

Q is a huge, monster library. It's 2x the size of caolan/async, which is already bloated. I personally think it's better to use https://github.com/mbostock/queue or another small, understandable library.

-----


Respectfully, is it possible you've confused Q with something else? I compressed and minified both caolan/async and kriskowal/q. Result: async is 3.4 KB, q is 4.1 KB.

I grant you that mbostock's queue is even smaller, but all are so tiny as to be a trivial download even on mobile.

The important issue here is IMHO not the size of the libraries, but how much they help you write maintainable code.

-----


If you're taking about node, what difference does it make how large the library is?

-----


These libraries work in the browser. It's not just size: debugging, understanding code, and performance matter too.

-----


Q and ECMAScript 6 solve the callback issue, but I think people might like the built in async iterators and error handling.

-----


Does anyone know if there is a JS dialect out there that adds support for callback-less async code without also adding a bunch of other coffeescripty syntax changes at the same time?

-----


Yes, it's called "JavaScript". Specifically, combining generators with promises.

http://blog.alexmaccaw.com/how-yield-will-transform-node

Definitely not as clean as a whole new language, but you can program like this today, at least in bleeding edge node and some recent browsers.

-----


That would be TameJS: https://github.com/maxtaco/tamejs

-----


Try http://www.neilmix.com/narrativejs/doc/ -- "a small extension to the JavaScript language that enables blocking capabilities for asynchronous event callbacks".

-----


StratifiedJS allows you to write callback-less async code, including full support for exceptions thrown from async code (stacktraces and all):

http://onilabs.com/stratifiedjs (disclosure: I work at Oni Labs)

It does extend JS with some additional syntax as well (new concurrency constructs as well as some syntactic sugar), but it doesn't alter existing JS syntax.

-----


rzimmerman I like it! and would consider using it for my next big project.

If I were you I would make a slight course correction follow in the footsteps of Python and Go and have one and only one correct way to do something.

When you say use two spaces, but you can use more if you want, make it an error to use more.

Pick the correct way to call a function. Pick the correct way to declare a function.

Keep up the good work.

-----


Thanks! I wouldn't call it stable or production ready by any means, just as a disclaimer.

-----


Oh hey, another idea, if you keep the project really tight and small, I wont be too intimidated to get into it and help fix issues if I start using it.

-----


I am a big fan of languages trying to solve callback hell. I use a library in Haxe that allows for similar syntax: https://github.com/proletariatgames/haxe-continuation. Haxe compiles to Javascript and is great for node programming...but it has the added benefit of also compiling to c++, php, java, c#, etc., etc. ;)

-----


JS without callbacks... so JS without the best part of JS?

-----


It's admittedly a bit link-baity. The async structures in Kal do compile to callbacks in JS.

-----


How exactly are callbacks the best part of JS? Callback aren't a language feature, they're a pattern of doing async code with anonymous functions. Anonymous functions are used for far more than just async code and cannot be replaced with an await keyword which only replaces the need to use callback for async code.

-----


Dont confuse closures with callbacks. Asynchronous non blocking code is the nice thing and callbacks are the price you have to pay to use that in JS,

-----


Of the tonnes of Javascript libraries that are unloaded on HN regularly, this is one of the more fascinating ones. Absolutely love the syntax. Great job!

-----


So instead of encouraging you to actually fix the bad nested callback code, you can dress it over with fancy syntax.

Well done on making a whole language though. It's pretty challenging and I hope you do well.

Aside: This seems very similar to narrativeJS in concept http://www.neilmix.com/narrativejs/doc/

-----


Thanks. I think the main issue with JS callback syntax is that it quickly becomes unmaintainable. Hopefully this fixes a lot of that by abstracting away the mechanics. I don't think there's something fundamentally wrong with the "relinquish control and wait for I/O" model, so I tried to make that explicit here.

-----


What do you mean by "actually fix?"

-----


Stop nesting anonymous functions. Un-nest them, name them, and encapsulate the process in a monad.

-----


monad*

*jQuery or promises like API object, whereupon you place methods- some of which may be asynchronous. an asynchronous operation returns an object with a method that performs the next action and optionally takes a callback, passing in the results of the previous asynchronous operation as a value- thus flattening the callbacks out into a sequence instead of a nesting.

Constructed properly, you can create monad combinators/transformers, to do things like automatically wrap every step in your chain with exception handling. Using common JS promises libraries gives you error handling for free.

-----


You do know that jQuery isn't a monad, right? That's the second time in this thread that you've called it one. Simplest demonstration that it isn't: try defining join.

-----


jquery-like. I never claimed that that jquery is a monad. I know it isn't. But also, I don't want to be one of those guys that throws around the word monad expecting everyone to understand what I'm talking about.

as for defining "join", help me understand. How is jquery "add" not essentially that? And why pick the fmap,join formulation instead of the bind,return formulation of a monad?

-----


jQuery's add function has a few different formulations depending on what argument you pass, but it's basically either M a -> a -> M a (if you pass it an html string, an element, or a selector) or M a -> M a -> M a (if you pass it another jQuery object) where here M refers to the jQuery wrapper and the first argument refers to `this` (obviously these type signatures are an approximation since JavaScript doesn't really work this way). join is M (M a) -> M a, which is very different.

The reason I picked join is because (in my view, anyway) it's the easiest demonstration of how jQuery is not a monad. There's no way to even get something of type M (M a) in jQuery, because the jQuery lifting operation is idempotent. The same exact problem exists with return, but I've found that people have a hard time seeing it with that example.

The problem I have with you referring to jQuery as a monad, even by analogy, is that jQuery really really isn't a monad. It's only very loosely analogous, and that analogy is more likely to teach people the wrong things about monads rather than the right ones.

-----


Well that is a fair enough concern, and I notice I used the word "Monadic" to describe jquery carelessly, to mean "Monad-like" though that isn't really what "Monadic" means.

For the case in hand though, you can get to almost but not quite really technically a monad, and still cure most of the nested callback headaches. One way I think about monads is they are a strategy for turning nested functions into sequentially composed functions. Which is, pretty much what we want, right?

-----


I just don't think it's useful to use the word monad to describe the strategy you're suggesting. I'm not criticizing your suggestion, I just don't think it has anything to do with monads.

More to the point, I don't understand why we (HN and the JS community at large) keep having this discussion. Coroutines have been around since the 60s. This is a solved problem.

-----


because you can't use coroutines in client-side javascript yet, without some backpiler, and people will disagree on what the proper reaction to that should be.

-----


I came across Galaxy yesterday, linked to from the callbacks/goto discussion. It's an NPM module that gives C# async/await semantics to JS/es6 code (of course it requires the harmony VM). It simply toggles function*/function on things, such as required modules, and provides some extra helpers... the author gives a really nice walkthrough:

https://github.com/bjouhier/galaxy

Galaxy does not claim to get rid of callbacks, though... only to help make writing JS/es6 with async/await semantics easier. I've yet to try it out, but the instructions seem clear.

-----


What's up with everyone trying to re-invent coroutines? I personally think people should upgrade JavaScript instead of inventing new languages all the time, since it creates fragmentation.

-----


I'm puzzled by the bootstrapping process. It's written in Kal itself, and you distributed compiled code as JS. Do you have a way of re-boostrapping from only the source in github?

-----


The only way to re-bootstrap is to download a compiled (JS) release from npm, then use that to compile the .kal source in the git repository. Then you can use your newly compiled version of kal to recompile itself. This is how I create the npm releases.

It's not turtles all the way down, though. If you look way back, the 0.1ish releases of the compiler were written in CoffeeScript. I ported the compiler source over from CoffeeScript file by file as it matured and eventually got rid of the dependency entirely. Technically, you could start with the last release that was written in CoffeeScript and compile up to the current release.

-----


Interesting. It must get tricky when you make an incompatible change to the language. I guess you'd make the changes to the compiler still using the old language, update the test cases, make it stable, then change the compiler sources to reflect the new language, recompile the compiler, then check in the new compiler JS. With plenty of git checkpoints along the way.

-----


One of the gems in CS literature illustrates this nicely: http://cm.bell-labs.com/who/ken/trust.html

Essentially self-compiling compilers can "remember" things that are nowhere to be found in the source code.

-----


It appears to be very similar to icedcoffeescript: http://maxtaco.github.io/coffee-script/

-----


Agreed. Why reinvent wheels?

-----


Because spokes and rims work better for different things.

-----


Perhaps name a serious deal-maker of this over iced-coffee-script?

-----


Beautiful work. The `for parallel` notation is a really interesting way of writing it.

It seems like a number of folks in this thread have expressed interest in your process -- both of designing the feature set, and of gradually bootstrapping it away from CoffeeScript to become self-hosting. I'd love to hear more, if not here, then in a blog post, perhaps...

-----


Thanks! I'm a big admirer of your work, so it means a lot to me to hear that. I wrote up a little bit about this if you are still interested: http://blog.rzimmerman.me/how-kal-compiles-itself/

-----


What do I do when I want to limit the number of tasks that are run in parallel? I'm talking about something like the queues in the famous async.js. I've seen tame.js trying to solve this by a construct named rendezvous but to me it looked even messier than inline callbacks.

-----


Using 4 spaces to indent the JS code and 2 to indent the Kal code seems a bit disingenuous

-----


Is it possible to cut out some of the explanatory comments in the examples or to consolidate them somehow? It's difficult to see at a glance how the code looks, especially without any syntax highlighting at this stage

-----


I added syntax highlighting to the github landing page manually for now. Try the .tmbundle if your editor supports it. It's not full featured but it gets the colors going. I'm working on getting it into pygments.

-----


In the `Asynchronous Wait` example did you mean: `function getUserFriends(userName, next) {` when you wrote: `var getUserFriends(userName, next) {`?

-----


Woops good catch!

-----


But where is the error handling ?

You have some examples with bloated err-returns, but where is that in your kal-code ?

-----


cbs - cbSucces cbe - cbError

replaces all the "if (e) return cb(e)" lines with cbe when you call the async function.

A whole abstraction over js just because of this, running away from the problem...instead of solving it on the spot, keep running.

-----


I Love callbacks, wtf is wrong with you guys :'(

-----


Not callbacks are a problem, but many nested callbacks are unreadable und hard to maintain.

-----


ToffeeScript is very similar but better.https://github.com/jiangmiao/toffee-script

-----


This comment would be much more insightful if you put forth your argument as to why it is better.

-----


Sorry I was in a hurry. It has many more features for a sync that looks like sync and for other things. It has a cleaner syntax. It supports easy access to multiple callback return values. It has all of the other CoffeeScript features. It seems very obvious if you look at ToffeeScript that it is objectively better. I find it difficult to believe that it isn't getting more attention. Probably just because it doesn't have a web site.

-----


OK. Better how?

-----


i really like the syntax - really close to english. is there a side-by-side comparison of kal and the generated javascript?

-----


Thanks for the suggestion. The closest to a side by side is here (http://rzimmerman.github.io/kal/#asynchronous-wait), and also the table of operators in the readme (https://github.com/rzimmerman/kal/blob/master/README.md#oper...). Also the examples directory on the github repo.

Apologies if you saw these already. I'll add something like what you suggested, though.

-----


very cool & pragmatic project: javascript / java / c++ coders get one step closer to functional programming.

-----


Yay, another JavaScript super-set language that solves one problem.

-----


On the one hand, this comment has the kernel of a legitimate criticism in it, and gets across why I probably won't use this language in a real project.

On the other hand, that's so rude! Not every project has to be the next CoffeeScript to be interesting and worthy of appreciation. If you don't like it, just click on a different story instead of cutting down someone who chose to make something cool and share it with us.

-----


You're right. I pretty much just blurted out the first thing that came to my mind when I read about it, and what I said was equivalent to garbage. It was completely uncalled for and I apologize to everybody in this forum for lowering the level of discourse, much less crapping over someone's hard work and pride.

-----


What's with this attitude? This is someone's personal project.

-----


You're right. I apologize for my remark. It was crass and uncalled for.

-----


Psh, this solves at least two issues. Don't forget the Boolean "but" operator...

-----


Yeah, this shit's bogus man. It only solves one problem.

-----


Assuming CoffeeScript is in your list of JS super-set languages, what do you consider to be the one problem it solves?

-----


You should spend more time just reading HN, before commenting. Your makes no sense on _hacker_ news.

-----


How many times do I have to say this. Generator/Yields and equivalent constructs don't actually solve the callback problem, they exacerbate it. That is, because you only get to direct one call-level down, but, that's now how you'd code. You'd have some perfectly synchronous code until it hits some infrastructure I/O part some N call layers down, and then now what? You're gonna convert everything to yields? Got any idea how messy that is?

Javascript needs proper co-routines. Nothing short of co-routines will solve the issue. Please lookup microthreads, greenlets, fibres, etc.

-----


I think you have it wrong, generators would solve the problem. If you have asynchronous code N call layers down, then everything above it would be asynchronous as well. So you would only need to yield the first call.

Check how Q.nfcall works.

-----


That's a faulty assertion. A generators working principle is that it can resume into the closure if the "caller" is looping over the generator.

Let's assume a simple call stack, A -> B -> C. Now C would be a generator, so B has to loop over C instead of just calling C.

But what about A? Well if B is now looping, and if yielding out of a loop is how you cooperatively multitask, then A now has to loop over B. So you have to convert the entire call stack from A -> B -> C to A(for ... in B(for ... in C)).

You can argue that a compiler can make this invisible, however there are some serious drawbacks with that in that:

1) One solution would be to convert any call into a loop. How do you know which ones are actual calls (stuff like Math.pow etc?) 2) You only convert things that have generators in their call stack to loops, but how do you know that?

Both the above scenarios would depend on being able to infer the "generatoricity" of the called symbol. Something which is very, very hard to do for dynamic languages like Javascript.

Co-routines work differently. Co-routines save the entire call stack and restore another one on switching to somewhere different. Hence if you have A -> B -> C and C is some I/O that would have to go to some scheduler, then the A -> B -> C call stack is saved, the co-routine would switch to say, Main -> Scheduler, the scheduler would do its work, and when it's ready, it switches back to A -> B -> C where that co-routine left of.

-----


I am not sure if I followed entirely.

But let me give you an example, again with Q.nfcall.

    MultiplyAndSquare = (a, b, cb) ->
        Multiply a, b, (err, product) ->
            Square product, (err, square) ->
                cb null, square #ignore errors for now.
    
    nonCallbackVersion = (a, b) -> Q.nfcall(MultiplyAndSquare, a, b)
    
    result = yield nonCallbackVersion(10, 20)

-----


You see how the caller has to be aware of how what he's calling is a generator. You're dragging generators all the way trough the call chain as soon as anything deep down in the call chain is a generator, and you'd want to use generators to acomplish asynchronicity.

Let's say you write some code A, which uses some other code B, which uses some other code C. C is also used by various other pieces of code like X, Y, Z, D, E, F, G, H, J, K etc. Now at some point you decide, well, you don't like callbacks in C, so you'll convert it to something asynchronous. You'll now just have to refactor A, B, D, E, F, G, H, J, K, X, Y and Z to make all their calls to C a generator iteration, and you'll have to convert any code that depends on A, B, D, E, F, G, H, J, K, X, Y or Z to be aware of that, any any code which uses those, to be aware of that.

-----


Sure, call sites of C will need to change, since C's signature changed. That's expected, since C doesn't even take a callback anymore. But callers of A,B,D,E etc don't need to change. A,B,D,E etc can still take a callback parameter and return C's result via the callback.

    MultiplyAndSquare = (a, b, cb) ->
        Multiply a, b, (err, product) ->
            Square product, (err, square) ->
                cb(null, square) #ignore errors for now.

    
    C = (a, b, cb) -> 
        MultiplyAndSquare(a, b, cb)
        
    
    ABDEXY = (a, b, cb) -> 
        #Calls C
        C(a, b, cb)
        
    
    callersOfABDEXY = (a, b) ->
        #Calls ABDEXY
        ABDEXY a, b, (err, result) ->
            Console.WriteLine(result)
        
    
    #Refactored C. Returns a promise instead.
    refactoredC = (a, b) -> 
        (Q.async -> 
            result = yield Q.nfcall(MultiplyAndSquare, a, b)
            return result)()
    
    #Refactored ABDEXY.
    refactoredABDEXY = (a, b, cb) -> 
        #Change needed here, since C now returns a promise
        refactoredC(a, b).then (result) ->
            cb(null, result)

    #No need to refactor callersOfABDEXY

-----


exacerbate is the correct spelling

-----




Guidelines | FAQ | Support | API | Lists | Bookmarklet | DMCA | Y Combinator | Apply | Contact

Search: