Sorry, I didn’t meant to write lazy - should have avoided copying from elsewhere but can’t edit the original.
So the example I intended was something like this (the tail call was accidental)
private func flip() : async Bool {
let blob = await Random.rand();
blob[0] & 1 == 1
};
shared func Coin() : async {#heads; #tails} {
if (await flip()) #heads else #tails; // does two real awaits.
}
I still think it’s highly desirable to let users abstract code that does awaits, without taking the performance penalty of this code.
There are many examples of users using local async functions to abstract code and taking and unwittingly taking a large performance hit as a result, because the only means of abstracting code that communicates is to introduce a spurious additional await.
C# has partial classes than can span several files, thought this was originally introduced in order to separate generated code from user code for e.g. GUI apps.
It would be a shame if splitting your code across files, by introducing async local functions, causes you to take a performance hit. I guess that one could be solved by optimizing tail calls only, but still.
This language feature would massively help with Proposal to Adopt the Namespaced Interfaces Pattern as a Best Practice for IC Developers. Right now a bunch of logic needs to be duplicated to support legacy interfaces and the ability to abstract code into lazy private functions would fix that. Would queries be able to call lazy functions? See the pull request on the 5th post to see how annoying this is if you want two exposed functions to call these same code or have the same query at two endpoints.
@skilsare I had a look at that pull request but I think those occassaion, where you want to factor our some common code in two queries can just handled by introducing a third, local, non-async function, called from both (but perhaps you considered that and reject it).
The kind of examples I was thinking about are described here
But I’ve seen quite a few in the wild now and it pains me to tell users to inline their code.
Another example is the random_maze sample, that I had to rewrite awkwardly using a loop, not recursion, in order to be able to generate random numbers on demand without running like a drain.
FWIW, when I think of better abstraction facilities for async, I have in mind enabling more powerful composition of futures and future-based combinators. If those can be optimised, even better, but it should remain semantically transparent.
Agreed. I think something like a JS-style Promise.all would be very useful, at a minimum.
I still think it’s highly desirable to let users abstract code that does awaits, without taking the performance penalty of this code.
OK, this example makes more sense. Actually, even if flip is public instead of private, it’d be great if no performance penalty is incurred if flip is called from another method in the same canister.
I know that this is a little bit late; but why would a developer want to learn a new language (i.e. Motoko) for programming IC; when you would have a portable-to-other platform language (Rust)?
I have programmed in both (Motoko & Rust) and I don’t really see a clear distinguishing killer use case for new developers to learn a completely new language which they can take nowhere else.
From my experience, Rust is a huge blocker to adoption. It is …opinionated?.. to a significant degree and many competent developers(me included) bounce off of it. Motoko is a bit more ‘high level’ and approachable by your run of the mil Javascript dev that wants to get into writing smart contracts for the IC.
As an aside, I’ve actually had a couple of conversations about trying to find some alignment around using Motoko across a broader set of blockchains…specifically…ETH2 once they figure out how the hell they are actually going to do smart contracts on ETH2.