Hacker Newsnew | past | comments | ask | show | jobs | submitlogin
Roy — small functional language that compiles to JavaScript (brianmckenna.org)
125 points by paulmillr on Nov 25, 2011 | hide | past | favorite | 48 comments


It makes me sad that having monad sugar is now just a given feature for a functional language.

Especially when that language has unrestricted side effects and the example code using the monad syntax uses those side effects, completely obviating the need for, and in fact rendering useless, monads as used in the example.

This is cargo cult programming at its worst. Including monad sugar because it's the thing to do, without even understanding what monads are or why they're useful. This is quite clear from the "tracing monad" example code. This "tracing monad" is like the Writer monad, stripped of all utility and sense.

What drove you to unleash this horror on the programming masses, Moggi? Why have you cursed us so?


The example sucks but is very simple.

I didn't add monadic sugar just to be part of the "cargo cult". I added it because I'm eventually going to use it to implement an automatic continuation-passing transform. I've done a similar thing before with ClojureScript's macros:

http://brianmckenna.org/blog/cps_transform_js


Monadic sugar is probably not the way to go for implementing an automatic continuation-passing transform. Why not just do macros?


Monads suit the problem nicely and it's been done in quite a few languages already:

http://lambda-the-ultimate.org/node/4117

http://ocaml.janestreet.com/?q=node/100

http://en.wikibooks.org/wiki/F_Sharp_Programming/Async_Workf...

This is something that JavaScript desperately needs.

Macros aren't so nice for this stuff in non-homoiconic languages.


I previously had a someone antagonistic comment, but I decided to remove it.

I think that finding ways to notate asynchronous code cleanly is a pretty awesome goal, and I hope you find success in your project.


Monads are not simply a tool for managing side-effects. Why would you consider parser combinator/List Monads useless in a language with unrestricted side effects? Writing monads without sugar becomes clumsy very quickly and monads have been demonstrated to be useful innumerable times, why wouldn't you support some syntactic sugar for it?


I never said monads are made useless in a language with unrestricted side effects. I understand very well their utility even in the presence of side effects.

However, if you'll take a look at the example monad code, the bind and return methods themselves use side effects. I think that speaks for itself.


So, his example monad is bad. Indulging in hyperbole and invoking Moggi can at best be seen as attention-seeking behavior and at worst as an underhanded FUD tactic to turn people off the project. Peaker has pointed out the problem with the example monad without indulging in antics. There are better ways to communicate the mistake made in the example program. There is nothing irrecoverably terrible with Roy, but your comment conveys a false impression to the uninformed reader.


My issue is not with Roy. It's absolutely fantastic to have a functional language that compiles to JavaScript.

My issue is with the use of monads in particular, and I think my comment pretty well communicates that it's solely about that. I don't think it conveys a false impression about Roy as a whole at all.

Your statement that my comment "can at best be seen as attention-seeking behavior and at worst as an underhanded FUD tactic to turn people off the project" is just absolutely ridiculous. I have nothing against the project Roy, and I wish it nothing but success. Why would I possibly have any motivation to use underhanded FUD tactics to turn people off the project? And I don't even know what to say to your accusation of attention-seeking behavior. In what world is initiating a productive discussion about an important issue, and not in a self-promoting way at all, "attention-seeking behavior"? Get off your high horse.


> In what world is initiating a productive discussion about an important issue

A toy example is not an important issue. As for initiating productive discussions, I will let your comments speak for themselves

1. This is cargo cult programming at its worst

2. What drove you to unleash this horror on the programming masses, Moggi?

3. Why have you cursed us so?

4. It makes me sad that having monad sugar ....

5. .... stripped of all utility and sense.


The toy example is a microcosm of the important issue. The important issue is the use of monads in functional programming.

How is a discussion not productive just because it's not framed in the most formal language possible?


The tracing monad is fascinating. What would be a practical application of it? I've been studying monads through Learn You a Haskell for Great Good but haven't been able to put them into context (other than isolating side effects and enforcing order of execution).

https://github.com/pufuwozu/roy/blob/master/test/trace_monad...

  let traceMonad = {
	  return: fn x =
		console.log "Return:" x
		x
	  bind: fn x f =
		console.log "Binding:" x
		f x
	}

  console.log (do traceMonad
	  bind w = 1
	  let x = 2
	  bind y = 3
	  bind z = 4
	  return w + x + y + z
	)


That's actually not a Monad :-)

To be a monad, return must be a left and right identity:

    x >>= return = x
    return x >>= f = f x
By having return do things other than wrapping the value, the monad laws are broken.

Similarly, bind cannot have such effects, it would break its associativity.


The option monad example might be closer. I'm having trouble figuring out how to get the value out of it though

       data Option a =
	  Some a | None

        let optionMonad = {
		return: fn x =
		Some x
	  bind: fn x f = match x
		case (Some a) = f a
		case None = None
	}

	let m = (do optionMonad
	  bind x = Some 1
	  let y = 2
	  bind z = Some 3
	  return x + y + z
	)

	match m 
		case (Some x) = console.log x

shows an error Error: Type error: Native is not Option 'dwk


Bug in the type system. Fixed. Roy could be cached so you might need to force a refresh.


Know what would be awesome? If you could use the monad syntax to clean up async, cb-passing code. I've been learning a lot of Haskell lately too, and I like to think of the monads as returning a set of instructions with side effects to be executed in order. I think this example is kind of trivial. I'd love to see one that does something more than simple addition (IO!)


Jane Street's Async library for OCaml does this: http://ocaml.janestreet.com/?q=node/100. It's excellent, and I now miss it in every other language I use.

Code looks something like this:

    ...
    (* long_running_call : unit -> string Deferred.t *)
    long_running_call ()
    >>= fun result ->
    print_endline result
    ...
Running calls in parallel is easy. Say we want to run a couple of queries against a db at once, and only perform an action once both return:

    ...
    (* Db.query : Db -> Db.Query -> Db.Result Deferred.t *)
    let d1 = Db.query db query1 in
    let d2 = Db.query db query2 in
    d1 >>= fun r1 ->
    d2 >>= fun r2 ->
    ...
If you have questions about getting it set up/using it, the mailing list is the place to ask: https://groups.google.com/forum/#!forum/ocaml-core


Doesn't this have the same problem that Python's Twisted does -- that async calls may lack handlers for error conditions, and so errors may be silently ignored?


Async includes a module called Monitor that lets you correctly handle exceptions in async code. The signature of one of the most commonly used methods is this:

    Monitor.try_with : ?name : string
                       -> (unit -> 'a Deferred.t)
                       -> ('a, exn) Result.t Deferred.t
It takes two arguments. Name is used to tell you what monitor the error was caught by. The second is a function that returns some kind of Deferred.t. The whole thing returns a (deferred) Result.t (: [`Ok of 'a | `Error of exn]), letting you either do something with the results of the async call or handle the error. Usage might be something like this:

    (* query_or_print : Db.t -> Db.Query.t -> Db.Result.t option Deferred.t *)
    let query_or_print db query =
      Monitor.try_with (fun () -> Db.query db query)
      >>| function
      | `Ok result -> Some result
      | `Error e -> print_endline (Exn.to_string e); None


Actually, there are already monadic interfaces for Javascript async code with widespread use. Promises!

http://dojotoolkit.org/reference-guide/dojo/Deferred.html http://api.jquery.com/category/deferred-object/

new deferred objects can be created manually or obtained from say, an AJAX function (this is the equivalent of "return")

async operations can be chained with the deferred.then method (this is a hybrid of "fmap" and ">>=", aka "bind")


Well, there is a Cont monad in Haskell (Control.Monad.Cont). I am not too familiar with it though I know there is a good chapter about it in the Haskell Wikibook (http://en.wikibooks.org/wiki/Haskell/Continuation_passing_st...).


Author here, that's the plan! :)


This looks great! It has all the essentials and compiles to readable JavaScript, should be a real productivity booster.

Looking at the JavaScript, though, all those anonymous functions can't be very fast.. I wonder if the compiler could try to defunctionalize the output..


The only gratuitous functions I saw were the "case" statement and the "with" statement. The with statement can really only be implemented with that function (though there is something to be said of only defining it once instead of n times). The "case" statement may be gratuitous but it feels like the compiler has a reason for wrapping it in a leakless function wrapper.


For "match", it needs to be an expression for some things so that means wrapping it in a function and returning the result. In the future I'm going to do some optimisation to see if the expression is the last in a function and remove the wrapper. For example:

    return (function() {
        if(b instanceof True) {
            return ifTrue;
        } else if(b instanceof False) {
            return ifFalse;
        }
    })();
Can just become:

    if(b instanceof True) {
        return ifTrue;
    } else if(b instanceof False) {
        return ifFalse;
    }
The output of `with` is pretty gross. Definitely want to hear ideas on how to clean it up :)


Can't you take te same approach ClojureScript took, i.e. to generate a straitforward translation to JavaScript, and then let Google's closure compiler take care of the optimizing thing ?

For instance, for this Roy generated JS:

    var True = function(){};
    var False = function(){};
    var getBool = function(b, ifTrue, ifFalse) {
        return (function() {
            if(b instanceof True) {
                return ifTrue;
            } else if(b instanceof False) {
                return ifFalse;
            }
        })();
    }

The Closure compiler generates:

    var True = function() {
    }, False = function() {
    }, getBool = function(b, c, d) {
      var a;
      b instanceof True ? a = c : b instanceof False && (a = d);
      return a
    };


Roy has the same goal as CoffeeScript - create output that would be similar to how a JS dev would write it. If you use Roy but then decide you don't like it, just take the output and keep going.

I could rely on Closure for optimal performance but what I really want is optimal readability.


Interesting language. Feels a bit like Coffeescript with lesser use of symbols and (possibly less) syntaxic sugar. Going to play with it a bit more...


or... coffeescript looks like other languages that already exist.. and this one just happens to have significant whitespace as well :p


It looks very nice! I'm digging the simple Haskell-ish syntax.

The example on Types is not compiling as it is. The last line should be something like:

    console.log (getName {firstName: "John", lastName: "Doe"})


I do like Haskell syntax, but only because it allows partial application of functions.

No partial application allowed here, so what is the point? =\

   let add a b = a + b
   let add2 = add 2
Does not work.

   let fact a = 
   if a == 0 then 
      1
   else
      (fact (a-1)) * a
Does not support recursion like this either. The "functional" description brought my expectations too high I think.

It compiles to very lean Javascript though, so that's a big plus.


For some reason the parser breaks with that factorial definition, but adding spaces in between `a-1` seem to do the trick:

    let fact a = 
       if a == 0 then 
          1
       else
          (fact (a - 1)) * a

    console.log (fact 5)
Now, it's true that the Haskell syntax does not make that much sense if there is no partial application. I think that the variadic-arguments-are-always-allowed nature of JavaScript is not very compatible with partial application though.


> I think that the variadic-arguments-are-always-allowed nature of JavaScript is not very compatible with partial application though.

It could be implemented by compiling Roy functions to chains of 1-arg functions, but the amount of recursion and funcalling would probably be expensive (JS is not very good at these).

A SmartEnoughCompiler® you could also see partial application points and craft a partial application right there:

    let add x y = x + y
    console.log (add 1 32)
    let add2 = add 2
    console.log (add2 5)

    var add = function (x, y) { return x + y; };
    console.log(add(1, 32));
    var add2 = function (y) { return add(2, y); };
    console.log(add2(5));
As long as the number of arguments can be understood at compile time (which it can), it's not that hard: if the number of arguments provided make the function's arity reach 0 you partially apply it (and decrease the arity of the result), otherwise you fully apply it.


I decided against outputting like that a while ago but after seeing that example, I can't think of a great reason against it.

I'll have a go and see how well it works. Thanks!


Currying is a nice-to-have but definitely not a requirement for a usable functional language. It might be possible to add it to Roy by abusing JavaScript dictionaries and identifying functions that are partially applied but I'm not convinced it's worth the effort.


Currying is very easy to implement in terms of lexical closures.


Sure, but that can get costly fast (at runtime) when the language implementations are not highly optimized for funcalling and recursion (javascript VMs tends not to be), so ideally you'd only use partial application in the underlying (compiler result) code when that's needed rather than have all functions be curried.


It's an early version. Maybe, currying would be added in one of upcoming releases.


I have the same issue on Chrome 15.0.874.121 m. Also the tracing monad example 404s for me.


Sorry, I deployed it late last night and didn't test it.

That should be fixed.


I don't understand the monad because I'm too stupid, but I really really like Roy nevertheless.

I've really missed static typing on Javascript and Coffeescript; little typos are so hard to find! Structured typing really sounds like the way to go for putting type checking into Javascript, and I like how, just like Coffeescript, Roy tastes a lot like Javascript otherwise - avoiding all kinds of leaky abstractions you get with less JS-like languages. Plus, it has a sexy syntax.

I'd like to try this out in a real project. Is that being done already?


http://roy.brianmckenna.org/bundled-roy.js

There are some characters that shouldn't be there in the middle of the file, thus demo is broken.


This may just be my ignorance talking, but isn't a functional language that doesn't implement tail call optimization a Bad Idea?


The roy code looks very clean and readable, but what will the js output be like for larger projects, I wonder.


I think this has the same disadvantage with coffeescript. The web is full with javascript and there isn't an easy way to swap between the languages. Also it's a pain to debug.


Let's hope that Mozilla's SourceMap[1] gets finished then. Should alleviate most of the debugging problems.

[1]: https://wiki.mozilla.org/DevTools/Features/SourceMap


js2coffee helps there. I've gotten into the habit of converting old JS code with it as a first step of refactoring when I need to take over some old codebase.


Personally, I'd much prefer to have my compiled JavaScript have the same line numbers as the source. I think readability of the compiled js is great and all, but correct line numbers would be awesome

OTOH, string typing will greatly reduce your errors.




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

Search: