# Enums in FTL **Author**: @worstell ## Description We are enriching the FTL type system with enum support. Enums can be either "value" or "type" enums, where value enums associate names with constant values. Type enums function like sum types or discriminated unions, whereby names are associated with types with no underlying raw value. Type enums are discussed in futher detail [here](https://hackmd.io/TZqMfuyHTYaXd1gSECbpFg?both). ## Goals - Enums can be declared in supported runtimes and will be parsed into the FTL schema - Value enums associate names with constant values - Numeric and string values are the only supported types for value enums - Type enums (sum types/discriminated unions) associate names with types. Discussed in further detail [here](https://hackmd.io/TZqMfuyHTYaXd1gSECbpFg?both) ## Design ### Go Go does not have a concept of enums. In the absence of language builtins, we’ll require an annotation, `//ftl:enum`, to declare an FTL enum. This is necessary to make enums identifiable for schema parsing. Declaring an enum in a Go module will look as follows: _Value enum:_ ```go package example //ftl:enum type Color string const ( Red Color = "Red" Blue Color = "Blue" Green Color = "Green" ) ``` _Type enum:_ ```go! package example //ftl:enum type ScalarOrList interface { tag() } type Scalar string func (Scalar) tag() {} type List []string func (List) tag() {} ``` Variants are identified on the basis of implementing the discriminator (interface). The discriminator can only contain private methods. ### Kotlin _Value enum:_ Kotlin supports enum classes which we can leverage directly for declaring value enums as follows: ```kotlin package ftl.example enum class Color(name: String) { RED("Red"), GREEN("Green"), BLUE("Blue"), } ``` _Type enum:_ ```kotlin package ftl.example data class Scalar( ... ) data class List( ... ) @Enum sealed class SumType { data class Scalar( val value: ftl.example.Scalar ) : SumType() data class Scalar( val value: ftl.example.List ) : SumType() } ``` **Note: the `value` field is required to express sum type variants in the Kotlin runtime. It will be detected and used to extract the underlying type.** In the FTL schema, both of the above cases would appear as follows: ```graphql module example { enum Color: String { Red = "Red" Blue = "Blue" Green = "Green" } enum ScalarOrList { Scalar String List [String] } } ``` ## Rejected alternatives ### Structured enums Any hashable type supported in the FTL type system can be an enum value. The initial design leaves the door open to this change in future. :::info Enum values must be hashable to make their use as map keys possible. Enum types (transitively), then, cannot have any list/map fields. ::: In Kotlin, an `enum class` can be constructed with an arbitrary number of values (as any class). If multiple constructor parameters are present, we will synthesize them into a single parameter (wrapping them in an outer type). This is necessary to comply with restrictions of the Go implementation. Below represents Kotlin user code: ```kotlin package ftl.exchange data class CurrencyCode(val code: String) enum class Country(name: String, currency: CurrencyCode) { UnitedStates("US", CurrencyCode("USD")), Australia("AU", CurrencyCode("AUD")), } ``` And Go: ```go package exchange type CurrencyCode { Code string } //ftl:enum type Country struct { Name string Currency CurrencyCode } const ( UnitedStates = Country{ Name: "US", Currency: CurrencyCode{ Code: "USD", } } Australia = Country{ Name: "AU", Currency: CurrencyCode{ Code: "AUD", } } ) ``` Both would appear as follows the FTL schema: ``` module exchange { data CurrencyCode { code String } enum CountryValue { name String currency exchange.CurrencyCode } { unitedStates({ name = "US", currency = { code = "USD" } }) australia({ name = "AU", currency = { code = "AUD" } }) } } ``` ### Proposal We will implement restrictive value enums initially. FTL value enums must be either int or string types, which should encompass the vast majority of enum use-cases.