14 ms·
Rust vs. Haskell
- deleted 4y ago[deleted]
- sshine 4y agoGreat article. Rust struct and Haskell records work pretty much the same way, too: // Rust Item { name, price } Item { name: name, price: price } match item { Item { name, .. } => todo!(), } corresponding to -- Haskell Item { item, price } Item { item = item, price = price } case item of Item { name, .. } -> undefined Haskell has some opt-in flexibility wrt. packing and unpacking of field names [1]. [1]: https://ghc.gitlab.haskell.org/ghc/doc/users_guide/exts/record_wildcards.html https://ghc.gitlab.haskell.org/ghc/doc/users_guide/exts/reco...
- masklinn 4y agoFWIW if you’re just “unpacking” a structure `match` / `case` is unnecessary syntactic overhead, you can just use a regular let: // rust let Item { name, .. } = item; -- haskell let Item { name } = item in … (Note: the haskell version requires NamedFieldPuns, or RecordWildCards for something like Rust’s version)
- naasking 4y agoThere's one important difference: aren't Rust structs unboxed by default, where in Haskell they are boxed by default?
- ghkbrew 4y agoThis is true. Though I believe GHC is pretty aggressive about unboxing (when posible), but don't quote me on that. Another possible gotcha is that by default Haskell records introduce a named accessor function into the surrounding scope. So defining two records with a `name` field next to each other is an error
- Joker_vD 4y agolet nums = take 10000000 naturals print $ (sum nums, length nums) > Because the nums list is used for both sum and length computations, the compiler can’t discard list elements until it evaluates both. Now that makes me wonder, if I write something like print $ (sum (take 10000000 naturals), length (take 10000000 naturals)) will it run in constant memory? I think it ought to, but are there mechanisms in GHC optimizer to prevent extraction of CSEs that would cause huge increases in memory consumption?
- endgame 4y agoThe solution to this is a package like https://hackage.haskell.org/package/foldl https://hackage.haskell.org/package/foldl , which lets you fuse multiple traversals of a lazy list into a single pass and get the laziness right.
- hgsgm 4y agoIt's both solvable and easy to accidentally have blowups by accident.
- sbergot 4y agoyeah sadly this summarize my haskell experience. I would hate to do code reviews in haskell.
- armchairhacker 4y agoThis article paints Rust and Haskell as very similar languages, but when I saw the title I was thinking about how they are fundamentally different. Rust is technical and exposes/requires you to understand the reality of computation, while Haskell hides it away and lets you program in a system based on lambda-calculus (you don't even have strict order of evaluation!) Interesting to see that they are very similar regardless.
- thedataangel 4y agoQuite a few of Rust's features were borrowed from or inspired by ones in Haskell - albeit often modified to be more suitable for systems programming (and systems programmers!). Most Haskellers I know are quite fond of Rust :)
- tgv 4y agoIMO, that's too superficial to compare them. So what if both have a bunch of similar looking constructs? Rust is imperative, and sequential, and nearly everything is mutable, hence the borrow checker. That makes programming in Rust rather different.
- kzrdude 4y agoHaskell controls mutable data by pervasive immutability. Rust controls mutable data by the borrow checker system: allow sharing OR mutation but not both at the same time.
- dgb23 4y agoI get what GP is implying. The way you program (and think) is entirely different if you are working with immutable data structures and functions versus safe mutability.
- kilgnad 4y agoRust is immutable by default. You have to deliberately opt in for mutability. When you don't opt in to mutability the programming styles are very similar at a basic level. First the type system is very similar with the capability of building sum and product types with recursive values. Second pattern matching over sum types is also very very similar. Rust is like the bastard child of C++ and haskell.
- gilmi 4y agoVery informative. Thanks!
- vlovich123 4y ago> if we have a map and want to do some operation on a slightly modified map, we can have a value that keeps the old map but also works with the new map (without much performance cost). What black magic is this? Is the article just glossing over the cost of a copy or does Haskell do something weird here to avoid the copy while retaining both versions?
- ghostwriter 4y ago> What black magic is this? https://en.wikipedia.org/wiki/Persistent_data_structure https://en.wikipedia.org/wiki/Persistent_data_structure
- rkangel 4y agoI don't know about Haskell particularly but the underlying implementation of data structures in pure languages is sometimes more complicated to enable this sort of thing. For example, the new updated copy of the map may refer to the "old" map for most of its data, thereby making it cheap to have both.
- api 4y agoSo copy-on-write basically?
- hgsgm 4y agoNo, there is no "write" because the old value never chnages. It's "create an overlay on write"
- deleted 4y ago[deleted]
- arc-in-space 4y agoCopy-on-write generally refers to copying everything on every write; this is a persistent data structure, which can do updates while only copying a small part of itself. Same purpose as COW, but more efficient for larger data structures we want to make lots of small updates on.
- qwertox 4y agoAbout Haskell's function type annotations: "But it usually results in a warning, and adding a type signature is a good practice." I prefer Rust's way of doing this. When you're a beginner it ensures that you're passing and returning the proper type to and from the function, you kind of have the function as a guard which ensures that you're using the correct types.
- ghostwriter 4y ago> it ensures that you're passing and returning the proper type to and from the function You don't need it for all function declarations though, there are many trivial cases where type signatures don't add value. Consider that you probably would prefer most of the variables within a function to be inferred for you by the compiler. A similar thing could be said about the most of the functions within a given module. Important interface methods can be defined explicitly though, for extra clarity and self-documenting purposes.
- bryanlarsen 4y agopub functions: should be explicit module functions: if I chose, I'd let them be derived, but I'm relatively indifferent inner functions: should be allowed to be derived, IMO. They're very infrequently used so this choice doesn't have much impact.
- nicoburns 4y agoYou can use closures as functions with type inferred signatures
- bryanlarsen 4y agoClosures capture variables so have significant differences compared to inner functions. Capture can affect lifetimes so the difference is significant in Rust.
- 4y ago
- lvass 4y agoCan someone explain why/whether a language like Rust requires mutability? Wouldn't it be better if we could just have the compiler guarantee that functions marked as such are doing TCO, so algorithms would look better and had the full benefit of persistent data structures?
- andrewflnr 4y agoRust is designed for maximal performance, so it's designed to be close to machine language, where mutability is an unavoidable reality. That's pretty much the whole story.
- masklinn 4y ago> Can someone explain why Rust has mutability? Because it’s target domain requires reliable efficiency. > Wouldn't it be better if we could just have the compiler guarantee that functions marked as such As such what? > are doing TCO TCO is worthless, and Rust does not have TCE, nor does it care about TCE. > so algorithms would look better and had the full benefit of persistent data structures? Rust does not significantly care for persistent data structures, there are crates which provide them, but not the standard library. Rust is not a functional language.
- rom-antics 4y ago"Worthless" is a bit strong. The Rust devs want to add TCO/TCE, but there are still problems left to solve. The "become" keyword is already reserved in anticipation of guaranteed tail calls being added. https://github.com/rust-lang/rfcs/issues/2691 https://github.com/rust-lang/rfcs/issues/2691
- 29athrowaway 4y ago"What do you use Haskell for?" Most important question about Haskell
- Martinsos 4y agoI know it is common to think that Haskell is used only in academia and side/weird-projects, but there is a decent amount of companies using Haskell - e.g. we use Haskell in production for developing a DSL / web framework for building web apps (https://github.com/wasp-lang/wasp https://github.com/wasp-lang/wasp)! I participated teaching students Haskell on my alma mater this year and "what can Haskell be used for" was a common question, with genuine expectation that the answer will be it is limited to only specific use cases. I would answer that it can be used anywhere where languages like Java, C#, Go, and similar can be used -> it is a general programming language that uses garbage collector! And while somewhat harder to learn due to abstractions that we are all not used to, it is a delight to express business logic in it once you get to know it well. The biggest factor for deciding if Haskell is a good fit for the problem is probably ecosystem support -> are there enough libraries and tools to support efficient development in a specific problem domain. In our case, we are building a compiler/transpiler, and Haskell is well-known for great support in that area, so it was a no-brainer. We were actually also considering Rust, but we just had no need for that level of memory control and rather decided to go with language where we don't have to think about that (Haskell).
- OJFord 4y ago> The biggest factor for deciding if Haskell is a good fit for the problem is probably ecosystem support And hiring right, or being prepared to train/let new hires climb up a steeper learning curve than hiring someone with Python experience for a Ruby app say. Rust has reached that critical mass I think, got past the chicken/egg issue of experienced people to hire & companies interested in hiring them (to work on a rust codebase). Against the odds I think, there are plenty of languages you hear about similarly up and coming that haven't (D, Zig) or have only in a niche (F#, Swift, Kotlin - the last two I include mainly because I'm thinking Go could so easily have gone the same way, just been the one Google pushed for K8s plugins and GAE applications, not used generally as it is despite being a general-purpose language).
- ianpurton 4y ago> As a result, both languages have steep learning curves compared with other languages. Well yes and no. In a way the features such as immutability and algebraic data types are things you should know about as a software developer even if your current language means you can't use them at the moment. My 16 year old son has learned Rust coming from a python at school background and is now writing small games in the bevy game engine.
- surement 4y ago> yes and no Having to understand monad transformers or another kind of effect system to get anything working is a heavy load that's unnecessary in other languages.
- cosmic_quanta 4y agoYou need to use the IO monad to get anything working, true. Monad transformers? Not at all. I'd say knowing how to use monad transformers is the barrier between intermediate and expert Haskell programmers.
- jeremyjh 4y agoI'd say using transformers are more the barrier between beginner and intermediate. Its not impossible to do serious work without them but I'm not sure its worth doing.
- ghostwriter 4y ago> Its not impossible to do serious work without them but I'm not sure its worth doing. How do you think the folks on Java and C# find their work worth doing on their end in that case?
- jeremyjh 4y agoBecause they aren't using Haskell? Haskell more or less creates all the problems that transformers solve.
- satvikpendem 4y agoSurprised to see no mention of higher kinded types. Rust has generic associated types but they're not exactly the same and are more limited in their functionality than true HKTs.
- Hirrolot 4y agoHKTs in Haskell are quite castrated, too. Haskell only lets you define HKTs via data type declarations, while, in general, HKTs can be considered just functions "one level up" -- instead of operating on the term-level, they operate on the level of types. The type checker thus becomes undecidable for a Turing-complete language, since you need to be able to perform arbitrary computation on types during type checking. Not a huge problem for Rust though, since its type checker is already undecidable.
- ilovecaching 4y agoAs a systems programmer any mention of the word Haskell next to the word Rust sends shivers down my spine. Haskell is the king of yak shaving abstractions and is almost the polar opposite in all the ways that made C the most practically successful language on the earth. I do not want abstractions where they aren't needed. I want control, simplicity, a clear correspondence between what I'm writing and what logical assembly I'll be generating (logical, not physical). Most of all I want my code to be stupidly clear to the next person reading it. Systems programming isn't like writing a Java app, the domain is complicated enough that there's no room for abstraction astronauts to create problems where there are none. I am still very wary of Rust. I have used it and will continue to use it, but it still teeters on being too complicated to be useful in the same way as C.
- Ericson2314 4y agoI've helped write some very abstract combinator-y Rust that compiled down to an embedded program that runs with only 4K RAM. Get over it. > and what logical assembly I'll be generating The problem with Rust and C is not worrying about what assembly I'll get, it is that one has very little control over the layout of the stack. It's the last implicit data structure and that's a PITA for highly resource constrained programming.
- ilovecaching 4y agoWith all due respect, writing one program with a small memory footprint doesn't disprove my point. I'm referring to writing very large, complex programs that interact directly with hardware - kernels, boot loaders, firmware, all of which must be as clear as possible. And I say this with many years of experience dealing with other companies badly written device drivers and firmware. I absolutely need to worry about what assembly I get. I am often checking my assembly or looking for optimizations in my assembly. And I'm doing this across multiple architectures.
- Ericson2314 4y ago> I'm referring to writing very large, complex programs Then the power of good abstractions should be all the more useful! > that interact directly with hardware OK if you are using various privileged instructions, writing things with weird calling conventions like interrupt handlers, etc. Then the assembly matters. But, and this may sound heretical, this stuff is the boundary of the lower level code; its interface with the hardware. For the interior, the precise assembly once again doesn't matter. > And I say this with many years of experience dealing with other companies badly written device drivers and firmware. There is a lot of crap low level code out there, yes. And I would say a chief mistake of crap code is not properly respecting layers of abstraction. The low level world needs more http://langsec.org/ http://langsec.org/ where we precisely and mechanistically document the interface, rather than half-assing it with reference implementations. Just as user space weird instructions get intrinsics exposed to spare the user from writing inline assembly, libraries like https://docs.rs/x86/latest/x86/ https://docs.rs/x86/latest/x86/ should be maintained the device manufacturer or ISA consortium. They should be unit-tested with an emulator too. We properly do all this legwork to fix the foundation, and the rest of the low-level software will practical write itself, compared to today, and be accessible to a much larger pool of programmers.
- jeremyjh 4y agoWhen I first learned Rust I'd been writing Haskell in serious side projects for four years, and had used C++ at work as well as a game project and was quite familiar with it (I'm not sure C++ is ever comfortable). Practically every feature in Rust other than the borrow checker was thus already known to me, and I still found Rust a quite significant learning curve, though to be fair I probably also learned about some things in C++ that looked safe but weren't.
- deleted 4y ago[deleted]
- Hirrolot 4y agoOne important thing Rust and Haskell have in similar is the nearly complete absence of an endeavour to unify things. As more fancy type system features get added, both languages become quite complex, thus increasing the learning curve and compiler complexity. This is quite aptly expressed by the recent paper on dependent types [1]: > Previous versions of Haskell, based on System Fω , had a simple kind system with a few kinds (, → * and so on). Unfortunately, this was insufficient for kind polymorphism. Thus, recent versions of Haskell were extended to support kind polymorphism, which required extending the core language as well. Indeed, System FC↑ [30] was proposed to support, among other things, kind polymorphism. However, System FC↑ separates expressions into terms, types and kinds, which complicates both the implementation and future extensions. Later in the paper, they show how some of the most recent "fancy" features of Haskell can be achieved in a more economic framework based on first-class types. Unfortunately, systems programming (as in C/C++) puts strict constraints on a programming language, and currently it's not quite clear what's the best way to integrate dependent types with systems programming. In the coming years, I expect to see some forms of castrated dependent types tuned for systems programming (e.g., types dependent only on indices). [1] https://www.researchgate.net/publication/308960209_Unified_Syntax_with_Iso-types https://www.researchgate.net/publication/308960209_Unified_S...
- zozbot234 4y ago> currently it's not quite clear what's the best way to integrate dependent types with systems programming. Dependent types dispense with the phase separation between compile-time and run-time code, which is inherent to system languages. So you can easily have dependent types in a systems language as part of compile-time evaluation, but not really as a general feature. It would work quite similar to the "program extraction" feature in current dependent-language implementations, which does enable improved type checking because you can express arbitrary proofs and check them reliably as part of compilation.
- Hirrolot 4y agoYeah, the main problem with dependent types being used in low-level programming is that they're now able to perform arbitrary computation. That's why I think castrated dependent types might do the trick, since if you accept only integer indices as parameters to types, then you can boil down type checking to constraint solving without actually performing arbitrary computation.
- phplovesong 4y agoWould not a better comparison be Ocaml vs Rust? In the end Rust WAS written in OCaml, and is heavily influenced by it.
- Hirrolot 4y agoThat would not be a comprehensive comparison, but: - OCaml is definitely tailored for more "high-level" tasks, such as writing a programming language or a theorem prover (there are many of them in the OCaml world, and even Rust was initially written in OCaml, as you've mentioned). OCaml has a GC, which might be a problem under certain circumstances. - Rust has a far better ecosystem despite being a younger language. You can just compare the number of packages on crates.io and OPAM. - OCaml _sometimes_ has some more fancy type features, such as functors (modules parameterized by other modules), first-class modules, GADTs (Generalized Algebraic Data Types), algebraic effects, and the list could go on. It doesn't have type classes or an ownership system though. - OCaml is more ML-like, while Rust quite often feels C-like. For example, you have automatically curried functions in OCaml and the omnipresent HM type inference. - OCaml has a powerful optimizer called Flambda [1], which is designed to optimize FP-style programs. Having written some code both in OCaml and Rust, I can say they are in a lot of aspects quite similar though. They both take a more practical POV on programming than Haskell, which affects language design quite evidently. [1] https://v2.ocaml.org/manual/flambda.html https://v2.ocaml.org/manual/flambda.html
- wyager 4y agoEarly alpha/beta rust docs mentioned Haskell all the time as a point of comparison, but I don't recall them ever mentioning OCaml. Also, Rust's idiomatic type system usage is way closer to Haskell's than it is to OCaml's (with typeclasses, no polymorphic variants, no module functorization, etc.)
- fallingfrog 4y agoI love the ideas behind both these languages, even though in their approach they are complete opposites.
- mncharity 4y agoA recentish /r/haskell discussion[1] had some brief thoughts. [1] https://old.reddit.com/r/haskell/comments/zk2u6k/what_do_haskellers_think_about_rust/ https://old.reddit.com/r/haskell/comments/zk2u6k/what_do_has...
- karmakaze 4y agoAnother recent article that compared the expressiveness of several languages for a particular project including Rust and Haskell[0] as well as C++, Python, Scala and OCaml. That comparison was for a complex task undertaken by adept users of each language. The variance was within a +/- factor of 2 with an exception of one team which made choices that led to much more typing. [0] https://news.ycombinator.com/item?id=34684325#34688865 https://news.ycombinator.com/item?id=34684325#34688865