Skip to content

A normative immutable array type #619

Open

Description

@dsyme

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

  1. Add a bespoke immutable array to FSharp.Core.

  2. 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 module
  • IArray looks like an interface
  • FlatList breaks with industry naming, it looks too odd.
  • XArray, ZArray are possible if we are desperate – e.g. just make a new F# convention that ZThing is an immutable version of Thing
  • Block wins the day

Related questions are

  1. Would want a bespoke functional update syntax “{ arr with 3 = expr }” or “{ arr with n = expr } or “arr.[n=expr]”.
  2. Would the type feel right from F# code – good ergonomics etc
  3. What library dependencies would the type induce
  4. 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

  1. gerardtoconnor commented on Nov 14, 2017

    @gerardtoconnor

    ReadArray/RArray ? Core is developing Span that includes ReadOnlySpan , 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.

  2. tldrlol commented on Nov 14, 2017

    @tldrlol

    How about stealing terminology from other languages and calling them vectors?

  3. ijsgaus commented on Nov 14, 2017

    @ijsgaus

    Maybe, FrozenArray

  4. forki commented on Nov 14, 2017

    @forki
    Member

    FlatList is new react native component. Please don't use that

  5. forki commented on Nov 14, 2017

    @forki
    Member

    @tldrlol are we really talking about vectors? We have persistentvector in FSharpx.Collections - it's a hash array mapped trie.

  6. gerardtoconnor commented on Nov 14, 2017

    @gerardtoconnor

    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, Span or something similarly simple. For perf it might be worth looking at how Span implements index access in .net core 2.0

  7. rmunn commented on Nov 15, 2017

    @rmunn

    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 FArray as shorthand for FrozenArray? Or ImmArray since IArray sounds too much like an interface. Or the most radical suggestion: just name it Array, and the C# class would be called MutableArray, just like how the C# List class is named ResizeArray in F#.

    In the latter case, where we define Array as the name of the immutable-by-default type, there are many possible sources of confusion, such as "What's the difference between the types int [] and int 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.

  8. realvictorprm commented on Nov 15, 2017

    @realvictorprm
    Member

    Just realized again, that I can modify arrays if I want to 😄

    I'm for the radical version of naming our standard Arrays just Array and naming the C# or .NET regular Array just MutableArray.

    But I like the ReadArray idea too 🙂

    PS: Please don't call it Vector. Keep the mathematical meaning...

  9. mexx commented on Nov 15, 2017

    @mexx

    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?

  10. rmunn commented on Nov 16, 2017

    @rmunn

    A list-like structure that's array-based already exists; it's called ResizeArray. The difference is indeed performance: the existing List, 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 the List type would change that and make a lot of O(N) code to be suddenly O(N2). So the suggestion of changing List to be array-backed turns out to be a bad idea, and thankfully it will never happen.

  11. smoothdeveloper commented on Nov 16, 2017

    @smoothdeveloper
    Contributor

    @mexx

    What would be the main difference to an immutable list which is already present?

    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 Array into MutableArray, 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 the MutableArray module so the code can start to explicitly use that, but we would still need to find a transition approach for existing code relying on Array.

    Are there other languages we can draw inspiration from as to how to handle the concept of immutable arrays?

  12. mexx commented on Nov 16, 2017

    @mexx

    @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 list and array data types are concrete data types and have their characteristics. However I see both to be an implementation detail of the same abstract data type collection, previously I used list for 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# list as this collection ADT and be able to indicate which implementation should be used, linked list or array.

  13. dsyme commented on Nov 16, 2017

    @dsyme
    CollaboratorAuthor

    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 on System.Collections.Immutable.dll, with

    type roarray<'T> = ReadOnlyArray<'T> = ImmutableArray<'T>
    module ROArray = ...
    

    Or

    type immarray<'T> = ImmutableArray<'T>
    module ImmArray = ...
    

    Or

    type iarray<'T> = ImmutableArray<'T>
    module IArray = ...
    

    The last one has the drawback that IArray may be thought to be an interface, but has the distinct advantage of being short and visually non-instrusive

  14. dsyme commented on Nov 16, 2017

    @dsyme
    CollaboratorAuthor

    I'm labeling this as approved-in-principle - we should address this though the exact approach is far from decided

  15. 243 remaining items

  16. lfr commented on Jun 10, 2022

    @lfr

    Even though I no longer have a horse in this race after having replaced block with box in my own library, I still thought bloc was 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 block didn't get hijacked for this purpose, I think it may be for the best, though ImmutableArray is quite the mouthful and typeful 🤔

  17. dsyme commented on Jun 13, 2022

    @dsyme
    CollaboratorAuthor

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

  18. reinux commented on Jun 13, 2022

    @reinux

    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 😁

  19. dsyme commented on Jun 13, 2022

    @dsyme
    CollaboratorAuthor

    @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:

    ImmArray.map
    ImmList.map
    

    Note modules are rarely needed for dictionaries and more exotic collections, as most interesting operations are available immediately via dot-notation.)

    Types:

    immarray<int>
    immlist<int>
    

    Pattern matching

        match x with 
        | ImmArray [ a; b ] -> ...
        | ImmArray [ a; b; c ] -> ...
    

    To be performant the ImmArray active 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

    immarray.Create(1,2,3)
    immarray.CreateRange [1;2;3]
    

    and so on. Adding an immarray function taking an IEnumerable seems reasonable.

    immarray [ 1;2;3 ]
    

    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

  20. lfr commented on Jun 14, 2022

    @lfr

    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.

  21. NinoFloris commented on Jun 14, 2022

    @NinoFloris

    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!

  22. uxsoft commented on Jun 22, 2022

    @uxsoft

    I think the block was 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?

      • What are the major differences between the two?
      • Can we pick a default and the other be supported as any other C# code without specific FSharp.Core alias?
    • System.Collections.Immutable supports a number of immutable collections. Should more/all of them be supported in FSharp.Core?

      • How does the performance of the F# types compare to the System.Collections.Immutable counterparts?
      • In the ideal world would F# use these collections as a basis for the implementation of List/Set/Map?
        • If the answer is yes, do we want to take steps to get slowly closer to that world without breaking changes?
      • ImmutableArray
      • ImmutableList (counterpart to F# List)
      • ImmutableDictionary (counterpart to F# Map)
      • ImmutableHashSet (counterpart to F# Set)
      • ImmutableQueue
      • ImmutableSortedDictionary
      • ImmutableSortedSet
      • ImmutableStack
    • Is the intention for F# collection types to be immutable by default and mutable explicitly?

      • This is what already happens with variables and let vs let mutable
      • Kotlin does this as well and the names of the collections it uses are List, MutableList, Set, MutableSet, Map, MutableMap
        • This is very clean and understandable for beginners (Simple F# theme)
        • If we'd like to go this way, we should also introduce an alias for Array (MutableArray) 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?

      • Kotlin doesn't have any syntax for collection initializers, instead, it makes use of "params" helper methods in the global scope like listOf(_, _, ...), mutableListOf(_, _, ...)
        • This is very handy because new collections can be easily introduced by libraries and they "feel" native
        • It's Much easier to implement, and doesn't add any new language features
        • The use of brackets might not feel too "ML"
      • Swift uses a combination of the following (both suggestions already appeared above in this thread I believe):
        • Distinguish mutability by how the variable is declared (var vs let).
        • The type of the variable decides the type of the collection var deque: Deque<String> = ["Ted", "Rebecca"]
        • This would help also with the Array syntax ([| |]) which comes from OCAML
      • F# CE's
        • Similar to the Kotlin proposal above the, this would be the generic F# equivalent
        • Like seq { ... } we could introduce block { ... } and mutableBlock { ... }, etc.
        • CE's are very expressive, also very easy to implement without the need for extra language features
        • Are there performance implications to using CE's?
  23. uxsoft commented on Jun 22, 2022

    @uxsoft

    My personal opinion of the ideas above:

    • Let's take a broader look at F# collections to make them clean and easy to understand
    • F# should be immutable first, explicit about mutability (even in collection types)
    • I'd follow Kotlin in naming
    • I'd use F# CE's for initialization
    • I don't think we need both ImmutableList and ImmutableArray right now
    • Block seems like a good bridge to an immutable array because making an array immutable would be a breaking change

    So all in all it could look like this:

    • List (exists)
    • MutableList (missing, alias for C# LinkedList?)
    • Block (this RFC)
    • MutableBlock (= Array)
    • Set (exists)
    • MutableSet (missing, alias for C# HashSet?)
    • Map (exists)
    • MutableMap (missing, alias for C# Dictionary?)

    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.

  24. Happypig375 commented on Jun 23, 2022

    @Happypig375
    Contributor

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

  25. charlesroddie commented on Jun 25, 2022

    @charlesroddie

    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.

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

    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.

  26. kimsey0 commented on Dec 22, 2023

    @kimsey0

    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 for ImmutableQueue<'T>. I assume that should be a separate issue?

  27. xperiandri commented on Oct 30, 2026

    @xperiandri

    @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.Core as is.

  28. xperiandri commented on Oct 30, 2026

    @xperiandri

    Also, any thoughts on aliases vs native names would be appreciated.
    In my implementation, I inherited from the original repo creator:

    • FlatList → ImmutableArray
    • IndexedList → ImmutableList
    • HashMap → ImmutableDictionary
    • HashSet → ImmutableHashSet

    etc.

  29. kimsey0 commented on Oct 30, 2026

    @kimsey0

    Thanks for the ping, but I’m not the right reviewer. I only used ImmutableQueue once 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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions