#ask How should we improve Motoko?

How would you use that? Would you assign it to a variable?

Is it different than this that you can do today?

‘’’
let z = (x : Nat) : Nat {x+1};

‘’’

1 Like

Yeah I guess that’s pretty much the same as what’s already available. I just have noticed that when I pass functions as parameters it gets pretty messy. So assigning them to a variable would probably solve the problem.

I do like the distinction between → as the type identifier and then => as the implementation

The below looks kind of nice.

let z : (x: Nat) -> Nat = (x : Nat) : Nat => x+1;
1 Like

A way to ignore the return value of a group of functions without adding the ignore keyword to each line. I think something similar to this image would be suitable, where you could ignore the return values of a whole code block.

I was able bundle the return values into one and use a single ignore key because the values where boolean

Otherwise you would have to use the ignore key for every line.

4 Likes

I still haven’t quite figured why ignore is even a thing, like I understand why they added it, but I’m not sure it brings more benefits than annoyances, but I might be missing something.

Yeah….I’d love a primer on what it is useful for.

I just used it in my work on the cycles faucet. The faucet hands out cycles wallets, and these wallets require some canisters that are created somewhere. Previously, this was all done outside of the faucet in a script that called dfx ledger to create the canisters. Now, whenever I register_codes to the faucet and want to create create_n canisters to the queue, it runs this:

while (create_n > 0) {
    ignore create_canister_to_reserve();
    create_n := create_n - 1;
};

The ignore just dispatches an async call, but doesn’t wait for a response to arrive. This means that I get a response from the register_codes call without having to wait for all those canisters to be created, and all the create_canister_to_reserve calls run in parallel (I don’t await the result of one to start the next one).

IDK if there’s a better way to do it (I’m new to Motoko), but that’s the way I made it work with.

2 Likes

Ahh…I see. I always just call it something like


let create_future = create_canister_to_reserve();

…and then it gets discarded after the block. It would be great to know from @rossberg if one version has performance advantages over the other.

1 Like

From the documentation about ignore here:

Then the semantics is equivalent to let _ = <exp> : Any.

So I guess technically our versions are equivalent, but maybe the ignore approach makes a bit easier for the compiler (or reader) to figure out that the result can be discarded.

syntactic sugar for type changes when upgrading

So far, an ignore expression is literally compiled as the desugaring shown in the docs, so there is no difference other than brevity and clarity.

2 Likes

syntax:
golang style for loops
more concise syntax for pattern matching & error handling

other:
keep improving documentation
framework for unit testing
better package manager
vscode plugins

1 Like

in addition, if it can auto organize import that would be neat too.

In almost all my frontend projects I use Prettier and prettier-plugin-organize-imports. Would be cool if I could have something similar for my Motoko projects and code.

nvm it has been solved :star_struck:

https://twitter.com/kylpeacock/status/1563256664819507201

3 Likes

Whoa didn’t know this existed…

It’s brand new I think :rocket:

Regarding ignore:

Q1: Why do we have it?

A1: If you didn’t have ignore, you’d be writing let _ = in its place, and as @rossberg mentioned, they are equivalent from the viewpoint of the compiler.

Q2: Why do we have to write either of these, and why can’t we just ignore return values without extra syntax?

A2: Well, our type system needs more details, and perhaps if the type system made more distinctions other than () vs non-() return types, we could. For example, Rust has a more nuanced view of return results’ types:

So in Rust world, the Result type would require an ignore-like syntax, but other primitive types may not.

Q3: Is that more nuanced approach an improvement?

A3 (my own at least): Perhaps! Especially for data structure APIs that may return extra success information that users do not always need (as in the graph API mentioned by @tomijaga above)

Which IC features are the most challenging or most needed?

2 Likes

The short answer is: both to protect against accident and to make the intent explicit. Forgetting to use a result is a fairly common error, and neither compiler nor human readers would otherwise know if it was intended.

As a secondary effect, it is also meant to encourage less error-prone API design, i.e., avoid functions that both have non-trivial side effects and return results. If your interfaces are designed with that in mind (similar to the query vs update distinction), then you won’t need ignore.

1 Like

An impractical stance, but I admire the principles. :slight_smile:

I think the Rust compiler has a good compromise: It complains for Result types that could contain unhandled errors, but does not go so far as to assume that every result is worthy of being this pedantic.

BTW, for a data structure that replaces old stuff with new stuff, it’s natural to return the old stuff as an optional result. For instance, Trie.replace in base. So, that’s a good situation for the more relaxed Rust conventions in Motoko, and something that I would appreciate myself, personally.

1 Like

Unrelated, but one of the biggest headaches I had when initially developing on Motoko was dealing with the whole functional vs imperative data structure dilemma. I don’t want to think about it, but I have to if I want to work with stable variables, which most developers invariably will want to. The syntax is really quite different between the two.

Could you ever envision a world where imperative data structures could be stored in stable variables? Is there no theoretical way for the Motoko compiler to implicitly convert an imperative data structure to a functional one?