Open mikhail-turilin-dfinity opened 2 years ago
Yes, something like this would be nice but note that:
1) F# pipelining operators works best with curried functions, and most of the functions in the current base lib are not curried. Moreover, declaring curried functions in Motoko is currently awkward and would require more sugar.
2) Literally adding methods to, e.g. the Iter interface bloats the representation of these objects, which, in the current design of Motoko, are literally records of fields (some of which are functions/methods). That's a very different representation from objects in C# and Java, which just carry the fields of the object, but share all method implementations via a method table pointer (so that adding a method leaves the object representation unchanged).
This FR is related to https://github.com/dfinity/motoko/issues/2537, which proposes adding something akin to C# extension methods (a.k.a F# augmentations) to let you invoke a static function using the dot notation, passing the receiver as the first argument.
It's a little easier to understand your original code if you break it up using a let.
// Refactor using `let` and explicit type arguments and untyped lambdas
public shared ({caller}) func allPostsLet1() : async [Nat] {
let vals = posts.vals();
let filtered = Iter.filter<Post>(vals, func p { p.principal == caller});
let ids = Iter.map<Post, Nat>(filtered, func p { p.id });
Iter.toArray(ids)
};
// Refactor using `let` and implicit type arguments, but typed lambdas
public shared ({caller}) func allPostsLet2() : async [Nat] {
let vals = posts.vals();
let filtered = Iter.filter(vals, func (p : Post) : Bool { p.principal == caller});
let ids = Iter.map(filtered, func (p : Post) : Nat { p.id });
Iter.toArray(ids)
};
This is actually more efficient than using any higher-order combinator (in the current implementation) since we don't inline those yet and the all the calls will wind up being direct calls.
But yes, having to name the intermediate results is less concise.
https://m7sm4-2iaaa-aaaab-qabra-cai.raw.ic0.app/?tag=3719590015
Many func p { p.principal == caller}
-style lambdas can be simplified by func p = p.principal == caller
-style.
To throw in my two rappens:
could we add a syntax for anonymous function arguments, like in Scala? E.g., I would love to be able to write
_.principal == caller
instead of
func p = p.principal == caller
when the type of the lambda-expression can be inferred unambiguously.
Problem
Working with collections is verbose and difficult. For example, I want to filter blog posts and return the ids:
The example is difficult to understand, write, and debug.
Solution 1: Operator piping
We can use something like a pipe operator from F# to improve readability with Swift-style anonymous functions:
I think this example is much easier to read and understand.
Solution 2: Class-level higher-order functions
If a pipe operator is too much to ask, we can at least make higher-order functions a part of the class for chaining