Roadmap V2.0
I think for V2.0 we can start discussing features here, before moving to a formal project.
What comes to my mind at the moment is:
-
Remove subsumption from Applicatives: Right now
seq"eats" all custom types implementingseq -
Same for Traversable, same situation: although this might not require a breaking change (todo find out)
-
Implement IEnumerable for Vectors, now that (1) has to be done.
-
New
ListTmonad transformer, possibly newSeqTas well, coded using a similar technique as the one used for Free Monads. -
Review computation expressions: autosense will be removed but we have to do something to make it easy for users to understand why a strict monad can't be used with the non-strict CE. Maybe make compilation fail when calling a TryWith?
-
Add ValueTuple, ValueTask and other latest addition to the framework. We can require a higher framework version
-
Consider suggestions by @dsyme regarding using the return type for type inference.
Am I missing something?
EDIT
-
Better task support (maybe an intermediate type like
Stepwith conversions to/from Task and its use in Bind an other methods for task). -
Consistent type parameters order, this would need to swap type parameters in
Validation<'err,'t>toValidation<'t,'err>.
I would like to organise namespace so that we: 1) can more easily generate docs per functional area (thinking of how operations is separated nicely into sections by banner comment, but API docs are resorted alphabetically and all at same level), 2) reduce possible instances by opening only what you're using.
The later requires to split the Invokables so that each implentation sits with the 'openable' type. Such as Arrow, or NonEmptyList, etc.
This should help considerably with compiler error messages as there is reduced implementations. I also wonder if it would help speed up compiler time?
I'm not exactly sure on how to arrange for optimal dev work flow, but could be that there is separation between common and exotic abstractions, or maybe that there is 'open common' and otherwise opt in for each type...
WDYT?
No doubt namespaces should be re-organized.
I personally don't see a big benefit on splitting Invokables in different namespaces, but at the same time I don't see any harm on doing that, since they are rarely used in client code directly.
Separation between common and exotic abstraction could be interesting but hard to draw the line.
One thing I definitely would like to change is a few extensions (like List.traverse) which lives in .Data right now. I think they don't belong there, since List is not in .Data.
So I would say, if/when you have a concrete proposal in mind please drop the details and we can analyze it, because this is the right time to do it ;)
https://github.com/fsprojects/FSharpPlus/issues/245
I would vote for defaulting to left for success right for error
You could consider removing functions that are part of FSharp.Core, e.g. List.singleton.
Also personally I'd like String.trimWhiteSpaces to be String.trim, and the current String.trim to be String.trimChars or something else, but that seems like a fairly indulgent breaking change, so I'm not expecting anything there 😅 (Alternatively, trimWhiteSpaces should ideally have lower-case s for consistency with String.IsNullOrWhitespace, but again, that's a fairly indulgent change.)
You could consider removing functions that are part of FSharp.Core, e.g. List.singleton.
Good point, based on the min version of F# core we could remove some now-redundant functions.
Regarding the String.trim suggestions, we can consider some breaking changes for V2, at the very least your alternative suggestion (consistency) should be implemented.
Regarding the String.trim suggestions, we can consider some breaking changes for V2, at the very least your alternative suggestion (consistency) should be be implemented.
Thanks! (For the record, my primary suggestion regarding String.trim is based on my experience of using String.Trim() (empty, trimming whitespace) a lot more often than specifying what I want to trim. That's why I suggest that to be the behaviour of the "simple" String.trim.)
Yes, definitely. I think more than 90% of the cases are white-spaces.
Perhaps there should be a doc site for v1 so that it's possible to browse the docs for v1 and v2 once v2 is available?
So perhaps a PR @cmeeren with the change?
Regarding seq and subsumption: Seems it's also happening with tryFind (foldable) and empty (functor). When used for Map<_,_>, it's subsumed as seq<KeyValuePair<_,_>> in both cases.
Let's discuss it in #322
Again regarding seq and subsumption: Does that mean that currently, length is an O(N) operation also for arrays?
Yes, if there is subsumption, but AFAIK in that case, the overload which is specific for arrays have priority, so it shouldn't. If it does it is a bug. These kind of things, have to admit, are hard to test. I do normally test it before committing with some prints to see which overloads get actually called.
I see. Is there no simple way of having automated tests for this? (I can't think of any at the moment. As you say, it's a hard thing to test.)
I know FSharpPlus is all about working at a higher abstraction level, but performance still matters, and it's important for me to have an idea about whether if I replace Array.length, List.filter and Set.map with the generic length, filter, and map, I get the same performance as the original functions, or if I get Seq.length, Seq.filter, and Seq.map which AFAIK has worse performance (much worse in the case of Seq.length since it's O(N)).
but performance still matters,
It is also about performance. Some cases are hard to optimize, but this is not the case.
I can tell you, in general all 3 main collections (Seq, list and array) are always optimized. Sometimes there are more but those 3 are the bare minimum.
I have some ideas of how to test this cases. One way could be to expand some macros, in form of comments, which expanded create side effects.
Feel free to open an issue about testing optimized overload.
When a user comes to the API docs, it's very hard to orient yourself. Particular issues:
-
Extensions aren't presented as Extensions, and get lumped with all top level modules in FSharpPlus

-
Entry points are hard to find: Lens
This could be alleviated by giving a namespace to "Extensions", or somehow otherwise separate docs.
- Control takes up a lot of space, yet from an end user perspective, are not important. They should go to Operators.fs to use a generic function which will delegate into control.
I'd say we
... or am I wrong and it's useful for users to directly use Control?
I think for documentation purposes you want to expose the library as each part. Actually most parts already present well, except Extensions/Control/Operators.fs -- these muddy things from a API doc perspective.
Yes, presenting the namespaces like that is not useful at all.
Regarding Control, is rarely used unless you want to do some advances/exotic stuff like passing an Invoker as a function Category for instance to overcome rank-n types limitations.
I am happy to discuss re-organizing namespaces.
One thing I noticed is that is not possible to use our extensions without opening FSharpPlus which in turn will bring all generic stuff in scope.
This is something we definitely need to address. My wild guess is that many users are comfortable using extensions but don't want to use generic code for good reasons and the last thing they want is to have all generic functions in scope.
I think this is a must for V2, but we should find a way to address this, if possible before V2.
I was thinking in moving all extensions to its own namespace so they can just do something like open FSharpPlus.Extensions but it might cause a breaking change by old code referencing functions in the FSharpPlus namespace.
@wallymathieu do you think having a process of duplicating the extensions in the other namespace would make sense?
Perhaps. Such a way could be to create a separate library that is the subset (this would be a variant of #328). Using for instance ifdefs in order to have the extensions in different namespaces while keeping the code the same.
Splitting Extensions in its own dll is definitely another option, we can discuss in #328 it will clearly have some benefits but also drawbacks.
Let's assume here in this thread that we don't split the library. What can we do?
I think, we can put them in an Extensions namespace and autoopen it, in such a way that:
- If you want only Extensions you do
open FSharpPlus.Extensions - If you want generic functions, you do
open FSharpPlusand that will put the extensions in scope as well, which makes sense.
Yes, that sounds like nice user experience. Then you wouldn't need to install a separate lib to get the things that makes sense from a beginner perspective.
I like that approach. It would be a v2 due to binary changes, though?
Practically, how do you achieve open FSharpPlus and put the extensions in scope as well? Will that be the case if we have namespace FSharpPlus with [<AutoOpen.. on an Extension module?
I wonder if we could use the same approach for other features? Say, I only want Lens functions?
Also, do you plan on using a v2 branch ...? I would like to start on some of these ideas!
Actually, thinking it through some more, the problem would be that we'd have to put all Extensions in one file. As far as I know, you can't split a module into separate files.
You can with a type, and as of F#5 (and 4.7 preview feature) we can open a type, but you can't have nested modules (I think!).
It is probably cleaner to use FSharpPlus.Extensions as a namespace, but that would require an F#+ user opening no matter what, since you can't autoopen a namespace.
Personally, I think my preference is to make a separate lib. Seems like there are so many utility libs, that don't get wide acceptance and tend to attract fancy, but less commonly used features. An F#+ core could be a very tight, very slowly changing library that tracks F# core.
WDYT?
I'm a bit ambivalent about splitting the library.
It's true, some users would like to see the extensions in a separate library, @dsyme for instance told me he likes that part of the library and would love to see it as a separate thing.
Let's discuss that option in its corresponding issue https://github.com/fsprojects/FSharpPlus/issues/328#issuecomment-704095824 and assume in this discussion that the library will continue as a whole.
What was the suggestion from @dsyme regarding using the return type for type inference?
To avoid using SRTPs on the result type, unless it's the only way (ie: return, zero, maxValue).
We can try remove it in other functions, I'm sure it will be a breaking change, so we'll have to measure the impact and see if it worths.