diff --git a/spec/Candid.md b/spec/Candid.md index f68de3f3a..ddaa75f38 100644 --- a/spec/Candid.md +++ b/spec/Candid.md @@ -1,8 +1,8 @@ # Candid Specification -Version: 0.1.3 +Version: 0.1.4 -Date: February 3, 2021 +Date: January 11, 2022 ## Motivation @@ -885,62 +885,85 @@ service { : ; ;* } <: service { : ; This subtyping is implemented during the deserialisation of Candid at an expected type. As described in [Section Deserialisation](#deserialisation), the binary value is conceptually first _decoded_ into the actual type and a value of that type, and then that value is _coerced_ into the expected type. -To model this, we define, for every `t1, t2` with `t1 <: t2`, a function `C[t1<:t2] : t1 -> t2`. This function maps values of type `t1` to values of type `t2`, and is indeed total. - -to describe these values, we re-use the syntax of the textual representation, and use the the `` syntax (i.e. `(v : t)`) if necessary to resolve overloading. +This section describes the coercion, as a relation `V : T ~> V' : T'` to describe when a value `V` of type `T` can be coerced to a value `V'` of type `T'`. The fields `V`, `T` and `T'` can be understood as inputs and `V'` as the output of this relation. +To describe these values, we re-use the syntax of the textual representation. #### Primitive Types -On primitve types, coercion is the identity: +On primitive types, coercion is the identity: ``` -C[ <: ](x) = x for every , bool, text, null +, bool, text, null +--------------------------------- + : ~> : ``` Values of type `nat` coerce at type `int`: ``` -C[nat <: int]() = +-------------------------- + : nat ~> : int ``` Coercion into `reserved` is the constant map (this is arbitrarily using `null` as “the” value of `reserved`): ``` -C[ <: reserved](_) = null +-------------------------- +_ : ~> null : reserved ``` -NB: No rule is needed for type `empty`, because there are no values of that type. By construction, `C[empty <: ]` is the unique function on the empty domain. + +NB: No rule is needed for type `empty`, because there are no values of that type. By construction, `_ : empty ~> _ : _` will not hold. #### Vectors Vectors coerce pointwise: ``` -C[vec <: vec ]( vec { ;* } ) = vec { C[ <: ]();* } + : ~> : +---------------------------------------------------- +vec { ;* } : vec ~> vec { ;* } : vec ``` #### Options -The null type and the reserved type coerce into any option type: +The null value coerces into any option type: ``` -C[null <: opt ](null) = null +null <: +----------------------------- +null : ~> null : opt ``` An optional value coerces at an option type, if the constituent value has a suitable type, and else goes to `null`: ``` -C[opt <: opt ](null) = null -C[opt <: opt ](opt ) = opt C[ <: ](v) if <: -C[opt <: opt ](opt ) = null if not( <: ) + : ~> : +---------------------------------------- +opt : opt ~> opt : opt + +not ( : ~> _ : ) +---------------------------------------- +opt : opt ~> null : opt ``` -Coercing a non-null, non-optional and non-reserved type at an option type treats it as an optional value, if it has a suitable type: +Coercing a non-null, non-optional and non-reserved type at an option type treats it as an optional value, if it can be decoded successfully: ``` -C[ <: opt ]() = opt C[ <: ](v) if not (null <: ) and <: +not (null <: ) +not (null <: ) + : ~> : +-------------------------------- + : ~> opt : opt ``` -Any other type goes to `null`: -``` -C[reserved <: opt ](_) = null -C[ <: opt ](_) = null if not (null <: ) and not ( <: ) -C[ <: opt ](_) = null if null <: and not (null <: ) +Any other value goes to `null`: ``` +--------------------------------- + : reserved ~> null : opt -NOTE: These rules above imply that a Candid decoder has to be able to decide the subtyping relation at runtime. +not (null <: ) +not ( : ~> _ : ) +-------------------------------- + : ~> null : opt + +null <: +not (null <: ) +---------------------------- + : ~> null : opt +``` #### Records @@ -951,8 +974,11 @@ In the following rule: * the `*` field names are those only in the expected type, which therefore must be of optional or reserved type. The `null` value is used for these. ``` -C[record { = ;* = ;* } <: record { = ;* = ;* }](record { = ;* = ;* }) - = record { = C[ <: ]();* = null;* } + : ~> : +opt empty <: +------------------------------------------------------------------------------------------- +record { = ;* = ;* } : record { : ;* : ;* } ~> +record { = ;* = null;* } : record { : ;* : ;* } ``` #### Variants @@ -960,28 +986,40 @@ C[record { = ;* = ;* } <: record { = ;* Only a variant value with an expected tag coerces at variant type. ``` -C[variant { = ; _;* } <: variant { = ; _;* }](variant { = }) - = variant { = C[ <: ]() } + : ~> : +---------------------------------------------------------- +variant { = ; } : variant { : ; _;* } ~> +variant { = } : variant { : ; _;* } ``` #### References -Function and services reference values are untyped, so the coercion function is the identity here: +For function and services reference values, the coercion relation checks whether the given types are actually subtypes of the expected type, and fails otherwise: ``` -C[func <: func ](func .) = func . -C[service <: service ](service ) = service -C[principal <: principal](principal ) = principal +func <: func +--------------------------------------------------------------------- +func .id : func ~> func .id : func + +service <: service +----------------------------------------------------------------------------- +service : service ~> service : service + +------------------------------------------------------------ +principal : principal ~> principal : principal ``` +NOTE: These rules above imply that a Candid decoder has to be able to decide the subtyping relation for reference types. + #### Tuple types Whole argument and result sequences are coerced with the same rules as tuple-like records. In particular, extra arguments are ignored, and optional parameters read as as `null` if the argument is missing or fails to coerce: ``` -C[(,*) <: (,*)]((,*)) = (,*) - if C[record {;*} <: record {,*}](record {;*}) = record {;*} +record { ;* } : record { ;* } ~> record { ;* } : record { ;* } +---------------------------------------------------------------------------- +(,*) : (,*) ~> (,*) : (,*) ``` @@ -1001,12 +1039,17 @@ The relations above have certain properties. As in the previous section, ` : * Roundtripping: ``` - ( : ) ⟺ C[ <: ]() = + ( : ) ⟺ : ~> : + ``` + +* Well-typedness: + ``` + : ~> : : ``` * Soundness of subtyping (or, alternatively, well-definedness of coercion) ``` - <: ⇒ ∀ : . v[ <: ]() : + <: ⇒ ∀ : . : ~> : ``` * Higher-order soundness of subtyping @@ -1020,17 +1063,16 @@ The relations above have certain properties. As in the previous section, ` : * NB: Transitive coherence does not hold: ``` - <: , <: + : ~> : + : ~> : + : ~> : ``` does not imply ``` - C[ <: ] = C[ <: ] ⚬ C[ <: ] + = ``` - However, it implies - ``` - C[ <: ] ~ (C[ <: ] ⚬ C[ <: ]) - ``` + However, it implies ` ~ `, where ~ is the smallest homomorphic, reflexive, symmetric relation that satisfies `∀ v. opt v ~ null`. The goal of “subtyping completeness” has not been cast into a formal formulation yet.