Repository navigation
A normative immutable array type #619
Description
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.
ImmutableArrayis too long, especially for a moduleIArraylooks like an interfaceFlatListbreaks with industry naming, it looks too odd.XArray,ZArrayare possible if we are desperate – e.g. just make a new F# convention thatZThingis an immutable version ofThingBlockwins the day
Related questions are
- Would want a bespoke functional update syntax “{ arr with 3 = expr }” or “{ arr with n = expr } or “arr.[n=expr]”.
- Would the type feel right from F# code – good ergonomics etc
- What library dependencies would the type induce
- How do other collections from System.Collections.Immutable feel to use from F#?
Extra information
Estimated cost (XS, S, M, L, XL, XXL): M
Affidavit (please submit!)
Please tick this by placing a cross in the box:
- This is not a question (e.g. like one you might ask on stackoverflow) and I have searched stackoverflow for discussions of this issue
- I have searched both open and closed suggestions on this site and believe this is not a duplicate
- This is not something which has obviously "already been decided" in previous versions of F#. If you're questioning a fundamental design decision that has obviously already been taken (e.g. "Make F# untyped") then please don't submit it.
Please tick all that apply:
- This is not a breaking change to the F# language design
- I or my company would be willing to help implement and/or test this
Activity
gerardtoconnor commentedon Nov 14, 2017
ReadArray/RArray? Core is developingSpanthat includesReadOnlySpan, might this be a good representation to isolate underlying array?If we could also implement an Array cursor that holds position small struct, we could pattern match like list to allow
::decomposition and reduce need to convert arrays to lists given lists usually twice size and array already allocated.tldrlol commentedon Nov 14, 2017
How about stealing terminology from other languages and calling them vectors?
ijsgaus commentedon Nov 14, 2017
Maybe, FrozenArray
forki commentedon Nov 14, 2017
FlatList is new react native component. Please don't use that
forki commentedon Nov 14, 2017
@tldrlol are we really talking about vectors? We have persistentvector in FSharpx.Collections - it's a hash array mapped trie.
gerardtoconnor commentedon Nov 14, 2017
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
Range,Spanor something similarly simple. For perf it might be worth looking at how Span implements index access in .net core 2.0rmunn commentedon Nov 15, 2017
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
FArrayas shorthand forFrozenArray? OrImmArraysinceIArraysounds too much like an interface. Or the most radical suggestion: just name itArray, and the C# class would be calledMutableArray, just like how the C#Listclass is namedResizeArrayin F#.In the latter case, where we define
Arrayas the name of the immutable-by-default type, there are many possible sources of confusion, such as "What's the difference between the typesint []andint array?" (The former would be a mutable array, and the latter would be an immutable array). But that name would make a statement that F#'s types (seq, list, and array) are all immutable by default — which might not be a bad thing.realvictorprm commentedon Nov 15, 2017
Just realized again, that I can modify arrays if I want to 😄
I'm for the radical version of naming our standard Arrays just
Arrayand naming the C# or .NET regular Array justMutableArray.But I like the
ReadArrayidea too 🙂PS: Please don't call it Vector. Keep the mathematical meaning...
mexx commentedon Nov 15, 2017
What would be the main difference to an immutable list which is already present?
Is it only performance? Maybe we could just annotate the list instance to be array-backed instead of linked list-backed?
rmunn commentedon Nov 16, 2017
A list-like structure that's array-based already exists; it's called
ResizeArray. The difference is indeed performance: the existingList, based on a linked list, is extremely efficient for operations at the head of the list (inserting or removing an item at the head is O(1)). Lots of F# code depends on that property, and changing to an array-backed data structure for theListtype would change that and make a lot of O(N) code to be suddenly O(N2). So the suggestion of changingListto be array-backed turns out to be a bad idea, and thankfully it will never happen.smoothdeveloper commentedon Nov 16, 2017
@mexx
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
ArrayintoMutableArray, but how would you actually make that happen without breaking all the code? it sounds like we would need first to roll out a FSharp.Core with theMutableArraymodule so the code can start to explicitly use that, but we would still need to find a transition approach for existing code relying onArray.Are there other languages we can draw inspiration from as to how to handle the concept of immutable arrays?
mexx commentedon Nov 16, 2017
@rmunn and @smoothdeveloper you might have gotten my suggestion wrong.
It's not about changing the list to always be array-backed.
There are to levels of discussion, and I haven't made clear on which one I make my suggestion.
Concrete vs. abstract data type
The
listandarraydata types are concrete data types and have their characteristics. However I see both to be an implementation detail of the same abstract data typecollection, previously I usedlistfor it, that was misleading. This ADT actually defines the possible operations, like getting an item by index, getting the count of the items (length, size).So my suggestion was to define and see the current F#
listas thiscollectionADT and be able to indicate which implementation should be used, linked list or array.dsyme commentedon Nov 16, 2017
One approach would be to add a new
FSharp.Core.Extras.dll(exact naming TBD) to the set of default references in F# projects, with a dependency onSystem.Collections.Immutable.dll, withOr
Or
The last one has the drawback that
IArraymay be thought to be an interface, but has the distinct advantage of being short and visually non-instrusivedsyme commentedon Nov 16, 2017
I'm labeling this as approved-in-principle - we should address this though the exact approach is far from decided
243 remaining items
lfr commentedon Jun 10, 2022
Even though I no longer have a horse in this race after having replaced
blockwithboxin my own library, I still thoughtblocwas a great choice both semantically (a group of parties who work in unison) as well as aesthetically (it's super short).In any case, I'm still happy an ubiquitous word such as
blockdidn't get hijacked for this purpose, I think it may be for the best, though ImmutableArray is quite the mouthful and typeful 🤔dsyme commentedon Jun 13, 2022
@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.
reinux commentedon Jun 13, 2022
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 😁
dsyme commentedon Jun 13, 2022
@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
ImmArrayactive pattern would need to be a new kind of thing, e.g. if we allowed the use of list pattern syntax against indexable data-structures.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.
Construction
In the simplest addition, creation would just use the existing API, which would give
and so on. Adding an
immarrayfunction taking an IEnumerable seems reasonable.We would also support
immarray { for x in 1 .. 1000 do yield (x+1); .... }etc. without creating an intermediate sequence and only an intermediate builder, as for list and array construction.See also #625
lfr commentedon Jun 14, 2022
Knowing that there's going to be a number of these things, I think
ImmArray,ImmList, etc are definitely the better option. In isolation the "Imm" prefix seemed a bit jarring but as part of a collective, it makes sense.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.
NinoFloris commentedon Jun 14, 2022
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!
uxsoft commentedon Jun 22, 2022
I think the
blockwas good if the goal was to patch the gap for an immutable array.If the goal is broader support for immutable collections here are some interesting observations as food for thought:
Is support for ImmutableList needed in the FSharp.Core?
System.Collections.Immutablesupports a number of immutable collections. Should more/all of them be supported inFSharp.Core?List/Set/Map?ImmutableArrayImmutableList(counterpart to F# List)ImmutableDictionary(counterpart to F# Map)ImmutableHashSet(counterpart to F# Set)ImmutableQueueImmutableSortedDictionaryImmutableSortedSetImmutableStackIs the intention for F# collection types to be immutable by default and mutable explicitly?
letvslet mutableList,MutableList,Set,MutableSet,Map,MutableMapMutableArray) and deprecate it so that we could slowly migrate usage from Array and repurpose the Array for an immutable collection in the future.How will all of these collections be initialized? Is new syntax needed?
listOf(_, _, ...),mutableListOf(_, _, ...)varvslet).var deque: Deque<String> = ["Ted", "Rebecca"][| |]) which comes from OCAMLseq { ... }we could introduceblock { ... }andmutableBlock { ... }, etc.uxsoft commentedon Jun 22, 2022
My personal opinion of the ideas above:
Blockseems like a good bridge to an immutable array because making an array immutable would be a breaking changeSo 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.
Happypig375 commentedon Jun 23, 2022
@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.
charlesroddie commentedon Jun 25, 2022
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
[: :]. I started work on this in dotnet/fsharp#12859 . I can fix the conflicts.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.
kimsey0 commentedon Dec 22, 2023
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
ImmutableArray<'T>, it would be lovely to make a similar module forImmutableQueue<'T>. I assume that should be a separate issue?xperiandri commentedon Oct 30, 2026
@kimsey0 could you have a look at the work done in https://github.com/fsprojects/FSharp.Collections.Immutable/pulls?q=sort%3Aupdated-desc+is%3Apr+state%3Aopen+feat+OR+perf ? Review would be appreciated.
Technically, all of that can be migrated to
FSharp.Coreas is.xperiandri commentedon Oct 30, 2026
Also, any thoughts on aliases vs native names would be appreciated.
In my implementation, I inherited from the original repo creator:
etc.
kimsey0 commentedon Oct 30, 2026
Thanks for the ping, but I’m not the right reviewer. I only used
ImmutableQueueonce for an Advent of Code puzzle and don’t work in F# otherwise, so I can’t give this the review it needs. Good luck with the pull requests.