RawRepresentable – NSHipster


Programming is about typing.
And programming languages are usually judged by how a lot they make you sort —
in each senses of the phrase.

Swift is beloved for with the ability to save us a couple of keystrokes
with out compromising security or efficiency,
whether or not it’s by way of
implicit typing or
automated synthesis of protocols like
Equatable and
Hashable.
However the OG
ergonomic characteristic of Swift is undoubtedly
automated synthesis of UncookedRepresentable conformance
for enumerations with uncooked sorts.

the language characteristic that permits you to do that:

enum Greeting: String {
    case hey = "hey"
    case goodbye // implicit uncooked worth of "goodbye"
}

enum KindOrder: Int {
    case ascending = -1
    case similar // implicit uncooked worth of 0
    case descending  // implicit uncooked worth of 1
}

Although “enum + RawValue” has been carved into the oak tree of our hearts
since first we laid eyes on that language with a quick fowl,
few of us have had event to think about
what UncookedRepresentable means outdoors of autosynthesis.
This week,
we invite you to perform a little further typing
and discover some untypical use instances for the UncookedRepresentable protocol.


In Swift,
an enumeration may be declared with
uncooked worth syntax.

Based on the documentation:

For any enumeration with a string, integer, or floating-point uncooked sort,
the Swift compiler mechanically provides UncookedRepresentable conformance.

When builders first begin working with Swift,
they inevitably run into conditions the place uncooked worth syntax doesn’t work:

  • Enumerations with uncooked values apart from Int or String
  • Enumerations with related values

Upon seeing these brilliant, purple error sigils,
many people fall again to a extra typical enumeration,
failing to appreciate that what we wished to do wasn’t inconceivable,
however moderately simply barely past what the compiler can do for us.


NS_ENUM.
However interoperability with different C libraries is commonly much less seamless.

Take into account the duty of interfacing with
libcmark,
a library for working with Markdown in response to the
CommonMark spec.
Among the many imported information sorts is cmark_node_type,
which has the next C declaration:

typedef enum {
  /* Error standing */
  CMARK_NODE_NONE,

  /* Block */
  CMARK_NODE_DOCUMENT,
  CMARK_NODE_BLOCK_QUOTE,
  
  CMARK_NODE_HEADING,
  CMARK_NODE_THEMATIC_BREAK,

  CMARK_NODE_FIRST_BLOCK = CMARK_NODE_DOCUMENT,
  CMARK_NODE_LAST_BLOCK = CMARK_NODE_THEMATIC_BREAK,

  
} cmark_node_type;

We will instantly see a couple of particulars that may have to be ironed out
alongside the trail of Swiftification —
notably,
1) the sentinel NONE worth, which might as an alternative be represented by nil, and
2) the aliases for the primary and final block values,
which wouldn’t be encoded by distinct enumeration instances.

Making an attempt to declare a Swift enumeration
with a uncooked worth sort of cmark_node_type ends in a compiler error.

enum NodeSort: cmark_node_type {} // Error

Nonetheless,
that doesn’t completely rule out cmark_node_type from being a UncookedWorth sort.
Right here’s what we have to make that occur:

enum NodeSort: UncookedRepresentable {
    case doc
    case blockQuote
    

    init?(uncookedWorth: cmark_node_type) {
        swap uncookedWorth {
        case CMARK_NODE_DOCUMENT: self = .doc
        case CMARK_NODE_BLOCK_QUOTE: self = .blockQuote
        
        default:
            return nil
        }
    }

    var uncookedWorth: cmark_node_type {
        swap self {
        case .doc: return CMARK_NODE_DOCUMENT
        case .blockQuote: return CMARK_NODE_BLOCK_QUOTE
        
        }
    }
}

It’s a far cry from with the ability to say case doc = CMARK_NODE_DOCUMENT,
however this strategy presents an affordable answer
that falls inside the current semantics of the Swift normal library.

That debunks the parable about
Int and String being the one sorts that may be a uncooked worth.
What about that one about related values?

Because the outdated adage goes:

There are three numbers in pc science: 0, 1, and N.

enum Quantity {
    case zero
    case one
    case n(Int)
}

Due to the related worth on n,
the compiler can’t mechanically synthesize an Int uncooked worth sort.
However that doesn’t imply we will’t roll up our sleeves and decide up the slack.

extension Quantity: UncookedRepresentable {
    init?(uncookedWorth: Int) {
        swap uncookedWorth {
        case 0: self = .zero
        case 1: self = .one
        case let n: self = .n(n)
        }
    }

    var uncookedWorth: Int {
        swap self {
        case .zero: return 0
        case .one: return 1
        case let .n(n): return n
        }
    }
}

Quantity(uncookedWorth: 1) // .one

One other fable busted!

Let’s proceed this instance to clear up
a false impression we discovered within the documentation.

injective glory.

So the subsequent time you end up with an enumeration
whose instances dealer in discrete, outlined counterparts,
think about adopting UncookedRepresentable to formalize the connection.

Related Articles

LEAVE A REPLY

Please enter your comment!
Please enter your name here

Latest Articles