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
Adding Flow module #5830
Adding Flow module #5830
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I’m somewhat split on this. I don’t personally find myself using reverse function application very often but I don’t think that should be the deciding factor here. There are clearly a lot of people who do like this.
- Regular function application is unavoidable (even in the test for
|>
you use it) so this strictly increases the set of things users need to learn. On the other hand, as long as it is in a separate module and we do not start using it heavily in things like the GSG it is mostly opt-in and users can ignore it until they want to use it. - I wonder if
DA.Flow
is really a good module name. MaybeDA.Function
would be a better name? That’s where(&)
is in Haskell which is the identifier for reverse function application included inbase
.
What do you think @hurryabit @bame-da?
I'm split on this as well. I'm not convinced having both Also, should we decide to use it, we need to give it a fixity that plays well with other operators, particularly |
I think reverse function application is quite natural and as you said there are lot of people who like this. "Regular" function application tends to be quite hard to read and I find that I added it to separate module, so it would be - as @cocreature said - opt-in. I'm easy on the module name. I picked |
I would support this proposal if the function were named |
I'm totally fine with putting this in However, it is important that operator is |
|
I don’t think it makes a lot of sense to base decisions on the Haskell standard library for a couple of reasons:
|
I agree with @cocreature. I think it's perfectly fine to be influenced by other languages if it makes things easier, helps developers.
Following this I put together a little example in various languages. Java List<String> inputs = Arrays.asList("1", "2", "3", "4", "5");
inputs
.stream()
.mapToInt(Integer::parseInt)
.filter(x -> x % 2 == 0)
.sum(); C# string[] inputs = {"1", "2", "3", "4", "5"};
inputs
.Select(int.Parse)
.Where(x => x % 2 == 0)
.Sum(); JavaScript let inputs = ["1", "2", "3", "4", "5"];
inputs
.map(x => parseInt(x))
.filter(x => x % 2 === 0)
.reduce((x, y) => x + y); Scala val inputs = List("1", "2", "3", "4", "5")
inputs
.map(_.toInt)
.filter(_ % 2 == 0)
.sum Now convert this example into DAML: let inputs = ["1", "2", "3", "4", "5"]
_ = sum
$ filter (\x -> x % 2 == 0)
$ mapOptional parseInt inputs I think the examples above show that knowledge can be easily transferred between these languages. With DAML however, the logic is reversed and harder to follow. The proposed operator helps in this regard: let inputs = ["1", "2", "3", "4", "5"]
_ = inputs
|> mapOptional parseInt
|> filter (\x -> x % 2 == 0)
|> sum |
I'm personally quite fond of the "reverse function application" (never knew that was its name), and use it quite often, but I'm a bit skeptical of introducing a whole namespace for it. It's really easy to define as a user, and I think there is more value in telling people they can define it than in having yet another magic symbol in the standard library. It could be a great example in the docs of how to define custom operators and what fixity means and how to choose it etc. That could be a nice "aha" moment for users in driving home the point that these operators are not built-in magic, but regular functions with unfamiliar names.
Isn't DAML eager? I would expect evaluation order to work fine here if we just evaluate arguments left to right before applying a function, which I always thought was the definition of eager evaluation. I suppose I need to learn more about our interpreter. |
/azp run |
Azure Pipelines successfully started running 1 pipeline(s). |
Mathematically it is reverse, as normally the notation is like |
@garyverhaegen-da The problem I meant arises exactly because DAML is eager. Currently, the compiler immediately simplifies
@tamaskalcza-da Some branches of algebra use the |
Thanks @hurryabit, that's quite interesting. Am I getting this right that you guys find this notation convenient? If the module was renamed to Build |
Any news regarding this? @cocreature @hurryabit |
It seems that if you add the line |
/azp run |
Azure Pipelines successfully started running 1 pipeline(s). |
After carefully assessing the risk/reward profile of this change, we've come to the conclusion that the potential risk, as described above, does not clearly outweigh the benefit of adding |
Pull Request Checklist
CHANGELOG_BEGIN
andCHANGELOG_END
tagsNOTE: CI is not automatically run on non-members pull-requests for security
reasons. The reviewer will have to comment with
/AzurePipelines run
totrigger the build.
Introducing Flow module
Inspired by F#, the pipe system in Unix and Flow module in Haskell (see: https://taylor.fausak.me/2015/04/09/write-more-understandable-haskell-with-flow/) this PR is intended to add the most basic element, the forward pipe.
How to Use
I think the code here is relatively hard to read: https://github.com/digital-asset/ex-supply-chain/blob/master/src/main/daml/DA/RefApps/SupplyChain/QuoteRequest.daml#L327-L330
Using the forward-pipe we can improve it like this: