[RFC FS-1063 discussion] - Support let! ... and! ... for applicative functors #335
Replies: 35 comments
-
There are a couple of outstanding questions on the RFC, although I do have some conservative defaults in case we can't find a better solution here. Paraphrasing from the WIP RFC (I recommend you go there for the full detail):
|
Beta Was this translation helpful? Give feedback.
-
We were talking in the F# slack about this. There's a bit of discussion about the best way to make anduse! a little bit clearer. evgeny passingly suggested
because it aligns with my current best idea (not saying it's a good one) is to use the same style as the for..do or try...with pattern in CE's.
I agree with not changing Bind for now. Leave that to the other RFC to decide. |
Beta Was this translation helpful? Give feedback.
-
Unless I've missed it, the current RFC doesn't have a syntax for applicative I'm ok with having to do |
Beta Was this translation helpful? Give feedback.
-
Not by design; an oversight. I'll give this some more thought when I can, it's a reasonable question and it's something we already have for existing CEs, but I suspect there's a case for saying this is somewhat confusing: ce {
let! x = foo
do! bar
and! y = baz
return x + y
} |
Beta Was this translation helpful? Give feedback.
-
It'd have to be Also, I would expect |
Beta Was this translation helpful? Give feedback.
-
Good point. Sadly, |
Beta Was this translation helpful? Give feedback.
-
there's always "also" :P |
Beta Was this translation helpful? Give feedback.
-
Agreed, the compound keywords aren't pretty. I assume non-compound Some time ago I made a suggestion that would "do the right thing" without extra keywords. Sadly, it'd be harder to implement and goes against the rationale for the current syntax. @voronoipotato Just keep adding |
Beta Was this translation helpful? Give feedback.
-
@mistiara, yep, I spoke to @dsyme about this and he was keen to avoid magic and keep it as a straightforward syntactic translation. Certainly a more advanced system that knew about the functor/applicative/moand laws would be a large break from CEs as they stand now. |
Beta Was this translation helpful? Give feedback.
-
Ideally we can use new keywords. If not then opening the door to magic can at least open some optimizations that will make things much more performant :P. |
Beta Was this translation helpful? Give feedback.
-
Note you could use ce {
let! x = foo
and! () = bar
and! y = baz
return x + y
} though it's not ideal. However |
Beta Was this translation helpful? Give feedback.
-
One thing which would be really useful is adding custom keywords to applicative computation expressions. We think this can be done quite easily by mirroring the existing
|
Beta Was this translation helpful? Give feedback.
-
In summary: custom keywords force the builder to package up the variables into a tuple, then future functions act on that tuple. This is similar to how MaintainsVariableSpacingUsingBind works. |
Beta Was this translation helpful? Give feedback.
-
This looks reasonable. |
Beta Was this translation helpful? Give feedback.
-
A comment about In my talks at Build Stuff I showed examples of using adaptive view expressions with an experimental version of Fabulous. Slide 66 of this deck The reason I mention it is that the Here, it would be useful that |
Beta Was this translation helpful? Give feedback.
-
That makes sense, as another question how do you feel about an apply method being used for builders rather than a map + zip? Is there any scenario where you might want "and!" to do something different than zip? I personally think having an explicit method for a builder term may be useful and a little closer to the way CE's are currently done. I feel this way even if it means typing map zip each time for the definition, however I'm open to dissent. |
Beta Was this translation helpful? Give feedback.
-
I don't feel too strongly about it since it's just a convention to follow for a mostly advanced case. But this is more of a @dsyme question since this also unifies with another language suggestion and isn't merely about applicatives. |
Beta Was this translation helpful? Give feedback.
-
Note that the This is not a problem in the implementations as the |
Beta Was this translation helpful? Give feedback.
-
@cartermp I still think we should be able to use CE's without a library. If I was talking to someone about how CE's are great and you can do cool stuff with them. Then they might say "oh cool it's neat that its so generally applicable, how would I use it for option?" and I would have to tell them "I don't really know" because I don't. Do you know what the issue is for that Language Suggestion? |
Beta Was this translation helpful? Give feedback.
-
There isn't one. I don't think it makes sense to "use CEs without a library". FSharp.Core is a library, as are others that define their own CEs. So I'm not sure what the request is here. |
Beta Was this translation helpful? Give feedback.
-
That's of course a cute reply because it's * technically correct * ("the best kind of correct") but for all we care FSharp.Core is the F# framework, many things from equality to function values would not work if you don't load it. We all know what @voronoipotato means and I believe it has been an ask for years. Maybe it would help if there's a well-founded explanation * exactly why * CEs are not welcome in FSharp.Core. |
Beta Was this translation helpful? Give feedback.
-
Sorry if my reply came off that way, but no, I didn't know what @voronoipotato was referring to here. If this is specifically about including an |
Beta Was this translation helpful? Give feedback.
-
Fair enough, sorry for being snarky. @voronoipotato if you want it's all yours to file ;) |
Beta Was this translation helpful? Give feedback.
-
Well so here's part of it, for some things, Option being one of them, we have a bind and return functions already defined. When in the standard library we already have the functions defined, why don't we make a consumable CE for that? It is somewhat confusing to me that we often have the functions defined, but we don't provide any plumbing so that people can use them with a CE. I of course see the value of making your own CE's sometimes, and I see the value of custom operations etc, but it seems weird to me to have these functions defined but not use them in the construct specifically designed one to one to use them. I'll write up a lang suggestion, I think it would be sad to flesh out CE's further to have them continue to be overlooked for lack of a default implementation. Also yes, Nino was right, I was asking with the intent of including in FSharp.Core. I was using libraries as a shorthand for "external libraries" or "not standard library". Apologies for my loose language. |
Beta Was this translation helpful? Give feedback.
-
I think it's a good overall proposal/discussion - let's have it in a new issue there. |
Beta Was this translation helpful? Give feedback.
-
It seems to me that this new feature as it is in the preview, "calculates a method name" by filling the N in BindNReturn for instance. This approach is completely new to the F# compiler, AFAIK this the first time the compiler calculates a method name by inserting a natural number. That feels really odd to me. I think a better option is to use existing and simpler mechanism, like overloading. You can look in all cases for:
where So, instead of defining:
you define:
Finally, this has the potential advantage that future extensions to the F# compiler that allow to handle N cases in a specific construction, would be able to plug directly here. Actually it might be currently a way by using existing techniques like reflection, SRTP or type providers. So I propose we adjust it to use this model. Same goes of course for others N methods. |
Beta Was this translation helpful? Give feedback.
-
I did consider this. My main concern here is that error messages will be substantially worse for no actual gain to the end user. I have a secondary concern that type inference will be worse as the overlaod resolution can't commit eagerly. So given that overload resolution has these potential drawbacks, I couldn't see an alternative. You're right that this adds a new idiom. |
Beta Was this translation helpful? Give feedback.
-
I think my main fear is that they may have sub-standard debugging and performance, and that we won't be able to fix this at a later date (e.g. once generalized state machines lands). I also have a concern that we will suffer an explosion of cases (option, asyncOption, taskOption, listOption, arrayOption, result, asyncResult, taskResult, ...) |
Beta Was this translation helpful? Give feedback.
-
I understand your concern, although for error messages in the scenario I proposed of having the overload over the tuple argument, the error message will be clear. Maybe not that clear if we do different number of parameters. In the case I propose there will be always 2, so there's no way you can mix the function parameter with one of the arguments for instance. Maybe we could enable both ways, the current design plus the overloaded tuple one. I'm thinking that there might be cases where you can, using the above mentioned techniques, come up with an optimal solution that works for N arguments. This will avoid having to define boilerplate for 30 arguments that would certainly end up with code generators for the Builders. If 30 arguments sound not realistic, let me add that at Jet it was very common to have such number of fields. |
Beta Was this translation helpful? Give feedback.
-
You could create an edict not to cover the combination of two, and be explicit about how you won't/can't because of the multiplicative growth. It's certainly not going to have worse performance than the one I would write :V. Why wouldn't you be able to fix them at a later date? |
Beta Was this translation helpful? Give feedback.
-
Suggestion: fsharp/fslang-suggestions#579
RFC: https://github.com/fsharp/fslang-design/blob/master/FSharp-5.0/FS-1063-support-letbang-andbang-for-applicative-functors.md
Implementation: dotnet/fsharp#5696
Beta Was this translation helpful? Give feedback.
All reactions