You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
* [move-book] Function Value Documentation
* Moving docs from zh to en
* [function values] fix docs that were removed in merge changes
* Addressing reviewer comments
---------
Co-authored-by: Greg Nazario <greg@gnazar.io>
Copy file name to clipboardexpand all lines: apps/nextra/pages/en/build/smart-contracts/book/functions.mdx
+173-2
Original file line number
Diff line number
Diff line change
@@ -232,10 +232,12 @@ script {
232
232
}
233
233
```
234
234
235
-
Entry functions can accept parameters that are: primitive types, reference to a `signer`, vectors (where the element type is itself acceptable),
235
+
Entry functions can accept parameters that are: primitive types, reference to a
236
+
`signer`, vectors (where the element type is itself acceptable),
236
237
and certain standard library types such as `String`, `Object`, and `Option`.
237
238
Entry functions must not have any return values.
238
239
240
+
239
241
### Name
240
242
241
243
Function names can start with letters `a` to `z` or letters `A` to `Z`. After the first character, function names can contain underscores `_`, letters `a` to `z`, letters `A` to `Z`, or digits `0` to `9`.
@@ -736,7 +738,7 @@ Notice that you do not need to `use` the modules which introduce receiver functi
736
738
737
739
The receiver style syntax can also be used on generic functions, like shown below for the generic function `std::vector::remove<T>(self: &mut vector<T>, i: u64): T`.
738
740
739
-
```move
741
+
```move
740
742
module 0x42::example {
741
743
fun bar() {
742
744
let v = vector[1, 2, 3];
@@ -747,3 +749,172 @@ module 0x42::example {
747
749
}
748
750
}
749
751
```
752
+
753
+
754
+
## Function Values
755
+
756
+
_Since language version 2.2_ (preview)
757
+
758
+
Move supports *function values* as first-class citizen of the language. A function value is constructed from the name of a function or by a lambda expression, and is evaluated by passing parameters to it and causing the underlying function to be executed. This feature is often also called _dynamic dispatch_. Which concrete function is called, is not known to the caller, and determined from the runtime value. Dynamic dispatch is an important tool for composing applications. Move makes dynamic dispatch safe by providing builtin protection mechanisms against reentrancy, which can be further refined by user choice.
759
+
760
+
### Function Types
761
+
762
+
The type of functions values is already known from [inline functions](#function-parameters-and-lambda-expressions). A function type is denoted, for example, as `|u64|bool`, indicating a function which takes a number and returns a boolean. Lists of types are separated by comma, as in `|u64, bool|(bool,u4)`.
763
+
764
+
Function types can have associated abilities, written as `|u64|bool has copy`. Multiple abilities are separated by plus, as in `|u64|bool has copy+drop`. If no abilities are provided, the value can be only moved around and evaluated (for evaluation of function values, see [below](#function-evaluation)).
765
+
766
+
Function values can be stored in fields of structs or enums. In this case, the field type inherits the abilities of the struct:
767
+
768
+
```move
769
+
struct S has key {
770
+
func: |u64| bool /* has store */ // not needed since inherited
771
+
}
772
+
```
773
+
774
+
### Operations on Functions
775
+
776
+
A function value is evaluated by providing the corresponding number of parameters, similar as when calling a named function. During evaluation, the function value is *consumed*. Hence if the value needs to be evaluated multiple times, it's type must have the `copy` ability:
777
+
778
+
```move
779
+
let f: |u64|bool has copy = |x| x > 0;
780
+
assert!(f(1) == f(2))
781
+
```
782
+
783
+
Function values support equality and ordering. Note that those relations are based on the name of the underlying function behind a runtime value, and do not reflect semantic equivalence.
784
+
785
+
786
+
### Function Type Wrappers
787
+
788
+
Function types, specifically if they come together with abilities, can be verbose, and if the same type of function type is used many times in the code, repetitive. For this purpose, Move recognizes struct wrappers around function types as a special case. They can be used to effectively create named function types:
789
+
790
+
```move
791
+
struct Predicate<T>(|&T|bool) has copy;
792
+
```
793
+
794
+
Move supports this feature by automatically converting function values into the wrapper type and vice versa. Examples:
795
+
796
+
```move
797
+
let f: Predicate<u64> = |x| *x > 0; // lambda converts to Predicate
798
+
assert!(f(&22)) // Predicate callable
799
+
```
800
+
801
+
### Denoting Function Values
802
+
803
+
Function values can be constructed by directly using a function name. The resulting function type is derived from the signature of the underlying function, with abilities `copy+drop`. If the function is public, those function values have the `store` ability as well:
804
+
805
+
```move
806
+
public fun is_even(x: u64): bool { x % 2 == 0 }
807
+
fun is_odd(x: u64): bool { x % 2 == 1 }
808
+
...
809
+
let f: |u64|bool has copy+drop+store = is_even;
810
+
let g: |u64|bool has copy+drop = is_odd;
811
+
```
812
+
813
+
A _persistent_ function is required to build a storable function value because it needs to be guaranteed that the underlying function exists and can be safely restored from storage at any point in the future. However, code upgrade may change the underlying implementation of the function, while its signature is persistent.
814
+
815
+
While `public` and `entry` functions are persistent by default, a none-public function needs to be marked with the attribute `#[persistent]` to become storable:
816
+
817
+
```move
818
+
#[persistent] fun is_odd(x: u64): bool { x % 2 == 1 }
819
+
...
820
+
let g: |u64|bool has copy+drop+store = is_odd;
821
+
```
822
+
823
+
Using the `#[persistent]` attribute is preferred if the only objective is to make a function storable, avoiding security implications with public or entry visibility.
824
+
825
+
826
+
### Lambda Expressions and Closures
827
+
828
+
Function values can be denoted by _lambda expressions_ (as also available as parameters for [inline functions](#function-parameters-and-lambda-expressions)). Lambda expressions can capture context variables _by value_: those values are moved (or copied) into a _closure_, from where they are produced when the function is evaluated. Examples:
829
+
830
+
```move
831
+
struct S(u64); // cannot be copied or dropped
832
+
...
833
+
let s = S(1);
834
+
let add = |y| { let S(x) = s; x + y }; // s will be moved into the closure
835
+
assert!(add(2) == 3)
836
+
```
837
+
838
+
Closures with captured values are lexicographical ordered using first the name of the underlying function (which maybe generated from lambda lifting), and then the captured values.
839
+
840
+
The type of the closure constructed by a lambda expression is inferred from the expression (for example, the type of `add` in the example above is inferred as `|u64|u64`). The abilities of this function type are derived as follows. By default, the function underlying a closure is a private function, so the function itself is `copy+drop` (and not `store`). This is intersected with the abilities of all the captured context variables. However, there is a special case for lambdas where instead of a private function an underlying persistent function can be identified, such that the lambda just 'delays' certain arguments of this function. This pattern is also called 'currying' in functional programming (named after the mathematician Curry). Here are some examples:
841
+
842
+
```move
843
+
#[persistent] fun add(x: u64, y: u64) { x + y }
844
+
...
845
+
let x = 22;
846
+
let f: |u64|u64 has copy+drop+store = |y| add(x, y); // 1st argument captured, 2nd argument delayed
847
+
let f: |u64|u64 has copy+drop+store = |y| add(y, x); // 1st argument delayed, 2nd argument captured
848
+
```
849
+
850
+
Notice it is not possible to _capture_ reference values at this point of time in Move. Thus, the following code does not compile:
851
+
852
+
```move
853
+
let x = &22;
854
+
let f = |y| add(*x, y) // DOES NOT COMPILE
855
+
```
856
+
857
+
Related, it is not possible to mutate any locals in the context of a lambda. Specifically, the following pattern as known from lambdas with inline functions, is not supported:
858
+
859
+
```move
860
+
let x = 0;
861
+
collection.for_each(|e| x += e) // DOES NOT COMPILE
862
+
```
863
+
864
+
However, the actual parameters of lambdas can be references, only captured values are restricted. For example:
865
+
866
+
```move
867
+
let x = 22;
868
+
let f : |&u64|u64 = |y| add(x, *y)
869
+
```
870
+
871
+
### Reentrancy Check
872
+
873
+
Via dynamic dispatch of function values, reentrancy of modules in a chain of function calls is possible. If module `m1` uses module `m2`, and `m1` calls `m2::f` passing a function value to it, this function value can callback into `m1`. This situation is called _reentrancy_, and is not possible in Move without function values, since the module usage relation is acyclic.
874
+
875
+
The Move VM dynamically detects reentrancy of a module, and _locks_ all resources declared in this module from being accessed. Thus during reentrancy of `m`, calling resource operations like `&m::R[addr]`, `&mut m::R[addr]`, and `move_from<m::R>` lead to an abort. Here is an example:
876
+
877
+
```move
878
+
module 0x42::caller {
879
+
use 0x42::callee;
880
+
struct R{ count: u64 } has key;
881
+
fun calling() acquires R {
882
+
let r = &mut R[@addr];
883
+
// This callback is OK, because `R` is not accessed
884
+
callee::call_me(r, |x| do_something(x))
885
+
// This callback will lead to reentrancy runtime error
886
+
callee::call_me(r, |_| R[@addr].count += 1)
887
+
r.call_count += 1
888
+
}
889
+
fun do_something(r: &mut R) { .. }
890
+
}
891
+
892
+
module 0x42::callee {
893
+
fun call_me<T(x: &mut T, action: |&mut T|) {
894
+
action(x)
895
+
}
896
+
}
897
+
```
898
+
899
+
Notice that dispatching a function value to a concrete function in the same module is also considered to be reentrancy. If the function `callee::call_me` would be moved into the module `caller`, the same semantics is in effect.
900
+
901
+
The default reentrancy check ensures consistency of Move's reference semantics and suppresses side effects of reentrancy for the resources owned by the re-entered module. However, re-entered code is allowed to still access resource state managed by modules outside the reentrancy path. Such state accesses can be considered bad design, but they exist.
902
+
For these purposes, the `#[module_lock]` attribute can be attached to a function:
903
+
904
+
```move
905
+
module 0x42::account { ... }
906
+
module 0x42::caller {
907
+
#[module_lock] // without this lock, the notify call could withdraw more than intended.
908
+
fun transfer(from: address, to: address, amount: u64, notify: |u64|) {
909
+
// Oops. This should be really differently designed, using `Coin` type and moving it.
notify(amount); // attempt to re-entrer `transfer` is blocked
913
+
account::withtdraw(from, amout);
914
+
}
915
+
}
916
+
```
917
+
918
+
While a function with this attribute is running, all calls reentering any module will lead to an abort, given a stronger protection.
919
+
920
+
The attribute `#[module_lock]` restriction is not the default behavior since it is too strong for typical patterns of higher-order programming. For example, `collection.find(|x| cond(x))` will lead to a reentrancy of the module which contains this expression, from the module which defines the collection type.
Copy file name to clipboardexpand all lines: apps/nextra/pages/en/build/smart-contracts/book/global-storage-operators.mdx
+2
Original file line number
Diff line number
Diff line change
@@ -119,6 +119,8 @@ module 0x42::counter {
119
119
120
120
## Annotating functions with `acquires`
121
121
122
+
*Note: Since language version 2.2, acquires annotations are optional. If no acquires is given, it will be inferred.*
123
+
122
124
In the `counter` example, you might have noticed that the `get_count`, `increment`, `reset`, and `delete` functions are annotated with `acquires Counter`. A Move function `m::f` must be annotated with `acquires T` if and only if:
123
125
124
126
- The body of `m::f` contains a `move_from<T>`, `borrow_global_mut<T>`, or `borrow_global<T>` instruction, or
Copy file name to clipboardexpand all lines: apps/nextra/pages/en/build/smart-contracts/book/move-2.mdx
+6
Original file line number
Diff line number
Diff line change
@@ -2,6 +2,12 @@
2
2
3
3
The Move 2 language releases are described on this page. The reference documentation of the new features is integrated into the book, and marked in the text with "_Since language version 2.n_".
4
4
5
+
## Move 2.2
6
+
7
+
The Move 2.2 language release adds the following features to Move:
8
+
9
+
-**Optional Acquires**: The `acquires` annotation on function declarations can be omitted, to be inferred by the compiler.
10
+
-**Function Values**: Move now supports function values, which can be passed around as parameters and stored in resources. See the [reference doc here](functions.mdx#function-values).
5
11
## Move 2.1
6
12
7
13
The Move 2.1 language release adds the following features to Move:
0 commit comments