Hacker News new | past | comments | ask | show | jobs | submit login

Not surprised. A rough transliteration of `huntMen` for example into CoffeeScript using normal node callback syntax is under 20 lines and relatively easy to understand:

  huntMen =(buffy)->
    soulmates =(buffy, cb, mates=[])->
      getMatches buffy, 10, (userids)->
        for u in userids
         do (u)->
          getThumbnail u, (thumb)->
            isPicAVampire thumb, (is_vamp)->
              unless is_vamp
                getPersonality u, (personality)->
                  getLastTalked u, match, (last_talked)->
                    soulmates.push userid: u, thumb, personality, last_talked
                    if soulmates.length >= 10
                      cb(mates)
                    else soulmates(buffy, cb, mates)
              else soulmates(buffy, cb, mates)
                      
    soulmates buffy, (soulmates)->
      #Do whatever you need to with soulmates
Obviously it's still more of a hassle to deal with than a more featurey async library, but there isn't quite the panic of trying to do the same thing in JS.

Edit: For comparison, the Tame.JS style transliterates to ~15 lines of CS— but presumably that would compile to dozens more lines of JavaScript.

In exchange, it is lying to you about what the program is actually doing, as opposed to the callback syntax which obscures nothing. So yeah, not surprised if you don't get too much traction with examples like that.




In exchange, it is lying to you about what the program is actually doing, as opposed to the callback syntax which obscures nothing.

I'm really fascinated with this thought that adding sugar/coroutines is "lying". Everything is based on abstractions; this is just another one. This is not a different "lie" than any other abstraction (assembly, C, OS, JavaScript).


That's a fair point— it's certainly subjective. I'd disagree that an abstraction is an abstraction, though; some are more deceptive than others.

Node comes with single-threaded, callback-based async that works quite well, but it's a bit of a hassle to use for complex stuff.

Nothing wrong with that— abstraction time! The async module, for example, comes with a few different callback-based flow-control patterns. The Buffy example would look something like this:

    huntMen =(buffy)->
        soulmates = []
    
        getSoulmate =(callback)->
            mate = {}
            async.series [
                (cb)->getThumbnail u, (thumb)->cb null, mate.thumb = thumb
                (cb)->isPicAVampire thumb, (is_vamp)->cb('vampire' if is_vamp)
                (loaded)->async.parallel [
                        (cb)->getPersonality u, (p)->cb null, mate.personality = p
                        (cb)->getLastTalked u, match, (l)->cb null, mate.last_talked = l
                        loaded
                    ]
                ], (err)->
                    soulmates.push mate unless err
                    callback()
        async.whilst (->soulmates.length < 10), getSoulmate, ->
             #Do whatever with soulmates
(I don't actually use async much, so forgive me if there's an error there.)

Async's abstractions are what I would call "honest". It's still using callbacks, it's obvious what the relationship between them is. The meanings of 'series' and 'parallel' are clear; I could write them out myself, it'd just take longer. Nothing about what this code does is being obscured by the abstraction, just made prettier. I know (or can easily work out) exactly what will be executed.

Tame is abstracting the same single-threaded, callback-based async, but it's trying to make it look like it's using threads. I know in theory it must be turning my code into callbacks which are being passed around, but it's deliberately trying to make that unclear. The result is that I have a somewhat worse understanding of what my program actually does.

To be clear, I don't have a huge beef; like you say, abstractions are necessary, and Tame seems fine to me. It's just my personal preference for more honest abstractions over more dishonest ones (no doubt heavily influenced by the fact that I actually love callback-based async, which I think puts me in a small minority.)


I believe this code is painfully unreadable - and that's despite the fact that CoffeeScript is very elegant and easy to read. (It's not you, it's async.) Further, maybe i'm misreading this, but it appears getPersonality and getLastTalked are fired in serial. Can someone who knows CS well fix that and reply? Thanks!


Ah, that's a good catch. Missed that. Calling those in parallel is legitimately a hassle to roll yourself, something like:

              ...
              isPicAVampire thumb, (is_vamp)->
                unless is_vamp
                  mate = userid: u, thumb: thumb
                  finish =-> 
                    return unless mate.personality? and mate.last_talked?
                    soulmates.push mate
                    if soulmates.length >= 10
                      cb(mates)
                    else soulmates(buffy, cb, mates)
                  
                  getPersonality u, (p)->finish mate.personality = p
                  getLastTalked u, match, (lt)->finish mate.last_talked = lt

Anyway, I'll grant you that code is pretty harsh. It's much nicer with syntax highlighting and wide tabs, but still, fair cop. The flip side is that that code compiles into JavaScript that does exactly what it says.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: