
Haskell and Rust - pmoriarty
https://www.fpcomplete.com/blog/2018/11/haskell-and-rust
======
kaoD
Kinda surprised not to see HKTs[0] mentioned which, AFAICT, is what really
sets apart Haskell's from Rust's type system. Without HKTs you can't express a
Functor typeclass/trait[1].

[0] [https://github.com/rust-lang/rfcs/issues/324](https://github.com/rust-
lang/rfcs/issues/324)

[1]
[https://www.reddit.com/r/rust/comments/2av5tv/why_does_rust_...](https://www.reddit.com/r/rust/comments/2av5tv/why_does_rust_not_have_a_functor_trait/)

~~~
zozbot234
Aren't Generic Associated Types (in the pipeline for Rust) broadly equivalent
to HKT as found in Haskell?

~~~
kaoD
I'm kinda bad at these things so I'm not sure. GATs are a form of higher-
kinded polymorphism but I'm not sure if they're really equivalent to HKTs.

GATs are still missing anyways. Not sure how close they're to being
implemented.

~~~
pcstl
Not particularly close. The feature seems to be progressing slowly, but very
deprioritized (which is understandable - Rust has a bit of a reputation for
being hard to learn and too feature-heavy, so it's natural for them to focus
on stabilizing the language instead of adding new features)

~~~
steveklabnik
They’re needed for async fn in traits, which feels like a pretty natural
extension to async fns, so there’s a really good motivating case for them.

------
pron
I think the comparison dwells on superficial similarities. Rust's type system
is absolutely necessary to ensure a certain kind of safety under the
requirements Rust adopted for itself in order to address certain domains (like
no tracing GC), namely a guarantee of no undefined behavior, common in those
domains. This safety (or something very close to it) exists in every so-called
"managed" language anyway, be it Python, Java or Haskell, regardless of their
type system, so the goal of the type system in Rust and in Haskell -- at least
as far as safety goes -- is obviously very different.

Moreover, such safety is not at all what is normally considered "correctness"
in the field of software correctness; the term normally refers to _functional_
correctness, i.e. that the program satisfies the specification, not that it
doesn't have undefined behavior. On that front, there seems to be no evidence,
nor even hints, that either Haskell or Rust do better, or worse, than other
languages developed in recent decades. It's a myth, albeit one that's often
repeated in some circles.

That's not to say that type systems cannot be hugely valuable, and sometimes
even necessary. For Rust, the type system is what ensures undefined-behavior-
safety. In other languages it has many other advantages as well. Some type
systems are even intended for correctness. But Rust's type system, and
certainly Haskell's are not. Both are interesting though expressively "weak"
type systems, of the kind that has not been found to be correlated with higher
correctness. So focusing on _that_ as the point of comparison completely
misses the mark.

~~~
NOGDP
> the term normally refers to functional correctness, i.e. that the program
> satisfies the specification, not that it doesn't have undefined behavior

A program that has undefined behaviour is not correct, unless the spec has
undefined behaviour (why would it?).

> On that front, there seems to be no evidence, nor even hints, that either
> Haskell or Rust achieve better correctness (or the same correctness more
> cheaply).

I don't know what you mean by evidence, but Haskell has a stronger and more
expressive type system than say Java, it doesn't have null pointers, it has
referential transparency, and simpler, safer concurrency and parallelism
builtins. The fact that you can make more general assumptions about pieces of
code which you don't necessarily fully understand makes writing safer and
correct code easier.

> But Rust's type system, and certainly Haskell's -- both interesting but very
> expressively "weak" type systems -- have not been found to be correlated
> with higher correctness.

Are you saying there have been studies that show Haskell's type system has not
been correlated to higher correctness than say Java or Python, or are you
saying you are unaware of any such studies? Also, Haskell's type system is
certainly more expressive and strong than Java's or Python's.

~~~
jcranmer
> unless the spec has undefined behaviour (why would it?).

Because maybe you're specifying a partial function. The code to handle the
special cases of invalid inputs can sometimes have dramatic effects on code
size and performance, sometimes too much to bear for code that should never
run in the first place; allowing for garbage output to be produced for garbage
input instead of a result that says "this is garbage input" could well be
acceptable.

Furthermore, there are some places where we don't actually know how to specify
the intended behavior in the first place. Data races are perhaps the best
example here; we have yet to find a satisfying specification for data races
that is more constraining than "anything goes," and the two most well-known
attempts (Java and C++) have both been widely regarded as failures. But
pointer aliasing is another area that proves surprisingly difficult: if
requiring pointer provenance isn't acceptable, then trying to come up with a
reasonable specification is surprisingly difficult.

~~~
NOGDP
> The code to handle the special cases of invalid inputs can sometimes have
> dramatic effects on code size and performance, sometimes too much to bear
> for code that should never run in the first place; allowing for garbage
> output to be produced for garbage input instead of a result that says "this
> is garbage input" could well be acceptable.

That seems solvable by refactoring your types a little. Also, some languages
do not allow partial functions - such as total functional programming
languages.

> we have yet to find a satisfying specification for data races that is more
> constraining than "anything goes," and the two most well-known attempts
> (Java and C++) have both been widely regarded as failures.

What about nested data parallelism[1] or pure parallelism such as in Haskell?

[1]
[https://www.classes.cs.uchicago.edu/archive/2016/winter/3200...](https://www.classes.cs.uchicago.edu/archive/2016/winter/32001-1/papers/nepal.pdf)

------
siscia
Quick meta question.

Does this kind of article/content works for getting consultanting clients?

I am not sure I would pick them if I ever need a rust project done, but maybe
I am not the target audience.

~~~
hardwaresofton
Well they're primarily a Haskell shop IIRC so it makes sense?

Also making content for getting clients absolutely works, it's the same
concept behind big/small company engineering blogs.

------
mlthoughts2018
The idea of ubiquitous need for significant enforced correctness assurance is
one of the worst things in software. Correctness is a resource like time or
memory. Some applications need extremely high resource of correctness, some
applications don’t (and can even fail badly, usually due to delay or rigidity,
if they have an over-provisioning of correctness).

The presumption that every type of project should fundamentally begin with
adherence to patterns guaranteeing high resource usage of correctness is a bad
type of premature optimization / premature abstraction.

Rust & Haskell are great tools in the rare cases you do need extreme
correctness. But in my experience most situations do not benefit from spending
that cost (the ways Haskell & Rust enforce program structure is a huge cost)
to get correctness, and you can get approximately just as much correctness
with pretty low-touch unit & integration tests in most languages, without
heavy costs of roundtrip compile times, static typing, forced (instead of
selective) management of mutability and ownership, and so on.

~~~
FullyFunctional
I doubt it will surprise you, but many disagrees with you. Using Haskell isn't
a burden - on the contrary, it's far easier to get stuff working correctly.
The type checker looks over your shoulder and stays out of the way until you
make a blunder. It's not uncommon for things to work as soon as they pass the
compiler. I work in C every day and this is not the typical experience there.

I'm hugely interested in Rust, but I'm disappointed that they felt the need to
rename all concepts and adopt an ugly syntax. I assume it was done to attract
the C++ crowd.

~~~
nimih
Additionally, at least in (GHC) Haskell, you can recover most of the
flexibility typical of dynamic languages via use of undefined values, partial
functions, the PartialTypeSignatures extension, and the defer-type-errors
flag. Personally, I've found these tools to be very useful when prototyping,
exploring/experimenting in a REPL, or beginning a refactor, and the process
feels very akin to development without unit tests (or tests turned
off/ignored/whatever), especially in the progression of incrementally adding
back compile-time-checks and watching my mistakes surface.

~~~
verttii
Great advice, I'll try these features out while prototyping on Haskell.

------
The_rationalist
I wonder if typescript has more shared features with haskell than has rust (at
least for the type system).

~~~
kaoD
It's actually farther than Rust compared to Haskell.

~~~
The_rationalist
Why? It has union, intersection, recursive and conditional types.

~~~
kaoD
Adding to sibling comment: structural typing vs traits is kind of a game
changer. The way I use them is very different. TS still feels like OOP (since
it's just a formalization over JS's implicit native typing). Rust and Haskell
feel mostly the same to me wrt how I think about my code (abstraction through
capabilities).

Intersection types are very different. In Haskell/Rust it means "this generic
type implements traits A, B and C". In TS again it's about structure "it has
at least the properties shared by types A, B and C". This means the
intersection of A, B and C is all of A, B and C in Rust/Haskell, while in TS
it can be a completely different type that only has their shared props, which
makes no sense in Haskell/Rust since traits/typeclasses are disjoint (their
intersection is always empty).

All of them have recursive types (though in Rust the recursion must be boxed
explicitly to make the type sized) and union types.

Neither Rust nor Haskell have conditional types. This is really cool from TS:
types are functions! Rust/Haskell are purely nominal.

~~~
The_rationalist
Interesting, but I don't really get the difference about intersection types.
_(abstraction through capabilities)_ What differentiate an intersection type
from a mixin? Secondly I get it that intersection types in typescript are more
permissive but they are mostly functionaly the same, aren't they?

~~~
kaoD
Traits are similar to mixins, this SE answer explains it better than I could:

[https://stackoverflow.com/questions/925609/mixins-vs-
traits](https://stackoverflow.com/questions/925609/mixins-vs-traits)

Sorry about the intersection confusion, I mixed up things.

Intersection generic types are types that adhere to a set of traits (all of
them, completely). Since traits don't overlap (even if their method names are
the same) their intersection requires them to be defined explicitly.

E.g. if you have an interface A { length: number } and an interface B {
length: number, size: number } in Rust/Haskell you need to implement both
explicitly for your type, while in TS your type just has to adhere to B's
shape to be both.

TL;DR: it's mostly about the "structural vs nominal" \+ "traits vs
interfaces".

But yeah they're functionally the same indeed, sorry for the confusion.

------
timClicks
The example in the polymorphism example feels strange to me. Why convert from
Option to Result?

~~~
zoul
IMHO conversions between various “error types” are quite common when gluing
together different APIs. Like when you are chaining a handful of Result-
returning calls and there’s one in the middle that returns errors using
Option.

~~~
coolsunglasses
This is exactly right. We habitually lift `Maybe`/`Option` values into a more
explicit and comprehensive `Either`/`Result` type in our programs. It's pretty
common to have an error type hierarchy in the program too, so you could find
yourself chaining from one `Either e` to another. There are libraries in
Haskell and Rust for regularizing/code-gen'ing these patterns.

------
martinez099
We see everywhere influences from Haskell

