FSharpPlus contains traverse and sequence. I assume they are monadic. Is there support for applicative versions? If not, would it make sense to add?
For details, see https://fsharpforfunandprofit.com/posts/elevated-world-4/#applicative-vs-monadic-versions-of-traverse
That blog post is misleading.
Applicative and Monadic versions are different internally, in terms of implementation, externally the sole difference should be that the Monadic version requires that the type is a Monad.
Since all Monads are Applicatives our implementation is based on the latter, to avoid unnecessary restrictions.
But having said that, it's important to remark that contrary to what he says, both Applicative and Monadic implementations should return exactly the same results.
I think he's misusing the names to signify that the applicative implementation is based on Applicative Validation, which is a different Applicative for errors. See https://github.com/fsprojects/FSharpPlus/issues/51 which is already implemented.
PS: You are not the first person I know that got confusing after reading that post, I think we should ask the author to rectify. He should find another name to distinguish, not just Applicative, maybe Accumulative.
Thanks!
If possible, I'd prefer to stick to the built-in Result instead of a custom Validation type. If I want to, say, sequence Result<'a, 'b list> list to get Result<'a list, 'b list> while collecting all errors, can I do that using functions in FSharpPlus?
The meaning of Result is different why using your custom type and then as the final step converting to Result in order to implement the specific behavior.
Yes, I understand your concern but if you think about it, it's a logical problem.
We can't have two different implementations on the same type and use generic operators at the same time.
When you have two or more implementations on the same type there are 2 options:
You don't use generic operators, you create a specific operator for the implementation you want. This is the approach most non-generic F# libraries take, some of them use the same identifier for the function/operator but in fact they are different, they force you open different namespaces to switch between operators with the same identifier but different implementations.
Why didn't we choose the Accumulative errors as the default implementation?
Because it's not monadic. Had we choose that one we were not able to create a Result CE. Or we could have use the other implementation for the Monad as shown in the post, but that would break a lot of rules and would be impossible to reason about the code. Other libraries in other languages took the same path.
Now, we can consider some mixed functionality by providing non-generic operators namespaces. This is a path we never went into because this library started initially as a generic one, but it has evolved in the non-generic field as well, so at some point we could consider it. If you feel like, you can open a new issue proposing these changes.
Note this is not an issue that involves only applicative validation, same situation exists with seqs, lists, arrays (which have an alternative point-wise applicative ZipList), numeric types also have an alternative monoid that does multiplication instead of addition, function default to Writer but they could implement State as well.
Thank you so much for the clarification, @gusty! Category theory is not my forte (yet), but I think I follow enough of your explanation to see that it makes sense.
The problem with namespacing, of course, comes when you want to use operators. A relevant example in this case is the <*> operator. As I understand it, I can use the generic operators with Validation and get the expected behaviour (combining errors), is that correct?
It could still be the case that adding non-generic functions for Result<_, _ list> error combination improves FSharpPlus. However, I haven't used it extensively enough to know for sure. So I think that I'll try to stick with what's included first, and use Validation.toResult and Validation.ofResult to gradually introduce this to the codebase.
As I understand it, I can use the generic operators with Validation and get the expected behaviour (combining errors), is that correct?
Yes, but not limited to the generic <*>, if you don't open the FSharpPlus.Operators namespace (which is auto-opened with open FSharpPlus) you can use Validation's non-generic <*> which, as any non-generic operator, should benefit in theory of better type inference in some situations.
Adding functions for Validation as an extension to Result sounds less conflicting than opening some specific non-generic operators, but as you said we need to analize how it would play in a real code base.
So, have fun with Validation and your feedback will be much appreciated.
I have experimented some more now. Unfortunately it is not feasible for me to use Validation. I am using a library which accepts and returns Result all over the place, including in ways where it's very cumbersome for me to convert back and forth all the time.
It seems that for the time being, I'm stuck with non-generic operators on Result when I want to combine errors.
I've started to do some trial around mixing and matching:
https://github.com/wallymathieu/validation-studies
There is also https://github.com/fsprojects/Chessie that could be enhanced to perhaps suite your needs
I am using FsToolkit.ErrorHandling for all my error handling needs, which is much more complete (and I'd say gets out of your way more) than Chessie.
Let me know if you want my input on something specific. :)
Most helpful comment
That blog post is misleading.
Applicative and Monadic versions are different internally, in terms of implementation, externally the sole difference should be that the Monadic version requires that the type is a Monad.
Since all Monads are Applicatives our implementation is based on the latter, to avoid unnecessary restrictions.
But having said that, it's important to remark that contrary to what he says, both Applicative and Monadic implementations should return exactly the same results.
I think he's misusing the names to signify that the applicative implementation is based on Applicative Validation, which is a different Applicative for errors. See https://github.com/fsprojects/FSharpPlus/issues/51 which is already implemented.
PS: You are not the first person I know that got confusing after reading that post, I think we should ask the author to rectify. He should find another name to distinguish, not just Applicative, maybe Accumulative.