fsharp / fslang-suggestions

The place to make suggestions, discuss and vote on F# language and core library features
345 stars 21 forks source link

A normative immutable array type #619

Open dsyme opened 7 years ago

dsyme commented 7 years ago

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.

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:

Please tick all that apply:

lfr commented 2 years ago

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 🤔

dsyme commented 2 years ago

@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 commented 2 years ago

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 commented 2 years ago

@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 https://github.com/fsharp/fslang-suggestions/issues/625

lfr commented 2 years ago

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 commented 2 years ago

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 commented 2 years ago

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:

uxsoft commented 2 years ago

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.

Happypig375 commented 2 years ago

@uxsoft The problem is seen here: https://github.com/fsharp/fslang-suggestions/issues/619#issuecomment-1154210776

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 commented 2 years ago

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 https://github.com/dotnet/fsharp/pull/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 commented 11 months ago

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?