PureScript: a statically typed language which compiles to JavaScript
github.com
github.com
Granted I would love for more languages to be usable on the web; even if I thought JavaScript was the best language it's always good to have competition and alternatives. But I think that has to come through browser support. I wonder how feasible it would be to generate some sort of byte code similar to how Java and others work with the JVM.
Understanding the key parts of writing JS (closures, scope, GC, etc.) isn't all that hard and it seems like more efforts are geared towards an abstraction layer and/or framework than just learning the language in the first place.
I think it's syntax and familiar looking and feeling environment cheerfully misleads people into thinking they are in a comfort zone, while they thrash around throwing sand in their own eyes, albeit with a familiar looking bucket and shovel.
So where is PureScript positioned related to this? Let's say I'm writing a backend in Ruby or Python and not Haskell, why PureScript instead of CoffeScript or sweet.js?
(I'm actually interested in this, not intended as a put-down.)
Of course, one of the best things about AltJS is the interoperability with other languages. PureScript won't be the best tool for every case, but it has a simple FFI. It's possible to write complete front-ends in PureScript, but I'd love to see more examples where PureScript is used alongside something else (I've used TypeScript with PureScript successfully, for example).
The reason I have been against the idea so far is that an integer type is easily accomplished using the FFI and user code. Here is one example (not quite what you're asking for, but hopefully instructive): https://github.com/darinmorrison/purescript-int64
Personally, it surprises me that with all the years of extensions to ECMAScript, nobody has added 1) sized integer types like word32, word64, and so on, and 2) transparent bignums, similar to int/long in Python.
I mean there are several compilers from langs with strong type systems, such as F#->JS with FunScript, and in those, usually the source lang has integers...
For the cases you mention the checks would simply be: integer * integer = integer, integer + integer = integer. Problems only arise when real/floating point numbers are introduced and how to handle integer division.
If your claim is that no compiler in existence can make these kinds of checks at compile time, that's not true. Any dependently-typed language can do this (Idris, Coq, Agda, ATS). In fact, even a language with refinement types can do this, like Liquid Haskell.
And a number of languages do these checks at runtime: Ada and Nimrod are two that come to mind.
It sounds like there are adapters to few popular frameworks, like React [2] and Angular [3].
[1] https://leanpub.com/purescript/read#leanpub-auto-the-eff-mon... [2] https://github.com/purescript-contrib/purescript-react [3] https://github.com/purescript-contrib/purescript-angular
I'm guessing a JS-controlled framework, like React or Mithril, would be a better fit if you choose to use PureScript as a core piece of your app, as they are function-based, like PureScript.
If you prefer dynamically-typed languages, use them directly (JS, CoffeeScript, etc.)
If you prefer statically-typed languages, you must care about a dynamically-typed language, since every statically-typed language can be thought of as a dynamically-typed language + a machine-checkable safety net. For example, Haskell/ML/etc. are built on Lambda Calculus, C/FORTRAN/etc. are built on machine code. If you're programming for the Web, why not use JS as the dynamic part of your language (the "computational content")?
(I.e. what are the relative strengths and weaknesses of these two "modern languages targeting JS" offerings?)
Elm has its own package manager, not sure if that is good or bad!
The biggest difference is that Elm is almost incidentally a similar strongly typed functional programming language and its biggest focus has always been on FRP (functional reactive programming) to simplify UI programming in the browser.
fact :: Number -> Number
fact 0 = 1
fact n = n * fact (n - 1)
This will build stack frames and fail with a stack overflow for large inputs. We can fix this by using tail recursion and an accumulator: fact :: Number -> Number
fact = go 1
where
go acc 0 = acc
go acc n = go (acc * n) (n - 1)
The PureScript compiler will recognize that every recursive call is in tail position, and will convert the function to a while loop.If we want to perform side-effects during the recursion, we typically use a monad to track the type of side effect in use. For example, we can use PureScript's Eff monad to trace information to the console:
fact :: Number -> Eff (trace :: Trace) Number
fact = go 1
where
go acc 0 = do
trace "Done!"
return acc
go acc n = do
trace "Keep going ..."
go (acc * n) (n - 1)
In the case of the Eff monad, the PureScript compiler employs some special tricks in the optimizer to enable the tail call optimization, so we're still OK. But this is only possible because Eff is defined in the standard library (at least until we implement custom rewrite rules).In the general case, the compiler only sees applications to the monadic bind function of the underlying monad. For example, if we use the Writer monad instead:
fact :: Number -> Writer String Number
fact = go 1
where
go acc 0 = do
tell "Done!"
return acc
go acc n = do
tell "Keep going ..."
go (acc * n) (n - 1)
which is equivalent after desugaring to the following code: fact :: Number -> Writer String Number
fact = go 1
where
go acc 0 = tell "Done!" >>= \_ -> return acc
go acc n = tell "Keep going ..." >>= \_ -> go (acc * n) (n - 1)
This is an example of "monadic recursion" - a recursive function whose return type uses some monad. In this case, the recursive calls are no longer in tail position, so the compiler cannot apply the tail call optimization. The result is that the compiled JavaScript might blow the stack for large inputs.The point is that idiomatic Haskell code is often not appropriate in JavaScript because of the different semantics, so it might not be appropriate in PureScript either. The good news is that many Haskell idioms have corresponding PureScript idioms (often using the Eff monad).
Install purescript:
https://leanpub.com/purescript/read#leanpub-auto-installing-...
The code, put it in a file named recur.purs (or w/e you want):
-- recur.purs
module where
import Control.Monad.Eff
import Debug.Trace
-- naive factorial function
fact :: Number -> Number
fact 0 = 1
fact n = n * fact (n - 1)
-- factorial function exploiting tail call optimization by putting function in tail position (at the end)
fact' :: Number -> Number
fact' = go 1
where
go acc 0 = acc
go acc n = go (acc * n) (n - 1)
-- use Eff monad to trace information to the console
fact'' :: Number -> Eff ( trace :: Trace) Number
fact'' = go 1
where
go acc 0 = do
trace $ "The answer is: " ++ show acc
return acc
go acc n = do
trace $ "(" ++ show acc ++ " * " ++ show n ++ ")" ++ "(" ++ show n ++ " - 1)"
go (acc * n) (n - 1)
main = fact'' 8
Then to compile it: psc recur.purs --output dist/Main.js --main Recur
Hope this is as educational (and fun) for others as it was for me.Though I'm not necessarily sure it wouldn't be high performance just maybe a hair slower to load. Then again I don't know how well their generator works.
1. JS as an assembly language. This is what emscripten[0], a LLVM bytecode-to-JS does, by following the asm.js specification[1]. This is for CPU-intensive tasks, not DOM manipulation. You can use any language that has a LLVM frontend.
2. JS as a target language, from a specific, new language. Those include cleaner variants that keep the same semantics (CoffeeScript, TypeScript, maybe Dart, ...) or more innovative ones, such as Elm[2], which is written in Haskell too. I'd say that a big innovation that makes the life easier is FRP (which is at the heart of Elm or React.js).
I fail yet to see what advantages PureScript brings compared to Elm.
0: https://github.com/kripken/emscripten 1: http://asmjs.org 2: http://elm-lang.org/
I think they fit different use cases. Elm is excellent at interactive web apps using FRP. PureScript is a little more general purpose and has a few type system features which Elm currently does not (type classes and rank N polymorphism). Also, PureScript's generated code is a bit simpler and doesn't need a runtime library.
The other major problem I have with it is that it encourages us to go back to the class-based patterns that we've been trying to get away from.
``` { "keys": ["super+v"], "command": "paste_and_indent" }, { "keys": ["super+shift+v"], "command": "paste" }, ```
Though you should always be careful when copy/pasting code.
I'm not sure how it encourages classes, I personally didn't even use classes at all for the first long period of my CoffeeScript life. And now only do sparingly.
There is a functional language called Elixir which runs on the Erlang virtual machine. Elixir has been designed to look like Ruby even if it's very different. That makes Ruby programmers (and maybe Python ones too) feel at ease in their first approaches with the language. When you realize how different the two languages really are you already grokked enough of Elixir to keep you going. There isn't that feeling of "oh my, I can't make it" one might have when looking at code like this http://svn.openfoundry.org/pugs/src/Pugs/Eval.hs when knowing only about C/Java/PHP/Python/Ruby
Anybody knowing Ruby or possibly Python should compare that with this https://github.com/elixir-lang/plug/blob/master/lib/plug/con...
So, my take is that if a language designer wants to create a functional language that will lure really many procedural programmers into it, s/he has to design something that looks like C or Java. PureScript is too much like Haskell (plus I hate indentation-sensitiveness, but that's almost only a subjective matter). Time will tell.
Javascript has enough implicit behavior and then some to make working with it a nightmare unless you remain extremely diligent with your coding practices. To a large extent, it's been around long enough for this to not be a major problem anymore, but is this an acceptable state of affairs for a language that dominates web programming? I'd expect most people to say "no".
There are already several JavaScript flavors for the audience you mentioned.
> unless a customer asks to do so.
which I agree with.
Since 1986 I already have seen too much technology come and go, to be convinced to use "language of the month" at work.