-
Notifications
You must be signed in to change notification settings - Fork 21
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
A normative immutable array type #619
Comments
If we could also implement an Array cursor that holds position small struct, we could pattern match like list to allow |
How about stealing terminology from other languages and calling them vectors? |
Maybe, FrozenArray |
FlatList is new react native component. Please don't use that |
@tldrlol are we really talking about vectors? We have persistentvector in FSharpx.Collections - it's a hash array mapped trie. |
Agree on flatmap not being great. I have never liked the blanket naming approach on vectors outside of linear algebra, like in c++. I do like the idea though of like vector using a simple name like |
I'd rather not use the name "vector" for this, since that's already got a meaning (PersistentVector from FSharpx.Collections, inspired by Clojure) that is NOT an array. If F#'s immutable arrays got called vectors, I'm afraid it would cause user confusion between that and the very useful persistent vector structure. (I'd also like to see PersistentVector and PersistentHashMap promoted to some kind of "official" status in the FSharp.Core library, but that's a different suggestion). I'll toss out another name suggestion or two to see how it sounds. How about In the latter case, where we define |
Just realized again, that I can modify arrays if I want to 😄 I'm for the radical version of naming our standard Arrays just But I like the PS: Please don't call it Vector. Keep the mathematical meaning... |
What would be the main difference to an immutable list which is already present? |
A list-like structure that's array-based already exists; it's called |
The immutable list doesn't expose the same operations as arrays, like getting an item by index, or getting the length, or rather their performance characteristics make them bad practice to use. @realvictorprm I like the idea to rename current Are there other languages we can draw inspiration from as to how to handle the concept of immutable arrays? |
@rmunn and @smoothdeveloper you might have gotten my suggestion wrong. There are to levels of discussion, and I haven't made clear on which one I make my suggestion. Concrete vs. abstract data typeThe So my suggestion was to define and see the current F# |
One approach would be to add a new
Or
Or
The last one has the drawback that |
I'm labeling this as approved-in-principle - we should address this though the exact approach is far from decided |
Do we want a new syntax for creating such an array? What about pattern matching on it? |
@Tarmil It's a very good question. Pattern matchingEffective, performant pattern matching would likely need an extension to active patterns. Best syntax would be something like
etc. but to be performant the Without such an extension I don't think we would make any specific pattern matching available and would just ask the user to write their own active patterns. ConstructionIn the simplest addition, creation would just use the existing API, which would give
and so on. Adding an
It's going to be hard to justify adding more beyond these, assuming we have the regular functions in In some ideal world, we would also support syntax
|
This is why I was thinking, additionally, we may want to include an ArrayCursor type that is a simple struct of standard array ref & index such that cons operator could be used on ArrayCursor to decompose to item at index, and new cursor with index incremented ... allowing for rec array traversal like list but without having to rebuild the array as a list. The ArrayCursor could also have item/index methods on it (although if struct, we risk boxing so best if we could index with static operator or similar) so we could do index jump & read to remain consistant and avoid boxing. |
Might need to be separated into different issue (as don't want to distract from type ArrayCursor<'T> =
struct
val private iary : 'T []
val position : int
end
static member empty (ac:ArrayCursor<'T>) = ac.position = -1
static member itemNext (ac:ArrayCursor<'T>) =
if ac.position + 1 < ac.iary.Length then
ac.iary.[ac.position] , ArrayCursor<'T>(ac.iary,ac.position + 1)
else
ac.iary.[ac.position] , ArrayCursor<'T>(ac.iary,-1)
static member next (ac:ArrayCursor<'T>) = ArrayCursor<'T>(ac.iary,if ac.position + 1 > ac.iary.Length then -1 else ac.position + 1)
static member prev (ac:ArrayCursor<'T>) = ArrayCursor<'T>(ac.iary,if ac.position - 1 < 0 then -1 else ac.position - 1)
static member jump (ac:ArrayCursor<'T>) (pos:int) = ArrayCursor<'T>(ac.iary,if pos < ac.iary.Length && pos > 0 then pos else -1 )
static member item (ac:ArrayCursor<'T>) = ac.iary.[ac.position]
static member jumpItem (ac:ArrayCursor<'T>) (pos:int) = if pos < ac.iary.Length && pos > 0 then ac.iary.[pos] else failwithf "Out of index exception"
new (ary: 'T [],pos) = { iary = ary ; position = pos } // include pos bounds check on constructor
new (ary: 'T []) = { iary = ary ; position = 0 }
let inline (!+) (ac:ArrayCursor<'T>) = ArrayCursor<'T>.itemNext ac
let inline (~+) (ac:ArrayCursor<'T>) = ArrayCursor<'T>.next ac
let inline (~-) (ac:ArrayCursor<'T>) = ArrayCursor<'T>.prev ac
let inline (!) (ac:ArrayCursor<'T>) = ArrayCursor<'T>.item ac
let inline (@) (ac:ArrayCursor<'T>) (pos:int) = ArrayCursor<'T>.jump ac pos
let inline (!) (ac:ArrayCursor<'T>) (pos:int) = ArrayCursor<'T>.jumpItem ac pos
module Array =
let toArrayCursor x = ArrayCursor<_>(x,0)
let acur = ArrayCursor<'T>([|1;2;3|])
let h,tail = !+acur // value , ArrayCursor (next)
let nextCur = +acur // ArrayCursor (next)
let prevCur = -acur // ArrayCursor (prev)
let value = !acur // value (at cursor)
let jumpCur = acur @ 4 // ArrayCursor (at index)
let jumpVal = acur ! 5 // value (at index) |
I think one major reason people currently use Notably, both Fable-React and Websharper go with lists. In their HTML DSLs, every element is followed by two collections (attributes and inner HTML) and even using a basic array would start to look 'noisy' really fast ( Typing I suspect that a collection type without its own (lightweight!) literal syntax would end up being used only when performance concerns begin to surface, and it would be largely absent from day-to-day usage. |
Why not finally to deprecate F# specific collections and use
|
Are the collections in |
@piaste Well, in WebSharper these functions take |
I think both may suffer somewhat because of this issue which can make a major difference to these collection implementations |
Thanks for making this case - it is a compelling argument and UI/HTML/JSON/DOM literals are a very fundamental use-case for immutable collections. Are there any approaches to literal syntaxes would be acceptable? e.g.
Again an interesting observation, thanks |
I like this idea a lot. I'd be very happy if |
🤘 I agree, type inference for collection literals sounds fantastic and, I suspect, a more efficient use of time than periodically introducing better data structures. Handling ambiguous cases should require a lot of care though. Some food for thought: how should the following cases be resolved?
a) Possible approaches:
|
How would you feel about |
|
I would like to point out that |
Range is already a .net type. https://docs.microsoft.com/en-us/dotnet/api/system.range?view=net-5.0 |
I'd be quite happy with |
I also like that just by having one less letter, // block1:'T block -> block2:'T block -> 'T block 😵
Block.append And the version with // bloc1:'T bloc -> bloc2:'T bloc -> 'T bloc
Bloc.append Signature characters are prime real estate 🏛 |
Hi all, It's time to close out this long discussion. We will proceed to the RFC using Thanks |
Discussion can continue here: fsharp/fslang-design#528 |
Just to say we started using the |
😭😭😭 |
@dsyme would it be possible to get some info on why |
Even though I no longer have a horse in this race after having replaced In any case, I'm still happy an ubiquitous word such as |
@SamuelBerger @matthewcrews Certainly "Block" is a fine name and the best suggestion we have. However in the compiler code, I guess I just came to realise that we will ultimately need multiple immutable collections (ImmutableList, ImmutableArray, ImmutableDictionary in particular), and it's just better and clearer and simpler if these all use some common prefix and naming scheme, and thus that we need to connect more solidly to the existing naming schemes. Diverging one of these for "block" just didn't help. |
Not sure we need even more name ideas at this point, but how about "roar" or "roarr", as in "read only array"? It's fun to say 😁 |
@reinux It's a long, long thread. I think given my comment above I guess I'd suggest back to this suggestion below. BTW this whole topic makes the more common use of Fluent notation (as in #1073) more necessary/useful - the more collections you have, the more likely you crave to reach for fluent notation. Module-qualified functions:
Note modules are rarely needed for dictionaries and more exotic collections, as most interesting operations are available immediately via dot-notation.) Types:
Pattern matching
To be performant the Without such an extension I don't think we would make any specific pattern matching available and would just ask the user to write their own active patterns. ConstructionIn the simplest addition, creation would just use the existing API, which would give
and so on. Adding an
We would also support See also #625 |
Knowing that there's going to be a number of these things, I think In fact I'd bet that if you go with "Immutable", it's only a matter of time before you implement "Imm" as an alternative. |
I've never found ImmArray annoying as a name or frustrating to type since we started using it in 2020. So +1 for these new names! |
I think the If the goal is broader support for immutable collections here are some interesting observations as food for thought:
|
My personal opinion of the ideas above:
So all in all it could look like this:
On an unrelated note: I'd be willing to work on this / submit a pull request when there is a decision on how this should look. |
@uxsoft The problem is seen here: #619 (comment) The F# compiler makes use of ImmutableList, ImmutableArray, and ImmutableDictionary despite the presence of F# Lists and Maps. This is because F# collections and System.Collections.Immutable are fundamentally different - ImmutableArrays for small amounts of data, ImmutableLists for large amounts of data or frequent updates, ImmutableDictionary is unsorted unlike Map which provides performance benefits... Meanwhile, F# collections currently provide no support for comparers, and keys must be able to provide comparison (not just equality). Unifying these two is a guaranteed breaking change - F# Map is sorted while System.Collections.Immutable.ImmutableDictionary is not. F# Map requires comparison constraint and that will not be going away. |
I would rather we don't discuss the name any more! An abbreviated name doesn't need to go into any initial work on this. It can be added later without any breaking changes.
I believe the main piece of work is to create these things using It's my first contribution to the compiler and I could use some help with it @uxsoft or anyone else! Feel free to message me on slack. I believe it's at the point where it ought to work but doesn't, so some help testing/debugging would be very appreciated. Could be over a screen share. Module functions are already implemented and just need to be exposed. |
Now that it was been decided in principle that FSharp.Core will depend on System.Collections.Immutable and will implement a module that makes it easy to interact with |
For people coming to this thread afresh, the RFC is here: https://github.com/fsharp/fslang-design/blob/main/RFCs/FS-1094-immarray.md
I propose we work out how to make one particular immutable array type normative in F# programming "in the box", including in Fable programming.
The existing way of approaching this problem in F# is to use a user-supplied package of collections such
System.Collections.Immutable
Description
One particular thing that is a hole in our library is the lack of an immutable array data structure in regular F# coding. There are lots of use cases for this and it is easy enough to implement efficiently, e.g. here (originally from the compiler codebase) though there are other approaches.
I’m particularly aware that Fable and the Elmish design pattern is popularizing the use of immutable data for important model descriptions more and more and we should be helping improve the situation for that kind of programming
The main question is to how to make on immutable array type normative in F# coding
Add a bespoke immutable array to FSharp.Core.
Encourage people to take a dependency on System.Collections.Immutable and add a reference to it to our standard templates. However we would still presumably want an FSharp.Core module making it look and feel like a normal F# collection, but we wouldn't want FSharp.Core to have a dependency on System.Collections.Immutable.
Probably the hardest thing is to decide its name.
ImmutableArray
is too long, especially for a moduleIArray
looks like an interfaceFlatList
breaks with industry naming, it looks too odd.XArray
,ZArray
are possible if we are desperate – e.g. just make a new F# convention thatZThing
is an immutable version ofThing
Block
wins the dayRelated questions are
Extra information
Estimated cost (XS, S, M, L, XL, XXL): M
Affidavit (please submit!)
Please tick this by placing a cross in the box:
Please tick all that apply:
The text was updated successfully, but these errors were encountered: