Skip to content
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

Roadmap V2.0 #288

Open
gusty opened this issue Mar 7, 2020 · 26 comments
Open

Roadmap V2.0 #288

gusty opened this issue Mar 7, 2020 · 26 comments
Labels

Comments

@gusty
Copy link
Member

gusty commented Mar 7, 2020

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:

  1. Remove subsumption from Applicatives: Right now seq "eats" all custom types implementing seq

  2. Same for Traversable, same situation: although this might not require a breaking change (todo find out)

  3. Implement IEnumerable for Vectors, now that (1) has to be done.

  4. New ListT monad transformer, possibly new SeqT as well, coded using a similar technique as the one used for Free Monads.

  5. 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?

  6. Add ValueTuple, ValueTask and other latest addition to the framework. We can require a higher framework version

  7. Consider suggestions by @dsyme regarding using the return type for type inference.

Am I missing something?

EDIT

  1. Better task support (maybe an intermediate type like Step with conversions to/from Task and its use in Bind an other methods for task).

  2. Consistent type parameters order, this would need to swap type parameters in Validation<'err,'t> to Validation<'t,'err>.

@adz
Copy link
Contributor

adz commented Mar 8, 2020

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?

@gusty
Copy link
Member Author

gusty commented Mar 8, 2020

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 ;)

@NinoFloris
Copy link
Contributor

#245

I would vote for defaulting to left for success right for error

@cmeeren
Copy link
Contributor

cmeeren commented Apr 24, 2020

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.)

@gusty
Copy link
Member Author

gusty commented Apr 25, 2020

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.

@cmeeren
Copy link
Contributor

cmeeren commented Apr 25, 2020

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.)

@gusty
Copy link
Member Author

gusty commented Apr 25, 2020

Yes, definitely. I think more than 90% of the cases are white-spaces.

@wallymathieu
Copy link
Member

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?

@wallymathieu
Copy link
Member

So perhaps a PR @cmeeren with the change?

@cmeeren
Copy link
Contributor

cmeeren commented May 7, 2020

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.

@gusty
Copy link
Member Author

gusty commented May 8, 2020

Let's discuss it in #322

@cmeeren
Copy link
Contributor

cmeeren commented May 19, 2020

Again regarding seq and subsumption: Does that mean that currently, length is an O(N) operation also for arrays?

@gusty
Copy link
Member Author

gusty commented May 19, 2020

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.

@cmeeren
Copy link
Contributor

cmeeren commented May 19, 2020

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)).

@gusty
Copy link
Member Author

gusty commented May 19, 2020

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.

@adz
Copy link
Contributor

adz commented Oct 1, 2020

When a user comes to the API docs, it's very hard to orient yourself. Particular issues:

  1. Extensions aren't presented as Extensions, and get lumped with all top level modules in FSharpPlus
    image

  2. Entry points are hard to find: Lens

This could be alleviated by giving a namespace to "Extensions", or somehow otherwise separate docs.

  1. 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 the entire namespace from docs...

... 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.

@gusty
Copy link
Member Author

gusty commented Oct 1, 2020

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.

@gusty
Copy link
Member Author

gusty commented Oct 4, 2020

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?

@wallymathieu
Copy link
Member

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.

@gusty
Copy link
Member Author

gusty commented Oct 4, 2020

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 FSharpPlus and that will put the extensions in scope as well, which makes sense.

@wallymathieu
Copy link
Member

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.

@adz
Copy link
Contributor

adz commented Oct 5, 2020

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!

@adz
Copy link
Contributor

adz commented Oct 6, 2020

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?

@gusty
Copy link
Member Author

gusty commented Oct 6, 2020

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 #328 (comment) and assume in this discussion that the library will continue as a whole.

@wallymathieu
Copy link
Member

What was the suggestion from @dsyme regarding using the return type for type inference?

@gusty
Copy link
Member Author

gusty commented Nov 7, 2020

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

5 participants