Mapping Tagged Union types to other languages?

Rust

I have not tried Rust yet, but I know it has tagged unions, so I thought I should investigate:

The example given there is:

enum Animal {
    Cat { weight: f32, legs: usize },
    Dog { weight: f32, legs: usize },
    Monkey { weight: f32, arms: usize, legs: usize },
    Fish { weight: f32, fins: usize },
    Dolphin { weight: f32, fins: usize },
    Snake { weight: f32, fangs: usize }
}

Not a gazillion miles away from how it looks in Elm. So I guess my content model in Rust would look like:

enum Content {
  Text { uuid: string , title: string },
  Markdown { uuid: string , markdown: string },
  Figure { uuid: string , markdown: string , title: string , imageId: Option<string> },
}

P.S. I think you can also leave a trailing comma in Rust, and with the code layout as above, it means you can easily swap the positions of lines in an editor using CTRL+arrow up/down. I sometimes wish that first and last lines in syntax lists in Elm were not special.

As far as I know, there is no default. The common JSON encoder/decoder Poison will reject a tuple as input, since it cannot be injectively encoded to JSON (and back).

This is an interesting question though; I’ll ask the Elixir community for more input on this.

EDIT: Elixirforum topic

1 Like

Related to this discussion is this great set of answers on the Software Engineering StackExchange, about representing Tagged Union types (AKA sum types) in OOP languages:

That is interesting. The mapping of List to C# given there is:

interface List<A> {
    B Match<B>(B nil, Func<A, List<A>, B> cons);
}

class Nil<A> : List<A> {
    public Nil() {}
    public B Match<B>(B nil, Func<A, List<A>, B> cons) {
        return nil;
    }
}

class Cons<A> : List<A> {
    private readonly A head;
    private readonly List<A> tail;
    public Cons(A head, List<A> tail) {
        this.head = head;
        this.tail = tail;
    }
    public B Match<B>(B nil, Func<A, List<A>, B> cons) {
        return cons(head, tail);
    }
}

So to do a pattern match, you pass in a pair of functions, and depending if its a nil or cons one of the functions gets called.

I have used this style in Elm, where I had an opaque type, so the consumer could not pattern match on it.

In Elm this would look like:

module OpaqueList exposing (List, match)

type List a
 = Nil
 | Cons a (List a)

match : b -> (a -> List a -> b) -> List a -> b
match nilFn consFn xs = 
  case xs of
    Nil -> nilFn
    Cons head tail -> consFn head tail
1 Like

Nice! Is the type you have over here correct, though?

I’d expect the nilFn to only have one argument (the empty list) and the consFn to have two (the head and the tail). Shouldn’t it be:

match :  (List a -> b) -> (a -> List a -> b) -> List a -> b

?

The empty list is always the same [] so I just left the argument off. nilFn isn’t really a function, its a constant, so perhaps I should just have called it nilVal.

1 Like

This topic was automatically closed 10 days after the last reply. New replies are no longer allowed.