8 ms·
PureScript and Haskell
- yoneda 6y agoThis is a great writeup, and I just want to offer a counterpoint regarding the eager vs. lazy trade-offs. I found this article really helpful for explaining why laziness is important: http://augustss.blogspot.com/2011/05/more-points-for-lazy-evaluation-in.html http://augustss.blogspot.com/2011/05/more-points-for-lazy-ev... In particular, scroll down to the "Reuse" section. Lazy evaluation enables you to compose functions to build new functions with the right performance characteristics (constant factors aside), as in: any :: (a -> Bool) -> [a] -> Bool any p = or . map p In an eager language, predicate `p` would have to be evaluated on every element in the list, even if the predicate is already satisfied after the first element. So instead you'd have to write this function in an ad hoc way with explicit recursion/looping, without being able to reuse the more primitive functions. Of course, in an impure language, laziness can't be the default because you wouldn't be able to reason about when your side effects occur. But in my mind that's an argument for purity rather than an argument for eagerness. BTW, comparing "strict" vs. "lazy" is a category error. The proper comparison would be either "strict" vs. "non-strict" or "eager" vs. "lazy". The former is in reference to the denotational semantics, whereas the latter characterizes the operational semantics.
- jeeeb 6y agoYour example relates to the properties of lazy sequences, which are generally acknowledge as a good idea and implemented as part of the standard libraries of most major languages (e.g. Java, C#, Python 3). For example, the following Python code has the same evaluation semantics. def any_predicate(predicate, iterable): return any(map(predicate, iterable)) What's less clear is whether using lazy evaluation everywhere is a good idea. Certainly it has some really nice properties - for example it blurs the distinction between macros and functions by allowing arguments to be lazily evaluated. However, It can lead to hard to debug errors (as the error will not necessarily surface at the call site), and make it hard to reason about the performance and memory usage of code.
- wyager 6y ago> the error will not necessarily surface at the call site One of the positive side effects of this fact is that Haskellers have been forced to figure out proper error handling strategies, for the exact same reason Haskellers were forced to figure out proper effect management strategies (e.g. monadic IO). In fact, errors and effects are part of the same problem. In imperative languages, the temptation to use evaluation sequencing as a hack for handling errors and effects has proven too tempting for every pre-Haskell language I'm aware of.
- mietek 6y ago> [laziness] blurs the distinction between macros and functions by allowing arguments to be lazily evaluated. Good point. Interestingly, there doesn’t seem to be a commonly known notion of evaluation that would blur the distinction between FEXPRs and functions — that is, one that would allow “functions” access to the unevaluated syntactic form of the arguments.
- jiyinyiyong 6y ago> However, It can lead to hard to debug errors (as the error will not necessarily surface at the call site), and make it hard to reason about the performance and memory usage of code. Agreed. Clojure uses lazy sequence by default. After ClojureScript is compiled to js, sometimes it's crazily hard to figure out why the error happened due to the obscure call site in the code.
- odyssey7 6y agoI got into PureScript last year, with a little apprehension about spending my free time with such a non-mainstream language. The community’s passion turned out to be really encouraging. I’m still writing things in it when I have the opportunity, and I’m watching it to see where all the natural energy will end up carrying it.
- tikhonj 6y ago> While not directly related to strictness, a pain point on the PureScript side that I didn’t experience in Haskell was stack safety. Stack safety is directly related to laziness—Haskell being lazy[1] by default is what lets functions that would cause stack overflows in other languages work in Haskell. If you're incrementally producing or consuming a lazy data structure, memory can be allocated on the heap and garbage collected as you're traversing the data structure without needing to use the stack[2]. Haskell makes recursing over or producing lazy data structures the path of least resistance, so you end up almost never worrying about stack overflows—they only pop up in code written with strict types like numbers. Of course you end up with other issues that are unique to lazy languages—like space leaks—but you really do get some pervasive advantages from having lazy code by default. Some people make it seem like laziness has lots of downsides with only niche upsides, but that hasn't matched my experience at all: I've found laziness makes day-to-day programming better in a few ways (like not thinking about tail calls and stack overflows) that are the opposite of niche—so pervasive it's easy to take them for granted. [1]: Technically Haskell is "non-strict" rather than "lazy", but I've found that distinction obscures the core issues in discussions like this. Just throwing in this footnote as a fig leaf to accurate terminology :). [2]: I feel this explanation is oversimplifying or not quite getting details right, but can't think of a better way to explain it at the moment.
- c-cube 6y agoThis is making me reconsider a bit my stance on laziness. I'm usually in favor of eager, call by value, cause it's simpler to reason about in terms of complexity; but in OCaml I often have to be careful about deep recursions and it's annoying.
- christiansakai 6y agoI really want to use PureScript, but I wonder why I should use PureScript if I already have TypeScript? Besides, as far as I know, the PureScript part is only for the business logic, not for the rendering, etc like if you want to use React or Angular or pure DOM manipulation. The backend story goes the same (i.e, PureScript only for business logic). With the extra tooling required (i.e, spago, bower) it seems pretty pointless to use PureScript these days, what am I missing? Someone please convince me to use PureScript.
- mlboss 6y agoIf you considering Purescript then also look at Elm. Few plusses - It handles dom manipulation. - Simpler syntax - Elm-UI framework - No extra dependencies
- christiansakai 6y agoI tried Elm but it is too DSL for me. Its approach also doesn't scale. Too much writing, like Redux.
- cies 6y ago> I tried Elm but it is too DSL for me. Fair. And that's what it wants/advertises to be. "Not a general prog lang". > Its approach also doesn't scale. Too much writing, like Redux. Not sure what you mean by this. Software that needs to scale do not run in browsers. Elm is just for the FE bit, the UI, the interactive browser stuff. Hence the DSL thing makes sense (to me); though I totally understand one does not like that. And when it comes to writing: it has very terse syntax. Look at all TodoMVC examples and you find that Elm will use about the least number of characters of any implementation that gives strong typing guarantees.
- mannerheim 6y agoAs far as I've heard, it's crippleware as of v0.19
- nikitaga 6y ago
- wyager 6y ago> I expected the Haskell ecosystem to be far more mature in the backend HTTP service space. I'm curious what the author means by this; Wai and Warp are collectively by far the best (and in a sense "most mature") HTTP(S) backends I have dealt with. It sounds like the author had a weird constraint ("with the ability work within my custom monad stack") that I have never run into before which caused him to use a relatively less popular backend; I would be curious to hear more about this.
- cies 6y ago> I expected the Haskell ecosystem to be far more mature in the backend HTTP service space. I also could not place this remark. Haskell has about the most mature BE ecosystem I can think of. Servant, IHP, Yesod, Wai/Warp, and there is lots more.
- surfer7837 6y agoAnyone know how I can write an AWS Lambda in PureScript? Do I just need a JS wrapper and then call my compiled PureScript from that?
- samhh 6y agoThat would certainly work, I've got a rewrite of a WebExtension in progress that does the same thing. Depending upon what the likes of `Aff` compile down to, you may not even need a JS wrapper. If the compiled type signature is compatible you can just point the Node.js runtime directly to the PureScript output.
- mlang23 6y agoIf only Haskell had a prelude more similar to that of PureScript. Insisting on keeping backwards compatibility for didactic reasons is getting very silly after 30 years. The haskell Num class is just ugly. And that is just one example.