
Session Types for Rust - adamnemecek
https://github.com/Munksgaard/session-types
======
wcrichton
I taught this concept in my course on Programming Languages at Stanford. If
you’d like a more pedagogic introduction to both session types and their
implementation, check out the course notes:
[http://cs242.stanford.edu/f18/lectures/07-2-session-
types.ht...](http://cs242.stanford.edu/f18/lectures/07-2-session-types.html)

------
chrismorgan
I don’t believe that strict “session types” are a good fit for Rust: it’s
shoehorning a concept, simultaneously specialised (it’s for channels) and
generalised (as in “left” and “right”), into Rust in a way that _works_ , but
is not as pleasant as can be achieved with little effort. (There’s not much
actual code in session-types/src/lib.rs—most of it is interfaces and macros
because it made life hard for itself by being too generic.)

The underlying concept in Rust is basically a type-level enum (state machine),
encoding the variant (state) as a phantom type in a generic parameter; _that_
, you can express without the full baggage of what is called session types,
and with greater freedom.

The situation is similar to the Either enum with its Left and Right variants:
it was in the standard library well before Rust 1.0 because it was easy, but
it was eventually abandoned in favour of good semantics: most users should use
Result, and the rest should write their own two-variant enums. Similarly, if
you encode your state machine in Rust types yourself, you end up with clearer
and more obvious code.

The main thing this library does is get you the specific channel behaviour;
that may be useful in some situations, but you can implement the underlying
concept atop regular channels without much difficulty, and it gives you
considerably more freedom in the API.

Here’s a small macro library that I wrote a few years ago, with an example
that demonstrates this general concept: [https://github.com/chris-
morgan/phantom-enum](https://github.com/chris-morgan/phantom-enum)

I find myself wishing more and more often that Rust enum variants could be
used as types, and that type refinement could work like in TypeScript.

(I do realise that what I’m advocating is targeting a potentially different
and broader sector to session-types, but I maintain my position: doing it from
scratch yourself is fairly straightforward, normally easy and more powerful.)

You can achieve all of this with different types instead of one type with a
phantom enum type parameter, too; see the insanitybit article linked elsewhere
in these comments. The primary benefit of the phantom enum type parameter
approach is when you have methods that apply in multiple, or all, states, so
that you don’t need to duplicate them. Sometimes you want extra data in
particular states; you can achieve that in the type parameter approach by
making the enum _not_ phantom, and rather a struct with extra data, or you can
achieve that by just using distinct types. Which is most appropriate varies
case by case.

~~~
bsaul
Do you know of any programming language where this type of things (having
functions defined only for certain enum values, or anything equivalent) would
be doable properly, without hacking the typesystem ?

~~~
chrismorgan
Look for languages with _dependent typing_. Idris and Liquid Haskell are the
two that spring to my mind.

Other than that, TypeScript has decent support for the concept, though with
standalone functions rather than methods: you’d define your “variants” as
types (or interfaces), define the “enum” as the union of those types (e.g.
`Foo | Bar`), and then have your functions take either the entire “enum” type,
or specific “variant” types. In place of Rust’s `match`, you will then use
type guards to narrow the “enum” type down to one (or more, if you like) of
the “variant” types. See
[https://www.typescriptlang.org/docs/handbook/advanced-
types....](https://www.typescriptlang.org/docs/handbook/advanced-
types.html#type-guards-and-differentiating-types) for a bit of info.

------
reificator
If you're interested in this, you may want to take a look at this article as
well: [https://insanitybit.github.io/2016/05/30/beyond-memory-
safet...](https://insanitybit.github.io/2016/05/30/beyond-memory-safety-with-
types)

EDIT: Sorry, I remembered the site name and saw IMAP in the title, but ended
up clicking the wrong article. Updated the above link from
[https://insanitybit.github.io/2016/06/28/implementing-an-
ima...](https://insanitybit.github.io/2016/06/28/implementing-an-imap-client-
in-rust)

~~~
agumonkey
is it different from phantom types ? more general ?

~~~
staticassertion
I wrote that insanitybit post. It's unrelated to Phantom types.

A session type allows you to encode a state machine into your type system.
What this means is that, at compile time, you can validate that your code
can't transition into an invalid state.

~~~
agumonkey
But state machine and communication protocols.. aren't they isomorphic ?

~~~
staticassertion
Sure, feel free to read 'communication protocol' where I say state machine.

~~~
agumonkey
oh ok; I double-misread your above comment. My assumption was that phantom
types were also about encoding state machines in types and thus could work for
communication protocols too just like your session type article; hence the
question about the difference between the two.

~~~
staticassertion
Oh, yes, I see the miscommunication now. Phantom types can be leveraged in a
similar way, where a type (the phantom type that you are generic over)
represents that state. To answer your question - the end result of both
approaches is a session type with the same guarantees.

I've seen that approach used but I can't really comment too much on the
differences as I've never used it myself. I find my approach simple in
practice (I've shipped Python code that takes the same approach, sans affine
types) because it separates the types out.

So, essentially, I write my non-sesssiony struct with all of its methods, and
then I wrap that in the session types. This lets me easily drop into the non-
session type if necessary, and the sessions types act as thin wrappers. My
imap client worked the same way, if I recall.

The downside is likely boilerplate.

~~~
agumonkey
is the python code public ?

I toyed with phantom type in kotlin recently (toy examples); I'm curious how
it would be done in python.

~~~
staticassertion
It's not. The short answer is it's the same thing as Rust, except instead of a
compile time guarantee that your state isn't reused I have an assertion and
test coverage.

~~~
agumonkey
aight, thanks

------
larsberg
If you're interested in this topic, I'd highly recommend watching Stephanie
Balzer's sessions at the Oregon Programming Language Summer School on
“Session-Typed Concurrent Programming”. The lectures are available online
([https://www.cs.uoregon.edu/research/summerschool/summer18/to...](https://www.cs.uoregon.edu/research/summerschool/summer18/topics.php#Balzer)).

Her recent work (sponsored, in part, by Mozilla Research) really advances
session types and the ability to get termination/progress guarantees:
[http://www.cs.cmu.edu/~balzers/publications/manifest_deadloc...](http://www.cs.cmu.edu/~balzers/publications/manifest_deadlock_freedom.pdf)

~~~
YorkshireSeason
In what sense does this work really advance session types? Prima facie it
looks like yet another tiny delta on Honda's original work that cannot and
does not get around session types' core problem of data-dependent
communication topology. Let me quote from the paper (Page 22, Section 6):

 _" The typing discipline presented in the previous sections, while rich
enough to account for a wide range of interesting programs, cannot type
programs that spawn a statically undetermined number of shared sessions that
are then to be used."_

That has been the problem of this approach to typing message passing for
nearly 3 decades, and like all predecessor papers in this theory tradition, an
ad-hoc approach is proposed that makes core session types incredibly
complicated without solving the problem. In order to get a publication out of
this, a different problem is solved, a yet another Curry-Howard corresponce
for some small delta of Linear Logic is given.

------
twic
I am pleased to see that this was done without macros, even custom derives.
There are a couple of macros in the library to make some use cases easier, but
they aren't essential.

There is a tendency in the Rust community to reach for macros as soon as
anything a bit complicated or verbose comes up. I think this most often ends
up sacrificing true understandability on the altar of a slick surface syntax,
and it doesn't help us write or understand well in the long run.

------
syspec
I never understood the idea of shortening variable names a few characters.
Recv vs Received, especially when there’s a also a Rec type mentioned in the
reader as well.

It’s not like Send, was shortened to Snd

~~~
userbinator
Read (or indeed, write) a lot of code and you'll quickly realise why. It's the
same reason acronyms and abbreviations exist in human languages.

Also, "recv" and "send" are both 4 characters, which is nice.

~~~
nitrogen
These days people type the first four letters and rely on IDE autocomplete to
finish the other 42.

------
choeger
Well. Rust is about to turn into Haskell 2.0

~~~
steveklabnik
You don’t need that powerful of a type system to do this kind of thing; Rust
is still pretty far behind Haskell in terms of raw power.

~~~
emn13
I don't think it's helpful to talk about "raw power" in a comparison like
this; clearly there are significant type system features Haskell lacks - so
this isn't a question of a simple superset.

And in any case; what does raw power really mean?

The two languages appear to have fairly widely different aims, and both see
type-system cleverness as a boon, so it should come as little surprise that
they don't all have the same tricks.

~~~
steveklabnik
There’s only one type system feature that Rust has that Haskell doesn’t, and
Haskell will have it soon. Yet there are many features that Haskell has that
Rust does not, and Rust is not getting that stuff any time soon. That’s what I
mean by raw power; it is very close to a subset relationship.

~~~
emn13
Didn't know about
[https://ghc.haskell.org/trac/ghc/wiki/LinearTypes](https://ghc.haskell.org/trac/ghc/wiki/LinearTypes)
until now; I'm assuming you're referring to that?

Neat!

------
emilfihlman
>Using this library, you can implement bi-directional process communication
with compile-time assurance that neither party will violate the communication
protocol.

No, you cannot. "Assurance of not violating communication protocol" and
"process communication" together are an oxymoron. If you do this, you deserve
all the possible failures that come along with it.

Please don't write infrastructure code that depends on "assurances" instead of
checking and validating communications.

~~~
myWindoonn
I wish I knew how to get this point across to people. The way I think of it is
that a type-checker which validates a single program at a time isn't capable
of proving certain facts which hold when access to filesystems or networks are
involved.

~~~
codebje
All validation is only performed up to an assumed platform.

There's usually little value in worrying too much about how a real platform
won't correspond precisely to that assumed platform, since the goal of
validation is to remove a class of errors, not all errors.

~~~
rocqua
I think OP's point is that getting these assurances doesn't mean you can stop
checking for errors that come from outside the platform.

------
asimpletune
I’m not sure if I’m misunderstanding the purpose of session-types, but it seem
that this a lot like finagle but for rust?

Session type is basically just a finagle service, etc...

Or are they more like go channels?

~~~
steveklabnik
Session types are more like “encode a state machine in your type system so
it’s impossible to mess it up”. They don’t inherently have to do with network
services, though since network services follow protocols that can be
represented as state machines (think “the S in REST”), they’re a good
application of session types.

That said, it looks like this library is not purely about session types, but
applying them to channels. (Which are go-style, yes.)

~~~
agumonkey
are they used in non academic contexts ?

~~~
Munksgaard
I'm one of the authors of the original paper/repository.

Part of the motivation behind our work was to try and apply it to the Servo
project (Mozilla's experimental browser engine, of which parts have already
been integrated into Firefox), which is highly parallelized. They had issues
with their internal communication because of their expansive use of threads,
and we wanted to see if session types could be applied to fix that.
Unfortunately, Servo is a rapidly moving target, so while we did manage to
convert some parts of the project to use our session types library, we never
got around to finishing the work, and now I believe that they've found
different solutions to their challenges.

With regards to your first question, this library is solely for in-process
communication. The concept could be expanded to cover inter-process
communication as well as communication over the network, and actually has been
in other similar session type projects, but focusing on in-process
communication allows us to make stronger guarantees.

~~~
agumonkey
Very nice

