Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Move Edition 2024: New Language Features! #14062

Open
tnowacki opened this issue Oct 2, 2023 · 17 comments
Open

Move Edition 2024: New Language Features! #14062

tnowacki opened this issue Oct 2, 2023 · 17 comments
Labels
design devx move Type: Major Feature Major new functionality or integration, which has a significant impact on the network

Comments

@tnowacki
Copy link
Contributor

tnowacki commented Oct 2, 2023

This year, we plan to fully release a set of new features to the language. Many of these changes are enhancements to the source language (that is they will affect the compiler without requiring any changes to the binary representation that is published on chain).

Primarily, the goal of these changes is to make Move easier to write, and hopefully easier to read. Secondarily, we will make a few breaking changes to the source language to better position the language for the future.

Do not worry though, existing code will continue to compile! So these new features and the breaking changes will be opt-in. Any existing code will continue to compile. This also means that you will be able to write your packages with the new features, even if your dependencies do not.

These features will be developed and rolled-out over the coming months (and some have already landed!).

  • To try these features out and provide feedback, you can specify edition = "2024.alpha" under the [package] section in your Move.toml

    WARNING! Features released under alpha will be unstable! Each time you update the compiler, any code in edition = "2024.alpha" might fail to compile, even though it previously did. We definitely want folks to try out the features here! But with the unstable nature of the code, it should not be used in scenarios where you will need to have a persistent source file; for example, you should not use this edition if you want your package to work with source verification (that is if you want to be able to match your source code with some published bytecode package)

  • Once features have stabilized a bit, and all breaking changes have been added, we will release a release candidate edition, i.e. edition = "2024.rc". Unlike alpha, code written in this edition should not break when you update your compiler. While that is our goal, keep in mind it still is a rc and things might break depending on bug fixes or changes related to feedback. In short, rc will be more stable but be wary that there might be some breakages.

  • Sometime later in the year, we will finalize the edition and edition = "2024" will become the default for all new Move packages! At that point, any further changes or breakages will come in a future edition.

Outlined below are the planned features and changes. If you have suggestions or feedback (either for the outlined features or for new features entirely), please let us know!

Major Features

Method Syntax

Method syntax is a syntactic transformation that allows for functions to be called “on” a value rather than directly from a module.
For example

let c2: Coin<SUI> = c.withdraw(10);

which would expand to

let c2: Coin<SUI> = sui::coin::withdraw(&mut c, 10);

There will also be additional syntax for adding new methods, without having to declare a new function, using the new use fun alias declaration. See the issue for more details.

Index Syntax

Building on Method syntax, we will add syntax for index accesses depending on the type of access. Tentatively:

  • &x[i] expands to x.borrow(i)
  • &mut x[i] expands to x.borrow_mut(i)
  • x[i] expands to *x.borrow(i)
  • x[i] = v expands to x.assign(i, v)

Macro Functions

Higher-order functions (such as map, filter, fold, for_each, etc) are useful in many languages for concisely transforming collections. Move does not have lambdas (or closures or function pointers), which makes defining these sorts of operations impossible.
Macro functions will allow for Move to mimic these sort of operations, without supporting the behavior at runtime. The body of the macro mimicking the "higher-order function" will get inlined at each call site. And the call site can provide a "lambda" that will be substituted in as the macro is expanded. For example

let v2 = v.map!(|x| x + 1);

or

v.for_each!(|x| foo(x));

The "lambdas" additionally will support control flow through break and return.

Enums

Enumerations allow you to define a single type that may hold multiple different shapes of data. Unlike structs which always have the same fields, enums can have different fields depending on the variant of the enum. For example, enum Option<T> { None, Some(T) } the variant None has no fields and the variant Some has a single field of type T.
Move will allow destructuring enums using match expressions. Some examples of enums in Move are the following:

public enum Color {
    RGB { red: u8, green: u8, blue: u8 },
    HSL { hue: u16, saturation: u8, lightness: u8 },
    Hex(u32)
}

public enum Option<T> {
    None,
    Some(T),
}

public fun is_rgb_color(color: &Color): bool {
    match (color) {
      Color::RGB { red: _, green: _, blue: _ } => true,
      _ => false,
    }
}

const EOptionIsNone: u64 = 0;
public fun unwrap_some<T>(option: Option<T>): T {
    match (option) {
        Option::Some(x) => x,
        Option::None => abort EOptionIsNone,
    }
}

Move is adding support for basic high-level enums that have similar visibility rules to structs in Move today; the enumeration type is publicly visible, just like struct types, but the variants of the enumeration are not public, much like fields. But, we have plans to add public variants in the future. Similarly, enumerations cannot be recursive at release, but we have plans on supporting this in the future.

Smaller Improvements

public(package)

Since friend declarations can only be made within the same package, it feels a bit strange to require explicit friend declarations.
public(package) will replace public(friend), and the compiler will do the rest, eliminating the need for explicit friend declarations.

Positional fields

For simple wrappers, it can be annoying to have to declare field names. Positional fields can make this a bit less tiresome, e.g.
struct Wrapper(u64)

Postfix has ability declarations

With positional fields, it is a bit awkward to read has declarations in the middle of a declaration. As an alternative, has can now be written after the fields. For example, both will be valid:

struct Wrapper1 has copy, drop, store (u64)

struct Wrapper2(u64) has copy, drop, store;

Type inference holes _ on type annotations

With type directed programming, often you need to annotate a variable declaration or provide type arguments. But, sometimes you really only need to annotate on specific type, but the other types can be inferred. _ will be added to allow that type to still be inferred, even when other parts of the type are annotated. For example

dynamic_field::borrow_mut<address, Coin<SUI>>(&mut id, owner)

could be rewritten as

dynamic_field::borrow_mut<_, Coin<SUI>>(&mut id, owner)

where the _ would be inferred as address

break with value

While Move is an expression based language, it is cumbersome to extract values from a loop concisely. In the 2024 edition, break will be able now take a value. This should help code be more concise and less nested. For example:

let mut i = 0;
let first_over_10;
loop {
    if (v[i] > 10) {
        first_over_10 = i;
        break
    };
    i = i + 1;
};
first_over_10

can be rewritten as

let mut i = 0;
loop {
    if (v[i] > 10) break i;
    i = i + 1;
}

Named blocks with enhanced control flow operations

Move 2024 supports naming loop, while, and normal blocks, allowing for more-complex control
flow.

Previous code with nested while loops (such as this simplified excerpt from deepbook) would need to
set a flag to break both loops:

let mut terminate_loop = false;

while (...loop_condition...) {
    while (...inner_condition...) {
        ...
        if (...break_condition...) {
            terminate_loop = true;

        }
        ...
        if (terminate_loop) {
            break;
        }
    }
    if (terminate_loop) {
        break;
    }
}

Now we can directly name the outer loop and break it all at once:

let mut terminate_loop = false;

while (...loop_condition...) 'outer: {
    while (...inner_condition...) {
        ...
        if (...break_condition...) {
            terminate_loop = true;

        }
        ...
        if (terminate_loop) {
            break 'outer;
        }
    }
}

This will immediately break to the outer loop, allowing us more-precise control flow when we'd like
to escape from loops.

This feature also works with normal loop forms, including breaks with values:

let y = loop 'outer: {
    let _x = loop 'inner: {
        if (true) {
            break 'outer 10;
        } else {
            break 'inner 20
        }
    };
};

In this toy example, y will take on the value 10 because the first break will break the
'outer loop with that value.

Finally, this feature can be apply to normal blocks in Move, but instead utilizes the return
keyword. This can be useful when sequencing a block of code that may need early returns with values.

public fun auth_user(auth_one: EasyAuth, auth_two: AuthTwo): u64 {
    let auth_token = 'auth:  {
        let maybe_auth_token = try_auth(auth_one);
        if (valid_auth(maybe_auth_token)) {
            return 'auth unpack_auth(maybe_auth_token);
        }
        // ... more complicated code involving auth_two
    };
    // ... code using the auth_token
}

While we do not expect programmers to use named blocks with return in everyday cases, we anticipate
that they will ease the development and usage of macros significantly.

Breaking Changes

public struct

While struct declarations in Move can currently only be “public”, they do not currently require the visibility modifier when declared. To make room for a future where struct types have visibility other than “public”, we will be requiring public on all struct declarations in the 2024 edition.

let mut

Today, all local variables in Move can be assigned x = e and mutably borrowed &mut x. While Move has been influenced by Rust greatly (especially in regard to references and the borrow checker), we did not see the need to require variables to be annotated as mut before being modified or mutably borrowed; our reasoning being that you could always look locally to check for assignments or mutable borrows.
In the 2024 edition however, the new “method syntax” feature will automatically borrow locals when some circumstances. For example, c.withdraw(10) might expand to coin::withdraw(&mut c, 10). The point being that c is being mutably borrowed implicitly, so you can no longer check locally for modifications to local variables when reading Move code.
To improve readability and understandability in the presence of method calls (and any other potential language features down the line), we will be requiring mut annotations to be added to all local variables if they are assigned or mutably borrowed.

New Keywords

Move 2024 will add new keywords that were previously accepted as identifiers. The list of new keywords is:

  • mut
  • enum
  • type
  • match

To help with any migrations of existing fields, functions, or local variables with these names, new syntax has been added that lets you use a keyword as an identifier, for example

let `type` = 0; `type` + 1

In short, any keyword can be used as an identifier by escaping it in backticks, `.

Namespace Revisions

In legacy Move, addresses, modules, module identifiers, and locals all live in separate namespaces.
This allows for users to write rather confusing code, such as:

module a::b {
    public fun a(a: u64): u64 {
        a
    }
}

module a::a {
    fun a(a: u64): u64 {
        if (a == 0) {
            a::a::a(1)
        } else {
            a * a(a - 1)
        }
    }
}

While nobody would write this specific program, there are some cases on-chain where we have found
name collisions. Move 2024 combines address, module, and non-function module member namespaces (leaving locals and functions to
their own namespace). The code above will now produce the following error:

error[E03006]: unexpected name in this position
   ┌─ tests/move_2024/naming/separate_namespaces.move:10:13
   │
10 │             a::a::a(1)
   │             ^ Expected an address in this position, not a module

This is because the a name for our module shadows the a address alias.

Similarly, use statements that both define names will now produce errors. For example, the following
use statement use a::a::{Self, a}; will produce an error:

   ┌─ tests/move_2024/naming/separate_namespaces.move:17:26
   │
17 │         use a::a::{Self, a};
   │                    ----  ^ Duplicate module member or alias 'a'. Top level names in a namespace must be unique
   │                    │
   │                    Alias previously defined here

This namespace evolution is toward making it easier to work with enums and enum variants, but we
also hope that it will improve code clarity and help programmers avoid ambiguity.

Other Move Changes

While not related directly to compiler editions, we do have other new features coming to Move!
The final version of these features will not be gated by any edition, and will be available across all editions. (Though as they are developed, some of them might be gated by editions until everything is finalized)

Major Features

Auto Formatter

A long requested feature has been an auto-formatter for Move. We are still in the early stages of planning and starting this work, but it is in the works.

Linting Framework

Still in the early days, but we are working on the a linting framework for Move. Most of the lints today are geared towards best practices in Sui, but we will add other lints over time.
If you have feedback for lints or have suggestions for future lints, please open an issue!

Smaller Improvements

@tnowacki tnowacki added Type: Major Feature Major new functionality or integration, which has a significant impact on the network devx move design labels Oct 2, 2023
@tnowacki
Copy link
Contributor Author

tnowacki commented Oct 2, 2023

Each major feature will get its own issue as we progress, but in the meantime, don't hesitate to ask if you have any questions!

@rockbmb
Copy link
Contributor

rockbmb commented Oct 18, 2023

Thank you for the report. I am of the opinion that these changes will be an improvement to Move.

I have a couple of questions:

  1. Are these changes going to be merged back into https://github.com/move-language/move/, or will this "break compatibility", so to speak?
  2. From the Sui Discord, in the #move-lang channel, I got the impression work on sui move prove is more or less frozen. These changes will require work to play nice with the Move Prover.
    • Is this planned, or will the Move Prover remain dormant for now?

@tnowacki
Copy link
Contributor Author

@rockbmb, great questions, thanks!

Are these changes going to be merged back into https://github.com/move-language/move/, or will this "break compatibility", so to speak?

These changes will not be merged back into https://github.com/move-language/move/ (except perhaps into our own branch over there, still TBD). As far as compatibility goes, Sui has had its own development stream of Move for some time now, as do all other deployments. I do not know of any deployment of Move currently tracking main in that repository.

From the Sui Discord, in the #move-lang channel, I got the impression work on sui move prove is more or less frozen. These changes will require work to play nice with the Move Prover.

Is this planned, or will the Move Prover remain dormant for now?
The Move Prover will remain mostly dormant. There will be minimal work done so that anything that has already been working with the prover will continue to do so, but we do not have plans for new development in that area at this time.

@rockbmb
Copy link
Contributor

rockbmb commented Oct 25, 2023

@tnowacki thanks for the reply, Todd!

I did not know that Sui has had its own stream of Move; I've found it at ./external-crates/move.

Regarding the prover, it is good to hear that the team would like to at least maintain the tool so that specifications that already work can continue to do so 👍

@thounyy
Copy link
Contributor

thounyy commented Nov 11, 2023

Would it be possible to add the option to make global constants and arbitrary struct fields public by default?

@tnowacki
Copy link
Contributor Author

Regarding the prover, it is good to hear that the team would like to at least maintain the tool so that specifications that already work can continue to do so

I wanted to note that we have decided to sunset support for the prover.

@rockbmb
Copy link
Contributor

rockbmb commented Jan 11, 2024

@tnowacki so I've seen! I just got a strange error caused by the Sui framework's sui::prover module being empty, and eventually ran into #15480.

I completely understand the decision - I have a few open issues related to the prover, I'll close them.

@tnowacki
Copy link
Contributor Author

tnowacki commented Jan 12, 2024

@tnowacki so I've seen! I just got a strange error caused by the Sui framework's sui::prover module being empty, and eventually ran into #15480.

I completely understand the decision - I have a few open issues related to the prover, I'll close them.

Do you have a link to those issues?
Just making sure those are on the Sui repo, not the main Move repo. As far as I know, Aptos is continuing to work on the prover, it is just no longer a priority for us.

@rockbmb
Copy link
Contributor

rockbmb commented Jan 12, 2024

@tnowacki

Do you have a link to those issues?

Here's a GH query to this repository's issues that shows them:
https://github.com/MystenLabs/sui/issues?q=is%3Aissue+is%3Aclosed+label%3Amove+author%3Arockbmb

@rockbmb
Copy link
Contributor

rockbmb commented Feb 10, 2024

Would it be possible to add the option to make global constants and arbitrary struct fields public by default?

This would be nice.

Two other ideas:

  1. constants should be exportable and usable outside of the module they are defined in, and
  2. a bit more liberty when defining consts. Perhaps arbitrary functions shouldn't be allowed when defining a const, but other consts perhaps should.

@tnowacki
Copy link
Contributor Author

Would it be possible to add the option to make global constants and arbitrary struct fields public by default?

This would be nice.

Two other ideas:

  1. constants should be exportable and usable outside of the module they are defined in, and
  2. a bit more liberty when defining consts. Perhaps arbitrary functions shouldn't be allowed when defining a const, but other consts perhaps should.

We will be allowing constants to be used by other constants, but only within a given module right now. After that, we are looking at enabling it within a package.

Long term, we are torn about outside of a package given the potential uncertain expectations around package upgrades and constants.

@rockbmb
Copy link
Contributor

rockbmb commented Feb 12, 2024

We will be allowing constants to be used by other constants, but only within a given module right now.

Great, an improvement is always an improvement.

After that, we are looking at enabling it within a package.

Even better!

Long term, we are torn about outside of a package given the potential uncertain expectations around package upgrades and constants.

That's understandable - in fact, I was only implicitly considering the first two cases, as the third is, as you say, trickier and perhaps not worth the potential headaches.

@rockbmb
Copy link
Contributor

rockbmb commented Mar 2, 2024

@tnowacki I'm currently trying out Move's edition = 2024.alpha on a couple of our packages, and this question came up.

  1. Consider Sui Move package P, whose Move.toml has, under [package], the key/value pair edition = 2024.alpha.
  2. Then, package P is published, resulting in an UpgradeCap, call it U, which can be used to upgrade said package.
  3. In the meantime, 2024.rc1 is released, and package P is moved from 2024.alpha to 2024.rc1.

Question: can I use U: UpgradeCap to upgrade package P to P'?

@tnowacki
Copy link
Contributor Author

@tnowacki I'm currently trying out Move's edition = 2024.alpha on a couple of our packages, and this question came up.

  1. Consider Sui Move package P, whose Move.toml has, under [package], the key/value pair edition = 2024.alpha.
  2. Then, package P is published, resulting in an UpgradeCap, call it U, which can be used to upgrade said package.
  3. In the meantime, 2024.rc1 is released, and package P is moved from 2024.alpha to 2024.rc1.

Question: can I use U: UpgradeCap to upgrade package P to P'?

No use of edition will affect upgrades. Some tangential things to keep in mind though:

  • Source validation/verification (checking source files produce the specified bytecode) might be a bit bumpy as you move across versions. This is because the compiler might produce different output for the same source code (as you update the compiler or move across versions). We are working on making this manageable in the toolchains, but it is still a work in progress as far as I know.
  • Enums are the only feature on this list that require bytecode changes. As they get released, the bytecode operations will be limited first to devnet, and later testnet, before being turned on for mainnet.

@rockbmb
Copy link
Contributor

rockbmb commented Mar 11, 2024

@tnowacki I understand, now - thank you for the reply, Todd.

@fl0ydj
Copy link

fl0ydj commented Mar 20, 2024

Hey @tnowacki :)
Posting this also here besides the enum PR for vis:

Love the concept of enums and for our project it would make things so much simpler.
We are currently in the architecture and early prototyping phase and plan to go live on mainnet in August.

2 questions accordingly:

Would you advise us to do our architecture with enums considering our mainnet launch? A bit scared of the required byte code changes?
Any estimate when they will be available as part of Move 2024 to prototype with? Only estimate we found was Q2.
Thanks!

@tnowacki
Copy link
Contributor Author

Would you advise us to do our architecture with enums considering our mainnet launch? A bit scared of the required byte code changes?

I'm not sure I would be able to make that call for you, sorry.

Any estimate when they will be available as part of Move 2024 to prototype with? Only estimate we found was Q2.

Our current plan is to have them in devnet by the end of May.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
design devx move Type: Major Feature Major new functionality or integration, which has a significant impact on the network
Projects
None yet
Development

No branches or pull requests

4 participants