13 ms·
Something that I found about going from Haskell to Rust was that Rust provides many powerful abstractions without compromising on performance. It was very frust
by slaymaker1907 9y ago
Something that I found about going from Haskell to Rust was that Rust provides many powerful abstractions without compromising on performance. It was very frustrating to try reasoning about performance in Haskell due to its laziness. Every language has quirks about how to write performant code, but Haskell is notorious in my mind for being quite easy to write obscenely slow code in.
- aaron-lebo 9y agoThis is what's really nice about Nim. It writes like Python, but is fast like Rust. Ran across Option in it the other day, though it's not used heavily. I really wish there was something like Haskell but with a runtime like Nim or Go. Perhaps that is OCaml?
- captainmuon 9y agoExactly. For me, Haskell was a gateway to Nim. I wanted something that feels familiar, but with a strong type system and native speed. I wish I could invest some time in working with Nim. It doesn't get as much attention as it deserves in my opinion, and it would really benefit from growing the community a bit.
- nicoburns 9y agoHave you looked at Swift? It's pretty similar to Rust, but eschews all the lifetime stuff in favour of a runtime that does reference counting.
- pjmlp 9y agoActually Swift 4.0 just got some memory ownership support, with more to come later.
- kibwen 9y agoLink? Last I checked all of Swift's ownership ideas were just in-progress proposals, with implementation to be left to far-future releases.
- pjmlp 9y agoWatch the video about the talk, what is new in Swift 4.0 at WWDC 2017. Basically 4.0 will introduce the notion of exclusive access, with more affine type ideas to follow up on 4.x. EDIT: This feature is described by SE-0176. https://github.com/apple/swift-evolution/blob/master/proposals/0176-enforce-exclusive-access-to-memory.md https://github.com/apple/swift-evolution/blob/master/proposa...
- beefsack 9y agoSwift is useful if you're an Apple developer only targeting Apple users, seems like a bit of a niche.
- AlexeyBrin 9y agoSwift works reasonably well on Linux. You can even run Swift on Windows 10 through Windows Subsystem for Linux.
- beefsack 9y agoI tried under Arch a few months ago and found swathes of the standard library didn't appear to work, is this something which has been fixed?
- AlexeyBrin 9y agoAFAIK only the core language is fully ported, for the libraries ... it is a work in progress.
- jandrese 9y agoIn other words it is useless currently.
- AlexeyBrin 9y agoTell that to IBM that uses Swift in production. See IBM's Kitura open source server framework if you are interested.
- jandrese 9y agoThey use a language without a standard library in production?
- 9y ago
- delhanty 9y ago>Every language has quirks about how to write performant code, but Haskell is notorious in my mind for being quite easy to write obscenely slow code in. +1 One approach that occurred to me was to use Haskell and Hackage for prototyping a design and then a Rust implementation for production. It also reminds me of this from several years ago: GHC honcho Simon-Peyton Jones saying the next Haskell will be strict, but still pure with effects only via monads. [1], [2]. Following on and presumably related to that PureScript is strict. [3] [1] https://news.ycombinator.com/item?id=1924094 https://news.ycombinator.com/item?id=1924094 [2] http://www.cs.nott.ac.uk/%7Egmh/appsem-slides/peytonjones.ppt http://www.cs.nott.ac.uk/%7Egmh/appsem-slides/peytonjones.pp... (PowerPoint) Slide 40 [3] https://github.com/purescript/purescript/wiki/Differences-from-Haskell https://github.com/purescript/purescript/wiki/Differences-fr...
- mrkgnao 9y agoI'm under the impression that SPJ made that comment thinking of adoption (for a future "regrettably popular" Haskell), since the difficulty of reasoning about laziness does put people off. I might be wrong though: maybe he was arguing that laziness doesn't warrant the user and compiler-author cost it brings. I don't remember where I picked this up from. Idris is strict too, but optional laziness is built into the compiler with the use of a special-cased Lazy wrapper of the form data Lazy a = Delay a This is kind of the opposite of strictness annotations (bang-patterns) from Haskell, except one doesn't need to pattern-match on Delay or use it to wrap arguments to functions expecting Lazy values. The Idris compiler can do something like "laziness analysis" (admittedly trivial in comparison to GHC's strictness analyser) to introduce and eliminate Delay constructors automatically. The laziness information is carried around in the types: force : Lazy a -> a force a = a https://github.com/idris-lang/Idris-dev/wiki/Unofficial-FAQ#can-i-have-lazy-evaluation-if-i-want-it https://github.com/idris-lang/Idris-dev/wiki/Unofficial-FAQ#... (purescript-lazy is also a thing, but I think it's not as seamless because of the "no compiler magic" position.) In any case, what about -XStrict? I've never used it (or -XStrictData either), but isn't it a solution to the laziness problem, albeit a nuke-ish one at that? And while we're thinking ahead, let's talk about the project to get linear types (something similar to Rust lifetimes, but weaker) into GHC :) https://ghc.haskell.org/trac/ghc/wiki/LinearTypes https://ghc.haskell.org/trac/ghc/wiki/LinearTypes
- oconnore 9y agoComparing it against Rust doesn't really seem fair. If I wasn't using Haskell, I'd be writing Erlang, and last I checked GHC is much faster than Erlang/OTP. Certainly faster than CPython, Ruby, and most Node.js. Trying to compete with Rust or C++ performance in Haskell is certainly frustrating to reason about :)
- pletnes 9y agoWhile "GHC is faster than CPython" is a fairly accurate generalization, I still think python performance is easier to reason about. Haskell can give you fast code, but sometimes it will be super slow for no obvious reason.
- motet_a 9y agoMoreover, CPython is a interpreter without JIT while GHC compiles to machine code. This is totally different. IMHO, it is much more correct to compare a JIT-enabled interpreter to GHC.
- chongli 9y agosuper slow for no obvious reason Obviousness is hard to pin down. What may be completely obscure to a Haskell beginner is obvious to a veteran. Is it really fair to count people's strict-language preconceptions against Haskell? I don't know. GHC provides a wealth of profiling tools that an expert Haskell user can employ to find space leaks. Heck, experts generally know where to look (excessive use of lists, spine-nonstrict data structures) so that the profiling tools become a last resort.
- whatshisface 9y agoA sufficiently smart [compiler | developer] can [make it so that | think] anything is easy. Since performance is exactly a measure of the number of imperative steps that need to happen in a program, low level imperative langauages are going to win in performance-obviousness every time. (Rust probably beating C in terms of avoiding memory problems, C probably beating rust in terms of not supporting language constructs that can make a single line worse than O(1).) "Accidentally" asking the language to do something time-expensive is as impossible in C as forgetting a free() in Haskell.
- JoshTriplett 9y agoI had the same experience: I really enjoyed the expressiveness and power of Haskell, as well as the strong type system. But Rust gives me all those things, as well as iterators for laziness when desired, without the pervasive laziness that had me constantly chasing down space leaks and adding strictness annotations.
- vmchale 9y ago>But Rust gives me all those things, as well as iterators for laziness when desired, without the pervasive laziness that had me constantly chasing down space leaks Well, yeah, Rust has manual memory management. Of course you don't get space leaks. Anyways, Rust's laziness is not on par with Haskell. Iterators are great but it's not at all the same as e.g. GHC's lazy IO.
- JoshTriplett 9y ago> Well, yeah, Rust has manual memory management. Of course you don't get space leaks. I wouldn't call Rust's memory management "manual". It's the only language I know of that I'd describe as having automatic memory management without garbage collection. > Anyways, Rust's laziness is not on par with Haskell. Iterators are great but it's not at all the same as e.g. GHC's lazy IO. Iterators, futures, and macros (for creating new language constructs) tend to provide the laziness I want. Beyond that, I've tended to find laziness as much a source of bugs as features. (Speaking from a perspective of having written and maintained large programs in Haskell, and in Rust.)
- agumonkey 9y agoHence a reason why so many people are sticking to ML despite the lack of social momentum.
- zem 9y agoocaml is a great language in its own right too; the only haskell features I find myself really missing are do notation and operator overloading.
- kornish 9y agoAlso the multicore concurrency.
- kornakiewicz 9y agoF# does have operator overloading.
- systems 9y agobut F# doesn't have Ocaml's performance, which I believe was the point being made for ML over Haskell I honestly don't know how F# compares to Haskell over performance
- bpicolo 9y agoF# is essentially just C#, which should be reasonably performant? It's possible the persistent data structures aren't optimized I guess? Pity something's busted in the benchmarks game: http://benchmarksgame.alioth.debian.org/u64q/fsharp.html http://benchmarksgame.alioth.debian.org/u64q/fsharp.html
- igouy 9y agoIt wasn't busted with .NET Core 1.0.1 005db40cd1. Is F# working with .NET Core 2.0 Preview 1 for you?
- wyager 9y agoThis is a very overstated concern, in my opinion. Based on my experience, once you learn the common gotchas (lazy folds, WHNF, etc.) it's rarely an issue. Real-world programs (web servers, databases, etc.) typically end up using frameworks that deal with strictness concerns for you anyway. For maybe the first couple weeks I was learning Haskell, this was a source of confusion, but I don't seem to run into it anymore. If it's really a struggle for some reason, you can always take the nuclear option and enable strict mode on your code. Haskell is very good for writing extremely fast code that's also extremely composable, which most languages (even Rust, to a substantial degree) really struggle with. Haskell's pure non-strict semantics and support for fusion/rewrite systems means that you can write complex operations on vectors, byte arrays, text, etc. that span multiple modules but get compiled down to extremely tight in-place assembly code. Another way of looking at it is that the economies of scale for Haskell are very different. There's a higher up-front cost because you have to learn about how strictness works, but as a consequence you can write vastly more complicated programs that are, say, 80% as fast as hand-optimized C for only 10% of the effort of hand-optimized C.
- delhanty 9y ago> .. you can write vastly more complicated programs that are, say, 80% as fast as hand-optimized C for only 10% of the effort of hand-optimized C. Computer Language Benchmarks of GHC versus C don't seem to be close to matching your 80% as fast claim [1]. Also, the 10% of the effort of hand-optimized C bit - seem to recall there was a caveat - "if you happen to Don Stewart" [2]. The following is just my vague, uninformed impression, but Haskell and Go seem to be opposite ends of a spectrum, with Rust somewhere in between: Haskell: good for expert individuals and small teams. The language is highly sophisticated and the compiler is rather slow. At heart, it's a research language, so the direction might not always suit production use. If you contract out to a Haskell development shop, who do you turn to with your Haskell code-base if things don't work out? Go: designed by Google for their use-case - large teams of commodity programmers. It's very quick to onboard programmers - they can be productive and non-dangerous quickly. The language is very simple and the compiler is very quick - which is again good for scaling large teams. [1] https://benchmarksgame.alioth.debian.org/u64q/compare.php?lang=ghc&lang2=gcc https://benchmarksgame.alioth.debian.org/u64q/compare.php?la... [2] https://donsbot.wordpress.com/2008/05/06/write-haskell-as-fast-as-c-exploiting-strictness-laziness-and-recursion/ https://donsbot.wordpress.com/2008/05/06/write-haskell-as-fa...
- hellofunk 9y agoI don't use Haskell but didn't I read recently that the latest version of Haskell addresses this common concern by allowing you to turn off laziness altogether so everything evaluates eagerly?
- lkurusa 9y agoThere is -XStrict, see here for a discussion: https://ghc.haskell.org/trac/ghc/wiki/StrictPragma https://ghc.haskell.org/trac/ghc/wiki/StrictPragma
- vmchale 9y ago>Haskell addresses this common concern by allowing you to turn off laziness altogether so everything evaluates eagerly It's a common concern but it's a terrible critique. Laziness has many subtle benefits, but everyone whines about it because its pitfalls are decidedly not subtle.
- deleted 9y ago[deleted]
- k__ 9y agoWasn't the selling point of laziness performance? "It only does what is needed!" I didn't use Haskell but Nix and at least there it seemed reasonable to "not do" everything.
- snovv_crash 9y agoIn other languages when the compiler misses an optimization you get a constant order penalty on a small piece of code. In Haskell when the compiler misses an optimization the performance hit is potentially unbounded, as it could think anything is a dependency. (You want the first N digits of pi? Just wait while I calculate all of them for you...)
- codygman 9y ago> You want the first N digits of pi? Just wait while I calculate all of them for you... I don't think that example cold actually happen, or at least I can't see how it would happen in Haskell.
- Tuna-Fish 9y agoHaskell can be made very fast. The problem is that at least for me, performance is often quite non-intuitive. As in, make tiny change to program, it is now a hundred times slower. Then you get to hunt down the reason why it's now doing too much work/ why stream fusion isn't working anymore/why it's leaking so much space/etc for hours. I enjoy writing Haskell for small hobby projects, but I doubt I would choose it for real work now that F# and Rust exist.
- tome 9y agoI don't think anything is the unique selling point of laziness. Wikipedia[1] says "The benefits of lazy evaluation include: * The ability to define control flow (structures) as abstractions instead of primitives. * The ability to define potentially infinite data structures. This allows for more straightforward implementation of some algorithms. * Performance increases by avoiding needless calculations, and error conditions in evaluating compound expressions." In my several-year experience as a professional Haskell developer the first and second points are at least an order of magnitude more important than the third. [1] https://en.wikipedia.org/wiki/Lazy_evaluation https://en.wikipedia.org/wiki/Lazy_evaluation
- georgewsinger 9y agoHaskell's laziness is an issue for you? Then how do strictness annotations (and/or module flags) not solve this problem? Haskell also has inline-C FFI support for "type safe" performance escape hatches (in the sense that you can manually bring C constructs into Haskell's type system): https://github.com/fpco/inline-c https://github.com/fpco/inline-c The only way that Haskell inescapably suffers from performance hits (that I'm aware of) is not in its laziness or lack of imperative programming support, but in its periodic GC pauses. In such cases where GC pauses are an issue, I still think you can -- in principle -- write more and more performance critical pieces of your application in a non-GC language (in fact, you could do this in Rust just as well as C/C++). You can also manually trigger a GC pass, although you cannot -- to my knowledge -- manually stop and start GC passes in your code. This is the approach I'm currently taking in a performance critical VR application where GC pauses could absolutely be an issue (using as much Haskell as possible, with C and GC triggers as an escape hatch). And if someone could point out a flaw in my reasoning (in particular, how GC pauses might be inevitably avoided even with C FFI), I would gladly pay them money, because it's so important I not get this one wrong :).
- mej10 9y agoIt is also possible to avoid GC pauses causing problems in your application with a load balancer. Basically shifting load between haskell processes when a big GC is imminent. Read about that technique from Simon Marlow of Facebook's Haskell team. Of course it would be awesome to not have to worry about that.
- georgewsinger 9y agoNice -- this technique never came up when I searched /r/haskell for GC pause workarounds. And yes -- it would be awesome not to have to worry about this.
- dllthomas 9y ago> Then how do strictness annotations (and/or module flags) not solve this problem? Strictness annotations (and associated pragmas) only change the strictness of constructors and functions defined in my modules. I still have to reason about laziness as I use almost anything from the Haskell ecosystem.
- brianberns 9y agoYou might want to consider F# as well. It supports both lazy and greedy functional programming very nicely.
- verandaguys_alt 9y agoWhile I've found that Haskell's laziness can often be worked around with appropriately-implemented memoization, it's an absolute PITA for anything that requires frequent hops into the IO monad (unsurprisingly). Historically, this has been a bottleneck for me that's prevented me from using Haskell in any major projects. A Haskell port of a logging library I'd originally written in Python ended up being almost 2 orders of magnitude slower than CPython 3, and almost 3 orders of magnitude slower than PyPy3. PyPy3 resulted in performance similar to a barely-optimized C port.
- vmchale 9y agoDo you have source code? It sounds like you were writing unidiomatic Haskell.
- verandaguys_alt 9y agoI actually posted the port on the Code Review SE, where (after almost a month of silence), someone stepped up and offered a CFFI solution. https://codereview.stackexchange.com/questions/157118/a-port-of-a-simple-logger https://codereview.stackexchange.com/questions/157118/a-port...
- vmchale 9y agoYou might have better luck with the fast-logger library, for one. I'd also suggest using ansi-wl-pprint, which would work on Windows and also probably be faster than string concatenation (which is just plain bad in Haskell). As was pointed out, GHC is calling getCurrentTime too often. If you take advantage of GHC's laziness, the value will stay valid longer. I assume that is what fast-logger does. https://hackage.haskell.org/package/fast-logger https://hackage.haskell.org/package/fast-logger
- crimsonalucard 9y agoIt's not just laziness. Functional programming is what happens to the universe when you eliminate the concept of Time. It is a mega abstraction over the what's really going on under the hood. However we live in the real world and in the real world you cannot eliminate the concept of time, even in an abstraction such as functional programming. Therefore Functional programming is an utter lie. Reasoning about the performance of a functional program is to peer through the lie and "reintroduce" time into this abstraction. This is why it it notoriously hard for functional programs to be reasoned about. Procedural programs are much more better for this in that regard.
- tome 9y agoThis is utter nonsense. Everything you wrote is somewhere between 90° and 180° wrong.
- crimsonalucard 9y agoI'm totally correct. You are completely unaware of what's going on. We can discuss this logically in the comments, you'll see. It's not going to pretty for you as my arguments will pick you apart piece by piece.
- hxegon 9y agosaying "I'm going to destroy your argument with logic" is not the same thing as actually destroying someone's argument with logic.
- crimsonalucard 9y agoLogically speaking, I never equated the two statements. It's up to him whether he wants to engage in a debate. Additionally, this guy just replied saying I was 180 degrees off angle without any explanation, how polite.
- akhilcacharya 9y ago>Functional programming is what happens to the universe when you eliminate the concept of Time. I've heard this a lot but still can't find a good code example to illustrate this. Any help?
- willtim 9y agoMost problems arise from using linked-lists in inappropriate situations. This includes using the default String type. Both the list and string syntax have been overloadable for a while, so using alternatives when necessary shouldn't be an issue. Understand the complexity of the data structure operations you are using and you will get fast code, laziness does not change this (although space usage can be hard to reason about). The benefits of laziness is difficult to appreciate if you are not used to it. For a great example of the benefits it brings, please read this: http://augustss.blogspot.co.uk/2011/05/more-points-for-lazy-evaluation-in.html http://augustss.blogspot.co.uk/2011/05/more-points-for-lazy-...
- vmchale 9y ago>It was very frustrating to try reasoning about performance in Haskell due to its laziness. Conversely, I find it hard to achieve the same performance with strict code. I think it's likely a matter of familiarity. >Haskell is notorious in my mind for being quite easy to write obscenely slow code in. The standard prelude is flawed in many ways. Haskell 2020 will hopefully fix this. In the meantime: the recursion-schemes library might be of great use.
- Symmetry 9y agoSomething I've always wondered about Haskell is whether there is, in practice, rope for the compiler to optimize things away from the theoretical underlying implementation. For instance a C compiler is free to turn for (int i = 0; i < N; i++) { a[i] = 2*b[i]; } for (int i = 0; i < N; i++) { c[i] = 2*d[i]; } into for (int i = 0; i < N; i+=2) { a[i] = 2*b[i]; a[i+1] = 2*b[i+1]; c[i] = 2*d[i]; c[i+1] = 2*d[i+1]; } It seems like GHC ought to similarly be able to turn some foo :: [Int] -> [Int] into foo :: [(Int, Int)] -> [(Int, Int)] under the covers for a similar speedup as long as it can, as with the C compiler, handle the case of an odd length with a bit of extra code. There are limits to how much you can restructure a list that's exported outside the compilation unit (my Haskell knowledge is fuzzy here so that's probably not the right terminology). And if the list is being re-stitched in odd ways it might be a bad idea. But as far as I can tell it ought to be doable and useful in most common cases. Of course maybe it's already doing this in those cases where it can prove that it works and that's why Haskell performance generally is good but fragile.
- haldean 9y agoHaskell performs a very cool optimization called "stream fusion" to deal with a whole class of traversal optimization problems; stream fusion removes intermediate list results completely, and bundles a bunch of things that look like maps/reduces/filters into a single operation. Your example doesn't quite hit it, but a similar bit of code like: for (int i = 0; i < N; i++) a[i] = 2 * b[i]; for (int i = 0; i < N; i++) c[i] = 2 * a[i] + b[i]; t = 0; for (int i = 0; i < N; i++) if (b[i] % 2 == 0) t += c[i]; Would be rewritten to the equivalent of: t = 0; for (int i = 0; i < N; i++) if (b[i] % 2 == 0) t += 5 * b[i]; Lots more info on the GHC optimizations page: https://wiki.haskell.org/GHC_optimisations#Fusion https://wiki.haskell.org/GHC_optimisations#Fusion
- Symmetry 9y agoYes, stream fusion is super nifty. But can Haskell alter the size of the chunks it's sending through the stream? There was a recent HN thread about how GNU 'yes' is so fast thanks to buffering. Obviously too large a buffer would blow out the cache and stream fusion avoids that but a moderate amount of buffering so that you're neither working array by array nor element by element but cachable chunk by cachable chunk should be a happy middle. And with Haskell lists reduce pointer chasing as well.
- Gackle_Murderer 9y agoYou might like Idris. Not nearly as mature as Haskell, but definitely as powerful, with much more reasonability when it comes to performance, thanks to eager evaluation.
- yitzg 9y agoAs a Haskell practitioner in industry - for several years we have been creating technical documentation sites for large enterprise customers, providing fast semantic search for gigabytes of content serving millions of users - I can attest that this simply isn't true. We have found that reasoning about performance in a pure functional language is not harder than for any other kind of mainstream language. Except in the sense that it is different, so not all of your skills and intuition from other languages necessarily apply.
- thesz 9y agoSomething I found about going to Haskell from <any other language> is that Haskell provides many powerful abstractions without compromising on performance. Especially programmer's performance. If you do not need to write notoriously slow code in Haskell, just don't do it. Use libraries (containers and mtl especially great), keep state strict (a handful of bang patterns). I use Haskell's laziness with great success (look for paper "generating power of lazy semantics" it's fascinating; look "the essence of functional programming" for even more fascination). I use Haskell's flexibility with great success (how about beating C speed ten times? how about parallelization to all cores with couple lines of code?). It is really great language. BTW, Haskell's RTS reuse chunk type field which is needed for implementation of laziness for synchronization and it is great! This means that it can provide everything that Java and/or .Net provides and get away with twice as less overhead for it.