Live data from Hacker News

Sum Types in Julia and Rust

andreaskroepelin.de

61–70 of 70 posts

Re: Sum Types in Julia and Rust

#61
post #60
post #59

Earlier quoted context omitted.

Your concerns are not unwarranted. I've done several Rust projects and usually there really isn't any trade-off. If you want something open for extension, use a trait unless you can prove to yourself that you "can't". If it's closed or unlikely to be extended, use an enum. Think of it this way: In Rust you have a choice between open and closed (trait vs enum). In Java, you get open (interface) and that's it. Other la…

This is an excellent answer, thank you. I suppose the only remaining discomfort I anticipate is if you have an internal abstraction that might one day be exposed externally in a library. That refactoring doesn’t seem fun, but I suppose these things rarely sneak up on you.

> I suppose the only remaining discomfort I anticipate is if you have an internal abstraction that might one day be exposed externally in a library. That refactoring doesn’t seem fun, but I suppose these things rarely sneak up on you.

The place where this ends up happening most is around error handling. Crafting your public error types in Rust is an art form. Usually in Rust libraries, errors are implemented in enums. So whoever calls your API can see that it failed and then can match on the reasons it may have failed (or just wrap it in their own error type and bubble it up). If you're not careful, you can cause breaking changes in your API by doing something as simple as changing a dependency (your dep's concrete error type was wrapped inside one of your error variants).

I have somewhat mixed feelings on it, but Rust does allow us to mark enums with a special annotation that forces all matches on it to include a wildcard match (even if it matches all current variants explicitly). It is generally considered good practice to mark your error enums with said annotation so that you can add failure modes in the future without requiring a major version bump for just an extra error case. One could use the same annotation for any enum, of course.

Re: Sum Types in Julia and Rust

#62

Earlier quoted context omitted.

That's right. It's unfortunate that Rust calls them "enums", since as you noted that term already had a well-established meaning in other languages, and the concept that Rust calls "enums" also had several existing names (sum type, coproduct type, disjoint union, and, more generally, algebraic data type) in the literature and in prior languages. Language designers: stop changing the meanings of words! I know you mean…

> It's unfortunate that Rust calls them "enums" Rust calls them enum to be familiar to people coming from C-family languages, which is a large target. > since as you noted that term already had a well-established meaning in other languages Rust enums "degenerate" to a C-style enum (except typesafe) as it can be repr'd to a number and it's possible to select the discriminant (if there's no associated data). > the conc…

Can't edit but

> the sum type simply has a single constructor.

this should be "product" not "sum"

Re: Sum Types in Julia and Rust

#63

Earlier quoted context omitted.

That's right. It's unfortunate that Rust calls them "enums", since as you noted that term already had a well-established meaning in other languages, and the concept that Rust calls "enums" also had several existing names (sum type, coproduct type, disjoint union, and, more generally, algebraic data type) in the literature and in prior languages. Language designers: stop changing the meanings of words! I know you mean…

Rust enums are also enums: enum Colour { Red, Blue, Green, } is fine.

When you say "Rust enums are also enums", that's not true in general. What is true is that _some_ Rust "enums" (like your `Colour` example) can be represented as traditional enums. You don't prove a universal quantification with a single example.

Re: Sum Types in Julia and Rust

#64
post #3

I may be wrong, but I don't think this is what people mean by 'sum types'. I was expecting union types (e.g., https://www.typescriptlang.org/docs/handbook/unions-and-inte... or https://dotty.epfl.ch/docs/reference/new-types/union-types.h... ) I guess enums are a type of sum type, but seems this is more specifically about enums

> I may be wrong, but I don't think this is what people mean by 'sum types'. It is. > I was expecting union types (e.g., https://www.typescriptlang.org/docs/handbook/unions-and-inte... . or https://dotty.epfl.ch/docs/reference/new-types/union-types.h... . ) Union types are basically an anonymous form of sum types. In the same way tuples and structs / records are both forms of product types. Statically typed languages…

> Union types are basically an anonymous form of sum types.

This is false on multiple levels. First, being a sum type has nothing to do with the type being named vs. anonymous. What makes a sum type a sum type is that it's a categorical coproduct, whether you give it a name or not. Second, sum types are synonymous with _disjoint_ (i.e., tagged) unions, not unions. Consider the union of boolean with itself. The result would be equivalent to boolean, because union is an idempotent operation. Disjoint union, or sum, would give you a type with 4 values instead of 2.

> Technically enums (or more generally tagged unions, which is what Rust's enums are) is a superset of sum types as you can have multiple variants of the same type

That just how sum types work (have you ever wondered why they are called "sum" in the first place?). You're thinking of union types.

Re: Sum Types in Julia and Rust

#65
post #8

When I think of sum types, I like the categorical definition the best, which is that a sum A+B has two morphisms (i.e. constructors) Inl : A -> A+B and Inr : B -> A+B, with a simple commuting diagram[0]. Or in Rust, enum Sum { Inl(A), Inr(B), } Why do I prefer this definition? Well, category theory abstracts away irrelevant details, and sums have a "universal property" associated with them. Roughly speaking that mean…

[deleted]

Re: Sum Types in Julia and Rust

#66

Earlier quoted context omitted.

Simple way to think about it is plus. Sum plus sum increases, union plus union does nothing. 1. A union type is like int | double. It means the value is one of a set of possible types. And it's a true set: `int | int | double` is indistinguishable from `int | double`. 2. A sum type is a new type built from a list of other types, which assigns a 'tag' to each possible list element, like a Rust enum. The tags are not t…

I've always considered both the union and tagged union to be Sum types[0]. > Two common classes of algebraic types are product types (i.e., tuples and records) and sum types (i.e., tagged or disjoint unions, coproduct types or variant types). Edit: e.g. X | Y isn't so different from 'a' A | 'b' B [0] https://en.wikipedia.org/wiki/Algebraic_data_type

> X | Y isn't so different from 'a' A | 'b' B

Except for the fact that the union of a type with itself (X | X) doesn't add any extra values, while a tagged union can have distinct tags with the same data type ('a' A | 'b' A) and actually sums the possible values for each tag. They are equivalent only so long as the unions are disjoint with no overlap between the members.

Re: Sum Types in Julia and Rust

#67
post #3

I may be wrong, but I don't think this is what people mean by 'sum types'. I was expecting union types (e.g., https://www.typescriptlang.org/docs/handbook/unions-and-inte... or https://dotty.epfl.ch/docs/reference/new-types/union-types.h... ) I guess enums are a type of sum type, but seems this is more specifically about enums

This is just a difference between named and anonymous types, really. This is very similar to named functions vs lambda functions, both provide the same exact functionality but avoiding superfluous identifiers makes programming more ergonomic. More specifically sum types in Rust must also be tagged , meaning you need to explicitly construct and deconstruct them. This aspect is along the type-alias vs newtype axis whic…

> So yes, Rust enums and Typescript union types are both exactly sum-types…

As others have pointed out, untagged unions are not sum types because the union of a type with itself has no effect, whereas adding a type to itself yields twice as many possible values. Untagged unions can function as sum types when there is no overlap between the members, but not in the general case.

To illustrate the difference: You can construct every possible algebraic type as some combination of void (no values), unit (one value), sum (|A + B| = |A| + |B|), and product (|A * B| = |A| * |B|). This does not work if the sum type is replaced with an untagged union. You can't even get as far as constructing the equivalent of the boolean type; while |Unit + Unit| has two distinct values, |Unit ⋃ Unit| only has one value.

Re: Sum Types in Julia and Rust

#68
rust nightly

    #![feature(specialization)]
    #![allow(incomplete_features)]

    struct Solarian;
    struct Polarian;
    struct Centaurian;

    trait Greet {
        fn greet(&self);
    }

    trait Player {}

    impl Player for Solarian {}
    impl Player for Polarian {}
    impl Player for Centaurian {}

    impl Greet for (Polarian, T) {
        default fn greet(&self) {
            println!("Hi");
        }
    }

    impl Greet for (Polarian, Solarian) {
        fn greet(&self) {
            println!("Your star doesn't even consist of three separate stars? Pathetic!");
        }
    }

    fn main() {
        (Polarian, Solarian).greet();
        (Polarian, Centaurian).greet();
    }

Re: Sum Types in Julia and Rust

#69

Earlier quoted context omitted.

Rust enums are also enums: enum Colour { Red, Blue, Green, } is fine.

When you say "Rust enums are also enums", that's not true in general. What is true is that _some_ Rust "enums" (like your `Colour` example) can be represented as traditional enums. You don't prove a universal quantification with a single example.

All enums are expressible as Rust enums.

Re: Sum Types in Julia and Rust

#70

Earlier quoted context omitted.

I've always considered both the union and tagged union to be Sum types[0]. > Two common classes of algebraic types are product types (i.e., tuples and records) and sum types (i.e., tagged or disjoint unions, coproduct types or variant types). Edit: e.g. X | Y isn't so different from 'a' A | 'b' B [0] https://en.wikipedia.org/wiki/Algebraic_data_type

> X | Y isn't so different from 'a' A | 'b' B Except for the fact that the union of a type with itself (X | X) doesn't add any extra values, while a tagged union can have distinct tags with the same data type ('a' A | 'b' A) and actually sums the possible values for each tag. They are equivalent only so long as the unions are disjoint with no overlap between the members.

I think we're saying the same thing. I was saying it's like thinking of 'a' A as an X and 'b' B as a Y.
Post reply on HN