Hacker Newsnew | comments | show | ask | jobs | submit | paf31's comments login

I think it really depends what you're trying to do. There's definitely a class of problems where the impact of a runtime type error would be low. But there are plenty of other cases where you want to constrain what you or your users can do at compile time.

-----


> I fail yet to see what advantages PureScript brings compared to Elm.

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.

-----


It uses JavaScript's evaluation model - i.e. strict, but you can write lazy code using libraries, such as purescript-lazy.

-----


Interesting, thanks - I've been interested in a language like the Haskell.Next that SPJ described: strict evaluation but with effects controlled by monads. I'll give it a look!

-----


Free HTML version is here: https://leanpub.com/purescript/read

-----


I've written quite a lot of TypeScript, and I generally feel very productive in it, but occasionally I feel like I want more from the type system (type classes, rank-N types, sum types, etc.) and tidier syntax. You might be interested in Bodil Stokke's recent Strange Loop talk, where she discusses why she switched from TypeScript. https://www.youtube.com/watch?v=yIlDBPiMb0o

-----


"Simple readable JavaScript" as opposed to the sort of output you might expect from compilers for other Haskell-like languages which attempt to preserve Haskell's semantics. Certainly there are cases where using techniques from pure functional programming can lead to poor performance (monadic recursion jumps to mind), but it is perfectly possible to write fast code using PureScript. And if you really need to squeeze out every last bit of performance, you can always write code in JavaScript and use the FFI.

-----


I can vouch for the real world performance. Phil has used Purescript to dramatically improve the performance of our medical image viewer. It's pretty impressive stuff.

-----


What's monadic recursion?

-----


Consider a recursive function like factorial:

  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).

-----


I wanted to play around with this, maybe you do too...

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.

-----


Thanks! Another trick I've seen used is `naked' avoiding recursion in your code, and relying on combinators implemented in some optimized lower level code. Would that work with PureScript?

-----


Yes - in fact, that's the approach we encourage for working with immutable arrays: https://github.com/purescript/purescript-arrays

-----


PureScript developer here. Happy to see this here, and happy to answer any questions.

-----


With some languages, a big part of the selling point is that it's the same or similar to a language the programmer already uses. Clojure and ClojureScript come to mind, or even server-side JavaScript. Now, with ClojureScript, another common selling point is the synergy with react -- something that would appeal even to people not using Clojure (maybe because they're averse to the JVM).

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.)

-----


I think it's definitely possible to put knowledge of Haskell to use in PureScript, but it's not like Fay, Haste, GHCJS etc. where you can copy code verbatim. I think of PureScript as "an environment in which to write principled JavaScript". That requires that, to a certain extent, you should have JavaScript's semantics in the back of your mind while developing. For that price, you get ease of debugging and some tools which you don't have in many other compile-to-JS languages (an expressive type system, type classes, ability to refactor with confidence etc.)

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).

-----


Care for a little comparation with TypeScript ? If i know TS why should i look at PS ?

-----


(Copied from below) I've written quite a lot of TypeScript, and I generally feel very productive in it, but occasionally I feel like I want more from the type system (type classes, rank-N types, sum types, etc.) and tidier syntax. You might be interested in Bodil Stokke's recent Strange Loop talk, where she discusses why she switched from TypeScript. https://www.youtube.com/watch?v=yIlDBPiMb0o

-----


Is there any particular reason why PureScript was designed with a single number type (so no proper ints)? I know that's how JS sees things, but now with a whole proper type system it would seem like we should have proper numbers? I admit I haven't read the entire docs, is there a way to e.g. have the type checker ensure a number field such as a year in a date isn't assigned a non integer?

-----


It has been discussed before: https://github.com/purescript/purescript/issues/396

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

-----


As long as it is efficient and compiler supported (I.e an int add ends up as the proper instruction, and improper assignment ends up as a meaningful compiler error) I think the implementation doesn't matter. Going to Natural numbers and such feels like a separate topic from just having efficient and type safe (modulo overflow) integers.

-----


Consider what it would take, writing in C, to store and compute integers in the "double" type, including range checks after every operation. Unless you go the asm.js route or otherwise ensure that your JIT special-cases integers, constructing range-limited integer types out of JavaScript doubles has a non-trivial cost.

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'm not sure I understand the problem, I meant that in PureScript there would be distinct number types, and the PureScript compiler would forbid me to assign an int field with a double value. The generated JS would be all regular JS "numbers" (ie floating point throughout).

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...

-----


What happens when you take two "integer" fields and add or multiply them? The compiler would need to check that the result still fits in an integer.

-----


No compiler in existence makes these kinds of checks on anything except maybe literal values in special cases. What you propose would entail evaluating the program's runtime behavior at compile time. Ensuring anything that gets put in an integer field is an integer is easy assuming you have type annotations on everything.

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.

-----


>No compiler in existence makes these kinds of checks

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.

-----


Assuming you mean two 32 bit integers, the result is a new 32bit integer that may have overflown. The compiler doesn't care in any language I know of.

-----


This looks really cool! Do you currently have any abstractions for interacting with DOM elements?

-----


I'm only a bystander, but I believe PureScript doesn't include data-to-DOM binding. If you want to do it manually, I would look at the Eff monad[1].

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.

-----


Interesting. For my current project, I don't want to include large frameworks so it looks like this library [1] should do the trick.

[1] https://github.com/aktowns/purescript-simple-dom

-----


There are a few options, but the most fully-formed is probably purescript-react. I'm also interested to see where the virtual-dom bindings go. The last chapter of the book covers how you might go about writing a library for working with DOM elements in PureScript.

-----


Why do I need statically typed languages to compile to Javascript? Isn't it just better to learn functional programming and dynamically typed languages?

-----


If you don't write Web stuff, you can ignore JS completely, and PureScript too.

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 didn't want my comment to devolve into an argument about the merits of static vs. dynamic. My comment is really to ask why would I use Typescript for web programming? Where does it fit? It doesn't seem to fit in or fill a need that isn't already possible with any combination of static and dynamic languages used separately. In fact I feel it blurs the separation of concerns between static vs. dynamic.

-----


Do you mean PureScript?

-----


My approach is to try to attract strong developers by using the best languages and tools. What technology stack do you use?

-----


Stack and tools dont attract strong developers. Salary and work-life balance does.

-----


You couldn't pay me enough to work on some stacks.

Also, certain technologies do a good job of convincing potential employees that you know what you're doing and committed to using the best tools available IMO.

-----


I agree completely.

It's hard to generalize though, you can find engineers for just about any stack with the right price points.

-----


Also why not consider remote employees?

-----


We currently employe a remote developer. Good talent, its a bit hard to manage when you're not in the same room. We're strong believers in remote workstations. My #1 priority is getting the work done. I don't care where or how you do it, as long as it gets completed in a timely and efficient manner.

-----


Very nice. It took me a while to realize that array literals aren't supported, but once I did that I was able to write concatMap and watch it being evaluated.

A couple of points:

- If there is a syntax error, the box just turns red. It would be nice to see what the issue is.

- You could probably make it clearer that it's possible to execute arbitrary expressions, by editing the map term, for example.

-----


I've added error messages in https://github.com/stevekrouse/hs.js/commit/2ed274d4607a4c9d... and https://github.com/stevekrouse/hs.js/commit/812945939e14e337....

The (edit) and (clear) links are now always visible to encourage editing, per https://github.com/stevekrouse/hs.js/commit/e301961c03d92c58...

Thanks!!

-----


Presumably this doesn't prevent a developer from creating another "pattern class" subclassing some superclass in another package, thereby breaking existing pattern matches?

-----


Mark all your types sealed. Roslyn can have a custom diagnostic analyzer to yell about unsealed pattern-matched public classes, but I'm not sure if we want to add language support for a closed set yet.

Scala allows you to seal a type hierarchy to a single file. That would work, but it seems hacky -- a file feels more like an implementation detail than a language construct to me. For one, the C# spec doesn't mention files at all -- from its perspective all your code may as well be in one giant file.

-----


C# doesn't allow you to specify both abstract and sealed on the same class - something that would be really useful for implementing closed types. The restriction is artificial and unnecessary though - CIL allows the two to coexist, and it is how the F# compiler implements discriminated unions. I think it would be wise to aim for feature-compatibility with F#, to ensure you can pattern match over types defined in F# from C#, and vice-versa.

-----


You're right of course -- I misspoke. Marking the types internal, not sealed, would allow the compiler to guarantee that all it sees is all there is.

-----

More

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

Search: