Is Enum/ADT or Variant preferred when consuming a library?

-- ADT flavor
data Fruit
  = Apple
  | Banana
  | Cantaloupe

-- deriving Enum through Generic or do by hand?

-- versus

-- Variant flavor
type Fruit =
    ( apple ∷ Unit
    , banana ∷ Unit
    , cantaloupe ∷ Unit

I know there various ergonomic issues either way, but what is preferred when consuming a library? Variant is easier to extend and isn’t that hard to use, but I feel like when it’s just Unit values, it’s not as useful.

In my case I want the ‘labels’ of the variant to be a defined set of possible incoming and outgoing values for an FFI according to a W3 specification. The advantage of variant is that you can exactly match the expect Strings casing with Variant.inj "apple" unit or use normally-invalid leading characters like an integer, but again the unit feels wrong, so ergonomics may want to see proxies like below, but adds a maintenance burden.

_apple ∷ Fruit
_apple = Data.Variant.inj "apple" unit

What do y’all normally do in this case?

1 Like

I rarely use Variant in libraries - it’s certainly very useful when you need it, but there’s less friction working with ADTs since they’re a first class part of the language, so I’ll tend to prefer that.

The main use for Variant I find is when you different overlapping sets of constructors that need dealing with - if it’s just for subsets, then I’ll tend to make multiple ADTs and have them carried by the “supertype”. For extendable ADTs something that works is to include something like an Other a constructor, where a can then be populated by another ADT (or perhaps a variant) if so desired, and Void when it’s not necessary.

Oh, I guess the other case is Halogen-slot-type-situations and things like that, where it’s an extensible type that is being consumed by the library, so it’s the fact the extensibility can be abstracted over with row constraints that makes it useful.

I can’t really think of a situation where I’d use a purely-Unit variant.


A bit of an aside - Variant compatible ADTs is very high on my wishlist for Purescript. Is there a technical reason why we can’t use Variants everywhere with inbuilt syntax compatible with ADTs?


That would would be interesting, but I think Variant was just an unintended, emergent side effect of how Rows worked, which happened to be useful and interesting, but was never ‘planned’.

1 Like

I’m sure we’ve discussed this before somewhere, but I can’t find it. It’s hard to evaluate without a more detailed proposal but I think the time to make a big change like that has probably passed, personally. I’d be most concerned with error messages, documentation, and whether it’s possible to do this without introducing breaking changes.