Enums with “cooked” values

I have an enum like this

enum Public 
{
    case IHDR,
         PLTE,
         IDAT,
         IEND,

         cHRM,
         gAMA,
         iCCP,
         sBIT,
         sRGB,
         bKGD,
         hIST,
         tRNS,
         pHYs,
         sPLT,
         tIME,
         iTXt,
         tEXt,
         zTXt
    
    init?(_ a:UInt8, _ p:UInt8, _ r:UInt8, _ c:UInt8) 
    {
        switch (a, p, r, c)
        {
            case (73, 72, 68, 82):
                self = .IHDR 
            
            case (80, 76, 84, 69):
                self = .PLTE
            
            case (73, 68, 65, 84):
                self = .IDAT 
            
            // ...
            
            default:
                return nil
        }
    }
    
    var rawValue:(UInt8, UInt8, UInt8, UInt8) 
    {
        switch self 
        {
            case .IHDR:
                return (73, 72, 68, 82)
            
            case .PLTE:
                return (80, 76, 84, 69)
                
            case .IDAT:
                return (73, 68, 65, 84) 

            // ...
        }
    }
}

The “raw values” are too complex to be actual raw values, but there’s still a 1–1 relationship between enum cases and expanded representations. Repeating all the constants in the initializer and raw value property is verbose and error-prone. Should there be sugar for this kind of enum, preferably similar to our existing raw value enums?

2 Likes

I think it's reasonable for us to extend raw values to support aggregates including tuples. Once we have @Chris_Lattner3's @compilerEvaluable feature, we ought to be able to use that to validate that any raw value expressible in terms of completely @compilerEvaluable expressions is distinct, instead of the very limited literal-specific implementation we have now. This would let you write:

enum Public: (UInt8, UInt8, UInt8, UInt8) {
  case IHDR = (73, 72, 68, 82)
  case PLTE = (80, 76, 84, 69)
  /* etc. */
}

as shorthand for what you've written above.

22 Likes

Joe was faster, I just wanted to pitch if it makes sense from the perspective of the @compilerEvaluable attribute to create some sugar syntax like Joe just showed. I'm really looking forward to that feature.

2 Likes

now if only we had those character integer literals…

1 Like

Well, you could have a @compilerEvaluable string literal processor:

@compilerEvaluable
func fourCharCode(_ s: String) -> (UInt8, UInt8, UInt8, UInt8) {
  assert(s.unicodeScalars.count == 4, "four-character code too long")

  assert(s.unicodeScalars.index { $0.value < 0 || $0.value > 0x7f } == nil, "four-character code must be ASCII")

  return (UInt8(s.unicodeScalars[0].value), UInt8(s.unicodeScalars[1].value), UInt8(s.unicodeScalars[2].value), UInt8(s.unicodeScalars[3].value))
}

enum Public: (UInt8, UInt8, UInt8, UInt8) {
  case IHDR = fourCharCode("IHDR")
  ...
}
19 Likes