The Cadence team is excited to share the first preview release of Stable Cadence with the Cadence developer community.
To learn more about Stable Cadence, have a read through the announcement post: http://forum.flow.com/t/the-path-to-stable-cadence/2702
This first preview release contains the changes that have been proposed through FLIPs and have been implemented so far.
This includes the changes we announced in the last progress update in the Ready/Merged section: http://forum.flow.com/t/another-update-on-stable-cadence/3715, as well as 3 additional changes that were completed since:
Previously in the “Upcoming” section:
References to resource-kinded values get invalidated when the referenced value is moved
Click here to read more
https://github.com/onflow/flow/pull/1043
https://github.com/onflow/cadence/pull/2037
https://github.com/onflow/cadence/pull/1999
Previously, when a reference is taken to a resource, that reference remains valid even if the resource was moved, for example when created and moved into an account, or moved from one account into another.
In other words, references to resources stayed alive forever. This could be a potential safety foot-gun, where one could gain/give/retain unintended access to resources through references.
In Stable Cadence, references are invalidated if the referenced resource is moved after the reference was taken. The reference is invalidated upon the first move, regardless of the origin and the destination.
e.g:
// Create a resource.
let r <-create R()
// And take a reference.
let ref = &r as &R
// Then move the resource into an account.
account.save(<-r, to: /storage/r)
// Update the reference.
ref.id = 2
Old behaviour:
// This will also update the referenced resource in the account.
ref.id = 2
In Stable Cadence, the above operation will result in a static error.
// Trying to update/access the reference will produce a static error:
// "invalid reference: referenced resource may have been moved or destroyed"
ref.id = 2
However, not all scenarios can be detected statically. e.g:
pub fun test(ref: &R) {
ref.id = 2
}
In the above function, it is not possible to determine whether the resource to which the reference was taken has been moved or not. Therefore, such cases are checked at run-time, and a run-time error will occur if the resource has been moved.
Semantics for variables in for-loop statements changed
Click here to read more
https://github.com/onflow/flips/pull/13
https://github.com/onflow/cadence/pull/2024
The behaviour of variables in for-loop statements changed. This removed an often surprising behaviour from the language and reduced the likelihood of bugs. This change only affects few programs, as the behaviour change is only noticeable if the program captures the for-loop statement variables in a function value (closure).
// Capture the values of the array [1, 2, 3]
let fs: [((): Int)] = []
for x in [1, 2, 3] {
// Create a list of functions that return the array value
fs.append(fun (): Int {
return x
})
}
// Evaluate each function and gather all array values
let values: [Int] = []
for f in fs {
values.append(f())
}
Previously, values
would result in [3, 3, 3]
, which might be surprising and unexpected. This is because x
was reassigned the current array element on each iteration, leading to each function in fs
returning the last element of the array.
In Stable Cadence, values
will result in [1, 2, 3]
, which is likely what the author of the program expected.
New:
Syntax for function types changed
Click here to read more
https://github.com/onflow/cadence/pull/2140
Previously, function types were expressed using a different syntax from function declarations or expressions.
e.g:
pub fun foo(n: Int8, s: String): Int16 { /* ... */ }
// function `foo` had the type `((Int8, String): Int16)`
The previous syntax was unintuitive for developers - the problem becomes more apparent in more complex type signatures. Nested function types led to deeply nested parentheses and discouraged smart contract writers from utilizing higher-order functions.
pub fun filter(f: ((A): Bool), xs: [A]): [A] { /* ... */ }
// function `filter` had the type `((((A): Bool), [A]): A)`
In Stable Cadence, function types are expressed using the fun
keyword, just like expressions and declarations. This improves readability and makes function types more obvious. The previous examples are now written as:
pub fun foo(n: Int8, s: String): Int16 { /* ... */ }
// function `foo` has the type `fun(Int8, String): Int16`
pub fun filter(f: fun(A): Bool, xs: [A]): [A] {...}
// function `filter` has the type `fun(fun(A): Bool, [A]): [A]`
The :
token is right-associative, so functions that return other functions can have their types written without nested parentheses:
fun curriedAdd(_ x: Int): fun(Int): Int {
return fun(_ y: Int): Int {
return x + y
}
}
// function `curriedAdd` has the type `fun(Int): fun(Int): Int`
To bring function types closer to function expressions, we now allow return types for procedures (functions with no explicit return type, i.e. Void
) to be omitted in types too. A function type without an explicit return type annotation will now default to returning Void
.
pub fun logTwice(_ value: AnyStruct) { // return type is inferred to be `Void`
log(value)
log(value)
}
// these types are equivalent
let logTwice1: fun(AnyStruct): Void = logTwice
let logTwice2: fun(AnyStruct) = logTwice
For contracts containing function-typed values with explicit type annotations, the old syntax will now throw an error during parsing. Developers can migrate their contracts by adding the fun
keyword to these type annotations, for example:
// before
let baz: ((Int8, String): Int16) = foo
// after
let baz: (fun (Int8, String): Int16) = foo
// the surrounding parentheses are no longer required
let baz: fun (Int8, String): Int16 = foo
As a bonus consequence of these changes to the language syntax, we now allow any type to be parenthesized. This is useful for complex type signatures, or for expressing optional functions:
// a function returning an optional Int16
let optFun1: fun (Int8): Int16? =
fun (_: Int8): Int? { return nil }
// an optional function returning an Int16
let optFun2: (fun (Int8): Int16)? = nil
Preview release installation
You can install this preview release using the following update command:
sh -ci "$(curl -fsSL <https://storage.googleapis.com/flow-cli/install.sh>)" -- v0.41.3-stable-cadence-5
Please start evaluating this preview release with your Cadence contracts.
The Cadence team is eager to receive your feedback! How do you feel about the proposed breaking changes? Did you have any problems upgrading and running your contracts? Did you find any bugs?
Note that this is only a first preview release, and more changes are planned for the final Stable Cadence release.