1

Is there a software pattern (or some recommended guidelines) for how to convert between enum values and discriminated unions? Or more specifically: project a discriminated union onto its enum case? And how do you call the enum case? "enum tag"? "union tag"? Does it even have a standardized name?

For example this Swift code (imagine that the enum has 20 cases rather than 3):

enum TValue: Equatable {
  case sBasic(String)
  case sSpecial([String: String])
  case sPizza(String, Int, Any)
}

and I want to check if two values have the same type. So I could define another type, a projection function and a comparison function for convenience.

enum TValueType: Equatable, CaseIterable {
  case tBasic
  case tSpecial
  case tPizza
}

extension TValue {
  var type: TValueType {
    switch self {
      case .sBasic: return tBasic
      case .sSpecial: return tSpecial
      case .sPizza: return tPizza
    }
  }
}

func is_compatible(_ a: TValue, _ b: TValue) -> Bool {
  return a.type == b.type
}

What I would like to do instead is this:

func is_compatible(_ a: TValue, _ b: TValue) -> Bool {
  return a.enumCaseNumber == b.enumCaseNumber
}

Of course I could do something like

func is_compatible(_ a: TValue, _ b: TValue) -> Bool {
    return "\(a)".split("(").first == "\(b)".split("(").first;
}

which is a waste of CPU time because I just want to compare the first few bits of two values for equality. It should be two load instructions, one test instruction and a conditional jump instruction at runtime (or something similar, depending on the CPU architecture.)

Or I could roll my own interface:

enum TValueType {
    case basic
    case special
    case pizza
}

struct TValue {
    private(set) var basic: String?
    private(set) var special: [String: String]?
    private(set) var pizza: (String, Int, Any)?
    var type: TValueType {
        if basic == nil {
            if special == nil {
                return .pizza
            } else {
                return .special
            }
        } else {
            return .basic
        }
    }
    init(basic: String) { self.basic = basic }
    init(special: [String: String]) { self.special = special }
    init(pizza: (String, Int, Any)) { self.pizza = pizza }
}

Now, at least the interface is clean. But there is still too much boilerplate code.

Seems like the C version of this code is more beauitful (or at least not more ugly) and the amount of boilerplate is the same:

#define TVALUE_BASIC 23
#define TVALUE_SPECIAL 24
#define TVALUE_PIZZA 25

typedef union {
    int tag;
    struct {
        int tag;
        MyStringRef value;
    } basic;
    struct {
        int tag;
        MyStringMapRef value;
    } special;
    struct {
        int tag;
        MyStringRef val1;
        int val2;
        MyAnyRef val3;
    } pizza;
} TValue;

int is_compatible(TValue *a, TValue *b) {
    if (a == b) {
        return 1;
    } else if (!a || !b) {
        return 0;
    } else {
        return a->tag == b->tag;
    }
}

Do you know of any programming language where this kind of pattern is straightforward to implement? Maybe because there is an "enumCaseNumber" property or some other method of accessing the union tag?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.