Skip to content

Conversation

@uxsoft
Copy link
Contributor

@uxsoft uxsoft commented Jul 25, 2021

Suggestion approved in principle: fsharp/fslang-suggestions#1047

RFC FS-1113 https://github.com/fsharp/fslang-design/blob/main/preview/FS-1113-insert-remove-update-functions.md

  • Map.Keys, Map.Values, Map.keys implementation
  • Array removeAt, removeManyAt, updateAt, insertAt, insertManyAt implementation
  • List removeAt, removeManyAt, updateAt, insertAt, insertManyAt implementation
  • Seq removeAt, removeManyAt, updateAt, insertAt, insertManyAt implementation
  • Map tests
  • Array tests
  • List tests
  • Seq tests
  • Map benchmark
  • Array benchmark
  • List benchmark
  • Seq benchmark
  • Map xml docs
  • Array xml docs
  • List xml docs
  • Seq xml docs

@dnfadmin
Copy link

dnfadmin commented Jul 25, 2021

CLA assistant check
All CLA requirements met.

@uxsoft
Copy link
Contributor Author

uxsoft commented Jul 30, 2021

@dsyme I found out that the Map.Values implementation collides with internal Map.Values extension in illib.fs - MapAutoOpens module which returns a list instead of seq. This is the reason the solution right now doesn't build.

Since it's not part of the spec you provided I'll remove it from this PR. That said I see it's something that IS used, so let me know if you'd like me to take a look at it in a separate PR.

@uxsoft uxsoft requested a review from dsyme August 3, 2021 12:17
Copy link
Contributor

@dsyme dsyme left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is looking good.

Based on the suggestion discussion and looknig at the implementation I think we should at least drop updateManyAt across the board. It is just too corner-case.

I'll discuss on the suggestion further

… illib.fs and NameResolution.fs to be compatible, update surface area
@uxsoft uxsoft changed the title WIP additions to immutable collections Additions to immutable collections Aug 11, 2021
@uxsoft uxsoft requested a review from dsyme August 11, 2021 22:09
Copy link
Contributor

@dsyme dsyme left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is looking great, just a few comments on the benchmarking

@uxsoft uxsoft requested a review from dsyme August 12, 2021 08:17
dsyme added a commit to fsharp/fslang-design that referenced this pull request Aug 12, 2021
# F# RFC FS-1113 - Add insert/remove/update functions for collections, also Keys/Values for Map

The design suggestion [Additions to collections for insert/update/ remove](fsharp/fslang-suggestions#1047) has been marked "approved in principle".

This RFC covers the detailed proposal for this suggestion.

- [x] [Suggestion](fsharp/fslang-suggestions#1047)
- [x] Approved in principle
- [x] [Implementation](dotnet/fsharp#11888)
- [Discussion](https://github.com/fsharp/fslang-design/discussions/FILL-ME-IN)

# Summary

1. Add insert/remove/update functions to List, Array, Seq, Map to improve them as workhorse functional collections
2. Add `Keys` and `Values` properties to Map

# Motivation

All the above are commonly useful.

# Detailed design

We add the following:

```fsharp
module List =
    /// Return a new list with the item at a given index removed
    /// If the index is outside the range of the list then it is ignored.
    val removeAt: index: int -> source: 'T list -> 'T list

    /// Return a new list with the number of items starting at a given index removed.
    /// If index is outside 0..source.Length-1 then it is ignored.
    val removeManyAt: index: int -> count: int -> source: 'T list -> 'T list

    /// Return a new list with the item at a given index set to the new value. If 
    /// index is outside 0..source.Length-1 an exception is raised.
    val updateAt: index: int -> value: 'T -> source: 'T list -> 'T list

    /// Return a new list with a new item inserted before the given index.The index may be source.Length to
    /// include extra elements at the end of the list. If 
    /// index is outside 0..source.Length an exception is raised.
    val insertAt: index: int -> value: 'T -> source: 'T list -> 'T list

    /// Return a new list with new items inserted before the given index. The index may be source.Length to
    /// include extra elements at the end of the list. 
    /// If index is outside 0..source.Length  an exception is raised.
    val insertManyAt: index: int -> values: seq<'T> -> source: 'T list -> 'T list

module Array =
    /// Return a new array with the item at a given index removed
    /// If the index is outside the range of the array then it is ignored.
    val removeAt: index: int -> source: 'T[] -> 'T[]

    /// Return a new array with the number of items starting at a given index removed.
    /// If an implied item index is outside the range of the array then it is ignored.
    val removeManyAt: index: int -> count: int -> source: 'T[] -> 'T[]

    /// Return a new array with the item at a given index set to the new value. If 
    /// index is outside 0..source.Length-1 an exception is raised.
    val updateAt: index: int -> value: 'T -> source: 'T[] -> 'T[]

    /// Return a new array with a new item inserted before the given index. The index may be source.Length to
    /// include extra elements at the end of the array. If 
    /// index is outside 0..source.Length than source.Length an exception is raised.
    val insertAt: index: int -> value: 'T -> source: 'T[] -> 'T[]

    /// Return a new array with new items inserted before the given index. The index may be source.Length to
    /// include extra elements at the end of the array.   If index is outside 0..source.Length an exception is raised.
    val insertManyAt: index: int -> values: seq<'T> -> source: 'T[] -> 'T[]

module Seq =
    /// Return a new ssequence which, when iterated, will have the item at a given index removed
    /// If the index is outside the range of valid indexes for the sequence then it is ignored.
    val removeAt: index: int -> source: seq<'T> -> seq<'T>

    /// Return a new sequence which, when iterated, will have the given count of items starting at a given index removed.
    /// If any implied item index is outside the range of valid indexes for the sequence then it is ignored.
    val removeManyAt: index: int -> count: int -> source: seq<'T> -> seq<'T>

    /// Return a new sequence which, when iterated, will return the given item for the given index, and
    /// otherwise return the items from source. If index is below zero an exception is raised immediately.
    /// If the index is beyond the range on the source sequence the update is ignored.
    val updateAt: index: int -> value: 'T -> source: seq<'T> -> seq<'T>

    /// Return a new sequence which, when iterated, includes a new item inserted before the given index. The index may be source.Length to
    /// include extra elements at the end of the sequence. If 
    /// index is outside 0..source.Length than source.Length an exception is raised.
    val insertAt: index: int -> value: 'T -> source: seq<'T> -> seq<'T>

    /// Return a new sequence which, when iterated, will include additional items given by values, starting
    /// at the given index. The index may be source.Length to
    /// include extra elements at the end of the sequence. 
    /// If index is outside 0..source.Length an exception is raised.
    val insertManyAt: index: int -> values: seq<'T> -> source: seq<'T> -> seq<'T>
```

# Drawbacks

Adds extra functions to FSharp.Core

# Alternatives

See suggestion discussion  thread for discusssion on `updateManyAt` and also other naming alternatives.

# Compatibility

Not a breaking change

# Unresolved questions

None
@dsyme dsyme changed the title Additions to immutable collections [RFC FS-1113] Additions to immutable collections Aug 12, 2021
@dsyme
Copy link
Contributor

dsyme commented Aug 12, 2021

@dsyme dsyme changed the title [RFC FS-1113] Additions to immutable collections [RFC FS-1113] Additions to collection functions Aug 12, 2021
@dsyme
Copy link
Contributor

dsyme commented Aug 12, 2021

@uxsoft I updated to

  1. Add Map.values
  2. Remove Map.Empty (in favour of existing Map.empty)

@dsyme dsyme merged commit a717e53 into dotnet:main Aug 12, 2021
@dsyme
Copy link
Contributor

dsyme commented Aug 12, 2021

@uxsoft Thank you for this contribution!

@uxsoft
Copy link
Contributor Author

uxsoft commented Aug 13, 2021

Thank you for your guidance!

@OkkeHendriks
Copy link

OkkeHendriks commented Aug 13, 2021

Nice work, now I can get rid of some of my 'extensions' in the near future :)

I am not sure if this is the right place to ask this, as this is already merged.
However, I see that at the toSeq function the summary states that The sequence will be ordered by the keys of the map.. This is also the case for Map.keys and Map.values, so I think it is an interesting property to note within the summary of those.

Also the tests should verify this if this is added.

@dsyme
Copy link
Contributor

dsyme commented Aug 14, 2021

This is also the case for Map.keys and Map.values, so I think it is an interesting property to note within the summary of those.

Yes, I agree. It's also interesting to think whether things like "get me the middle key in the ordering of keys" will run efficiently or not

let midKey =
    let keys = map.Keys
    keys.[keys.Length/2]

I haven't checked. Probably not

@uxsoft uxsoft deleted the feature/additions-to-immutable-collections branch August 14, 2021 07:30
@uxsoft
Copy link
Contributor Author

uxsoft commented Aug 14, 2021

I can open a new PR with these small changes if you'd like

@OkkeHendriks
Copy link

It's also interesting to think whether things like "get me the middle key in the ordering of keys" will run efficiently or not.

Correct me if I'm wrong but I think that is not even possible, directly, as ICollection does not implement Item:
image

So to get the middle one would have to MoveNext() through the sequence.

@dsyme
Copy link
Contributor

dsyme commented Aug 14, 2021

Correct me if I'm wrong but I think that is not even possible, directly, as ICollection does not implement Item:

Yes you're correct, thanks

@dsyme
Copy link
Contributor

dsyme commented Aug 14, 2021

I can open a new PR with these small changes if you'd like

Yes, please make the additions to the doc comments

Comment on lines -4323 to +4328
|> List.filter (function
|> Seq.filter (function
| Item.UnqualifiedType _ -> false
| Item.Value v -> not v.IsMember
| _ -> true)
|> List.filter (ItemIsUnseen ad g ncenv.amap m >> not)
|> Seq.filter (ItemIsUnseen ad g ncenv.amap m >> not)
|> Seq.toList
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why this change was needed?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I doubt it was necessary

Copy link
Contributor Author

@uxsoft uxsoft Sep 13, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Notice the implementation of values in illib.fs returns a list and the public one an ICollection. The old code expecting a List would not compile if I didn't change it to seq.

If there was a better solution I missed, I'd be happy to learn.

static member Empty : Map<'Key, 'Value> = Map.empty

member x.Values = [ for KeyValue(_, v) in x -> v ]
Copy link
Member

@auduchinok auduchinok Sep 13, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@dsyme Do we expect all FCS clients to use FSharp.Core previews now? I don't think it's a good idea to tie FCS and FSharp.Core previews like this.

The tooling now shows errors (#12142) and it seems we won't be able to run FCS with a stable FSharp.Core version anymore if FCS is updated?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@auduchinok Hmmmm could you please open a separate issue about this. I agree public non-pre-release FCS should indeed only use a public non-pre-release FCS.

However if FCS in pre-release and the release plan is matching FSharp.Core than it is ok

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

9 participants