Hacker News new | comments | show | ask | jobs | submit login
GHCJS - Haskell in the Browser (luite.com)
139 points by wtetzner 1629 days ago | hide | past | web | favorite | 56 comments

There's also Fay <https://github.com/faylang/fay/wiki> which provides a lighter-weight compilation of Haskell to JS, omitting stuff like threading and type classes.

Nice thing about Fay is you can read the generated javascript and see the 1:1 correspondance to the haskell code. This can be a nice way to get an intuition abvout how thunks are used to implement eg, infinite length lazy lists in haskell. There is also a live IDE <http://ide.fay-lang.org/>. I think this makes Fay an excellent teaching tool.

The way it seems to be shaping up is that ghcjs will be used for big serious projects where a javascript runtime that is measured in (IIRC) megabytes is acceptable and the full power of haskell is needed. While fay will be used as glue code for projects that are mostly written in server side Haskell and want to run some code in the browser, with access to the same types used on sever-side to eg, make AJAX nicer. Fay's entire runtime is 16 kb (unminified; with minification and other size optimisations a complete program can be as small as 3 kb) <https://github.com/faylang/fay/blob/master/js/runtime.js> which makes it work well in these niches.

It's been quite exciting to see all this activity around functional programming languages that target JavaScript. It's worth comparing both the GHCJS and Fay implementation approach with ClojureScript which has no runtime and ships with a full suite of efficient persistent data structures out of the box. It is also capable today of generating very fast code with little overhead over hand written JS http://swannodette.github.io/2013/06/10/porting-notchs-minec... (and is in fact why the persistent data structures we ship work so well).

For the folks who swear by static typing, I expect core.typed will eventually provide that: http://github.com/clojure/core.typed

I love clojure/clojurescript, but will core.typed really be comparable to Haskell's type system?

Haskell's type system is crafted for Haskell and core.typed is crafted for Clojure. I don't see how they can be compared given how different the two languages are.

I'd say it the other way round: that Haskell was crafted for Haskell's type system, i.e. Hindley-Milner.

(Although Haskell also has type-classes which make my statement not quite true)

I'd say your statement is not true, type classes arrived early on and adjustments needed to made. I imagine adjustments will continue to be made as Haskell evolves as a language.

It is true that Haskell & its type system evolve together whereas core.typed must follow Clojure wherever it may go.

Mmm, and Haskell added higher-kinded types which are not part of Hindley-Milner, so yes my initial statement was too simplistic.

well, they still can be compared at some level. For example, haskell type checker is turing complete, i.e. you can do type level computation (see http://www.haskell.org/haskellwiki/Type_arithmetic)

turing complete, i.e. you can do type level computation

Turing completeness → computation, but the converse is not valid (i.e. computation → Turing completeness is not necessarily true). e.g. GADTs allow for some amount of type-level computation, but they do not in and of themselves form a Turing complete system.

Alas, looking at your link, it does seem that Haskell's type system is sadly Turing-complete.

Only if you use some extensions.


correct, "turing complete, i.e. you can do any possible computation at the type level."

I don't know if I feel joy or sadness, but I have the feeling that if type level computation is allowed, then it's better to be turing complete than falling short while giving the illusion to be useful.

As for the practical applications of a powerful type system, I know about this (but I would be interested into finding more examples, as most of the information about that is very theoretical):

http://hackage.haskell.org/package/HList (http://userpages.uni-koblenz.de/~laemmel/TheEagle/dl/Kiselyo...)

FWIW, it's probably not entirely accurate to say that Fay has a runtime. Any more than some jquery-using code does.

I think ghcjs does have a traditional runtime of a sort, but don't quote me on that. :)

Code size is pretty big currently indeed. The highest priority is to make sure that we get all required patches to support GHCJS merged into GHC, before 7.8.1 is out. GHCJS is a standalone, cabal installable compiler (it uses the GHC API), but some features, like the "foreign import javascript" FFI need some minor changes in GHC itself.

We haven't really spent much time optimizing for code size yet, and there's still lots of low hanging fruit. We will never reach 3kB minified though, for example the JSBN library (that we use to implement Integer) alone is a lot bigger than that.

The GHCJS linker does tree shaking: Only functions that are reachable from main are included in the result. We have some plans to extend this a bit, an old version already had incremental loading, but that has not yet been implemented for the new code generator.

>The GHCJS linker does tree shaking

Ahh, so does this mean we can shake out entire libraries like JSBN if we avoid using Integer?

If you're really careful and not use anything from base that might use an Integer ;)

Currently non-Haskell dependencies are managed per package, so if anything from integer-gmp is linked, it will include the whole JSBN.

If you want to weed out the unused JSBN part, you can use closure compiler (although the new code generator will probably still have a few incompatibilities with it).

Oh, so does that mean I can't do anything that uses the Integral class, for example? Even if I make sure all my types are Ints?

You can use the Integral class (when compiling, the instance gets converted to an extra dictionary argument, if you never use the instance the dictionary will be unreachable)

But Integer is really hard to avoid in general, for example the Show instance for Double depends on it.

I like Fay, but I thought the no-typeclasses thing was attributed to the fact that they used GHC as a front end and unwinding them out of GHC Core was to tall of an order for the time being? The Github page/discussion seemed to say as much IIRC. I'd really like to have typeclasses in Fay. Like REALLY like to have typeclasses in Fay.

Also threading in Haskell isn't a first-class thing. I'm not sure what you mean by that. How does one omit threading if it's not really part of the language spec to begin with? Care to expand?

> How does one omit threading if it's not really part of the language spec to begin with? Care to expand?

Not the OP but I can't resist answering. Threading isn't really part of the language proper, but as a lazy language can make flow control from language or library indistinguishable that isn't a very important distinction.

What matters is that GHC has an excellent threading implementation in its runtime and that the compiler has the primitives to build the Haskell concurrency libraries on.

I never really understood how Fay users could avoid using typeclasses, since it means (presumably) avoiding functors, applicatives, monads and all that other Haskelly stuff.

Typeclasses provide nothing but convenience there (see [1] for instance). The real power comes from the Algebraic Data Types which are intact.

[1] http://www.haskellforall.com/2012/05/scrap-your-type-classes...

I don't see how that mechanism provides subtyping (which typeclasses do)? Last I checked Haskell's structures weren't structurally typed.

You could do it like in Agda,

    data ApplicativeI f = 
      AI { pure      :: forall a. a -> f a
         , ap        :: forall a b. f (a -> b) -> f a -> f b
         , _FunctorI :: FunctorI f }

    data MonadI m = 
      AI { bind          :: forall a b. (a -> m b) -> m a -> m b
         , _ApplicativeI :: ApplicativeI m }

    return :: _MonadI m -> a -> m a
    return (MonadI { _ApplicativeI = ApplicativeI { pure = fn } }) = fn

Almost, but that's not extensible: I can't declare some new typeclass Foo and then declare that every x s.t. Monad x is also Foo x without editing the definition of Monad.

Instead of providing an instance declaration you provide a value of MonadI f -> FooI f.

Exactly, and then that function is "proof" of the relationship.

Personally I wouldn't want to do the instance plumbing by hand.

Note that they have almost the entire runtime working, including the threading model, which allows you to fork threads, use MVars, perform STM, etc.

Truly awesome and very promising.

Is incredible that we don't have an Android or iOS Haskell interpreter but we can run it on an embedded browser first.

I hear ARM support will be standard in the next GHC, and there currently are ports and cross-compilers. Unless you're specifically talking about ghci, which I agree needs to be ported, though that hasn't happened for the browser yet. It's possible a project like this could help that happen, though...

http://hackage.haskell.org/trac/ghc/wiki/Building/CrossCompi... https://github.com/ghc-ios/ghc

I am looking to the gchi because it's mainly to play and learn . I think a mobile/tablet is a nice environment to do that.

There are ports of ghc to both Android and IOS. I've run ghc directly on Android. ghci not so much, so you're technically right about there being no interpreter.

That's awesome! I have played a lot with Scala and Android, and type safety, options, futures really improved the development on Android (less code, less bugs). I would be curious to try Haskell on that platform.

Exactly! As I understand it, the moment js gets nice things like tail calls or concurrency, with very little work ghcjs can too! I'm really really excited.

Unfortunately, tail calls can't be implemented without breaking JS semantics. See: http://code.google.com/p/v8/issues/detail?id=457 .

You don't need tail calls in JavaScript in order have them in a language that compiles to JS. The tail call can be compiled as a while loop. This is what Scala does on the JVM (and presumably what Scala.js does as well).

This only works with calls that can be resolved at compile time (unless you mean a trampoline). That happens to be most of the common use, which is great, but it's not perfect.

Trampolines still work, albeit with a pretty substantial perf penalty.

No. Automatic tail call elimination can't be implemented without breaking JS semantics.

Manual tail calls can perfectly well be implemented without breaking anything.

Of course! The parent mentioned JS getting them however, which is, as I mentioned, impossible.

Yeah, just add goto!

Yes, add goto. If JS is going to be the "assembly language of the web", it needs a general branch construct or it will suck.

Well then I dream of a day when an ecmascript revision makes the breaking changes needed to fix this. Or some sort of clever hack works around that constraint

The change is proposed for ECMAScript 6: http://wiki.ecmascript.org/doku.php?id=harmony:proper_tail_c...

GHCJS currently uses a trampoline, but all calls are in tail position, so when this gets implemented we can easily generate code that uses native JS tail calls.

Like GHC, GHCJS uses its own stack for non-tail calls.

I think native code GHC has its stack in the standard location, though it manages it explicitly (as most native code languages do). (I'll double check the ghc source later to make sure i'm quibbling correctly)

fork threads, use MVars, perform STM

As someone who is very familiar with purely functional languages and with multiprocessor synchronization, I must ask, why would anyone want to use any of these things in Haskell? Aren't two of the primary benefits of Haskell the ability to auto-parallelize, and not needing to reason about state?

"Purely functional" is a misnomer. A better description is "typed effects". Haskell doesn't forego effects, it types them. You still have imperative destructive updates if you need them (e.g: to avoid a logarithmic complexity hit, or a constant performance hit). You still may want to write imperative programs, often-times as the backend of a pure functional interface.

Haskell is actually quite good at it, and jokingly referred to as "the world's finest imperative language".

Haskell doesn't have auto-parallelism. There are some excellent libraries for parallelism (strategies, DPH, repa) but they take some work. True auto-parallelism (a la Parasail) is far more difficult to get right (often due to granularity of work units).

Mercury does a decent job of this (not quite auto-parallelism, you need to hint at parallelization points, but it manages granularity for you).

I really hope never to have to write Javascript again.

There is another option to GHCJS and Fay now - Sunroof (http://www.ittc.ku.edu/csdl/fpg/software/sunroof.html) - which is entirely inside Haskell - no compiler needed other than GHC.

Excellent, but where is the "try now" link to test in the browser?

I want that too! Dan Frumin is working on a GSoC project that uses GHC, GHCJS and diagrams to make a "try now" pastebin site with graphics.

Unfortunately, GHCJS is not quite Safe Haskell code, so we are still working on sandboxing the compiler with SELinux.

his weblog, more updates will follow: http://parenz.wordpress.com/

That's partly the point of the student's gsoc pastebin like project!

Luite also has a vagrant image available that has a working ghcjs patched build available for down load.

Where are all the people saying that we should only ever use Javascript in the browser? Mysteriously missing now that the language discussed is Haskell?

We should only ever use javascript in the browser.

And maybe not even there =P

Guidelines | FAQ | Support | API | Security | Lists | Bookmarklet | DMCA | Apply to YC | Contact