Enum
In the prelude: every iyi program has it, with no import.
The compiler gives an enum exactly two methods: value, the integer the member was numbered with, and new, which wraps one (EnumType#initialize). Everything a program does with an enum is the library's, and until samples/iyi/enums.iyi asked there was none of it — so the answers were Object's: Level::Warn == Level::Warn was **false**, to_s printed the type's name, and case level when Level::Warn matched nothing, because a when naming a member lowers to Level::Warn === level and === is ==. The compiler's own per-member question methods (level.warn?) call that same ==, and a @[Flags] enum's call includes?, so those were broken by one gap apiece.
Enum is predefined — it is what every enum's ancestor is, created before a prelude line is read — so this reopens it the way Range and String are reopened rather than declaring it.
Read off the type at each instantiation: a method here is stencilled onto the enum that calls it, so @type is that enum and @type.constants are its members. Nothing walks a table at run time and nothing is allocated to answer a name: to_s is a case over integers the compiler folds.
Enumstruct
struct Enum < Value
def &(other : self) : self
# An order, because a level is a threshold: `level >= Level::Info` is the
# first thing a program writes after a `case`. Four operators rather than a
# `Comparable` include — the prelude has no such module, and a trait would be
# `std/traits`' to carry (SPEC.md II.6).
def <(other : self) : Bool
def <=(other : self) : Bool
# Equality is the value's, which is what makes every other name work: `!=`
# and `===` are `Object`'s and both read this, `case` lowers to `===`, and
# the question methods the compiler writes are `self == Member`.
def ==(other : self) : Bool
def >(other : self) : Bool
def >=(other : self) : Bool
# A key in a `Hash` is hashed and then compared, so a type used as one owes
# both (`src/iyi/hash.iyi`). The value's own hash: `to_i32` panicked past it.
def hash : Int32
# Whether every bit of *other* is in this one, which is what a `@[Flags]`
# enum's question methods ask: the compiler writes `mode.read?` as
# `self.includes?(Mode::Read)`.
def includes?(other : self) : Bool
# `#{Level::Warn.inspect}` is the member written down rather than printed,
# and for an enum the two are the same text. It is here because `Object`'s
# sends it back to `to_s` — which was the type's name — and a sample that
# inspects a `Level?` was reading that.
def inspect : String
# The member's name, and the value's digits where no member has it —
# `Level.new(9).to_s` is `"9"`, which is what a value off a wire prints as.
#
# A flags enum answers with a member's name when one has its value (`None`
# for zero, `All` for every bit, which was skipped), else the names of the
# bits it holds, each named once, and the rest as a number: `Read | 8`.
def to_s : String
# The two set operators a flags enum is built to be used with. `Mode::Read |
# Mode::Write` is how one is written down, and `mode & Mode::Write` is how a
# bit is taken out of it.
def |(other : self) : self
# A member from the integer it was numbered with, or `nil` where no member
# has it. `new` takes any value of the base type by design — that is how an
# enum comes back from C or off a wire — and this is the question that asks
# whether the one in hand is a member.
def self.from_value?(value : Int) : self | ::Nil
# The same list said in text, which is what a message naming the choices
# needs: `"one of #{Level.names.join(", ")}"`.
def self.names : Array(String)
# A member read back from text, or `nil` where no member has that name. What
# a program does with an argument or a configuration line, so the comparison
# is the lenient one a person types: `"warn"`, `"Warn"` and `"WARN"` all
# answer `Level::Warn`, and nothing else does.
#
# `nil` rather than a panic, because the text is somebody else's input and
# the caller is the only thing that knows what to say about it (III.1).
def self.parse?(name : String) : self | ::Nil
# Every member, in the order they were written. A menu, a validation table,
# a loop that has to reach all of them — and for a flags enum the members
# rather than `None` and `All`, which are the compiler's own.
def self.values : Array(self)
end
What an
enumcan do — SPEC.md, "0.1.0", item 3.