[RFC FS-1036 Discussion] Consistent slicing #217
Replies: 18 comments
-
thank you. i found there's no test for list in https://github.com/Microsoft/visualfsharp/blob/master/tests/fsharp/core/array/test.fsx the only tests for list type slicing are here:
|
Beta Was this translation helpful? Give feedback.
-
Thanks for finding the tests.
|
Beta Was this translation helpful? Give feedback.
-
Closing old discussion - the feature is implemented and shipped |
Beta Was this translation helpful? Give feedback.
-
Re-opening as per "discovery" (🙃 ) that this isn't completed |
Beta Was this translation helpful? Give feedback.
-
Continuing from fsharp/fslang-suggestions#358 (Paraphrasing, so please correct if this is not accurate) @charlesroddie makes the claim that I find that the notion that
In fact, the current implementation feels broken: > let lst = [1; 2];;
val lst : int list = [1; 2]
> lst.[0..(-1000)];;
val it : int list = [] It could be argued that anything below At any rate, while we're consistent with one definition, it seems to me that we're inconsistent with the idea that getting the value from the negative index of a collection is exceptional behavior. |
Beta Was this translation helpful? Give feedback.
-
Definition 1: The current definition of
|
Beta Was this translation helpful? Give feedback.
-
|
Beta Was this translation helpful? Give feedback.
-
See above comment on valid indices.
This is not true. The difference of 1 in the definition of the second number between inclusive-end and exclusive-end languages does not affect the utility of extracting empty slices. Or do you think 0..0 should fail in exclusive-end languages too? The utility of extracting an empty sequence is enormous (and similar to the utility of using trivial base cases in other contexts). Slicing would be completely broken without it. Here is code that should succeed but would fail if your version of slicing were used and require hacky workarounds. (Typical of choosing the wrong base case.) // Not slicing but the same idea
// prints "hello" n times, for 0<=n
for x in 1..n do printf "hello"
// should (structurally) equal l for any list/array/seq l
l.[0 .. (l.Length-1)]
// does what the name says
let replaceFirstAWithB l =
match l |> List.tryFindIndex ((=) 'A') with
| Some n -> l.[.. n-1] @ ['B'] @ l.[n+1 ..]
| None -> l
// Parses an string of the form A*B where A and B are integers.
// Should return `Result.Error "Could not parse an integer"` given the input "36*"
let parseInt s = match System.Int32.TryParse s with (true, r) -> Some r | _ -> None
let parseAndCalculateIntProduct (s:string) =
match s |> Seq.tryFindIndex ((=) '*') with
| Some n ->
match parseInt s.[0 .. n-1], parseInt s.[n+1 .. (s.Length-1)] with
| Some a, Some b -> Result.Ok(a*b)
| _ -> Result.Error "Could not parse an integer"
| None -> Result.Error "* not found" In fact I would expect that most slicing in practice where the start or end case is not a specified constant* would fail. The fundamental problem is that getting the items with index <= i and items with index >= i becomes hard, because it cannot be done with slicing any more. Moreover many users would expect slicing to work for this purpose ( *(Naturally it would be rare to write |
Beta Was this translation helpful? Give feedback.
-
I spent some time playing around with a different GetSlice implementation for lists, and I came to two conclusions that are at odds with one another:
So, perhaps begrudgingly, I'm willing to accept this as the definition and move forward. The fact that changing it to an error case would involve a breaking change pushes this over the edge for me. It still will be hard not to see this as an ergonomic wart for others who don't see negative indices as unrelated to slicing. This is also possible: > arr;;
val it : int [] = [|1; 2; 3; 4; 5; 6; 7; 8; 9; 10|]
> arr.[3..2];;
val it : int [] = [||]
> str;;
val it : string = "Hello"
> str.[3..2];;
val it : string = "" So it seems the definition of a slice
I also feel that this is wrong, but since it's been shipped for arrays, strings, and lists I think the ship has sailed on this one. |
Beta Was this translation helpful? Give feedback.
-
Here's my take on this: Make Slicing Consistent AgainAfter dotnet/fsharp#3475 we got Assuming we have a list Our current slicing behavior in F# looks like:
The current behavior in C# .NET preview is:
The current behavior in Python is:
(Note: We can't have x < a and y < a cases because Python negative indices mean something else) ProblemAs you can see from the example above the slicing behavior in F# is a lot more confusing than C# or Python. Sometimes when indexes are out of bounds you get [], sometimes you get Error, and sometimes you get either. C# disallows any out of bound indexes and Python just takes What I think the solution should be:
This is pretty much equivalent to Why?
Some of the disadvantages are:
From-the-end slicingI've talked to @KevinRansom about this and we can't use @dsyme @KevinRansom @cartermp what do you think? |
Beta Was this translation helpful? Give feedback.
-
I think this makes sense. It seems to replace some runtime slicing failures and compile time slicing failures with empty slices instead.
Here is an F# API for creating Multi-dimensional arrays that have non zero bases ->
https://msdn.microsoft.com/en-us/visualfsharpdocs/conceptual/array2d.createbased%5b't%5d-function-%5bfsharp%5d
Wierdly enough the dotnet framework doesn’t seem to provide an API for creating a single dimensioned array that is not zero based.
This is the dotnet API for it ->
https://docs.microsoft.com/en-us/dotnet/api/system.array.createinstance?view=netframework-4.8#System_Array_CreateInstance_System_Type_System_Int32___System_Int32___
Anchoring from the end using ^ seems fine to me, however, we probably would need to modify the signature for the slicing helper function to include a flag Indicating anchored at end.
GetSlice(x : int, y1 : int option, y2 : int option, anchoredfromend:bool
|
Beta Was this translation helpful? Give feedback.
-
F# is consistentThere are three consistent slicing definitions:
(Here F# is currently Intermediate. Intermediate is better than Forgiving@nelson-wu 's suggestion is to go from Intermediate to Forgiving On undisputably valid ranges ( Moving to Forgiving will lead to a significantly less safe language. Indices are easy to get wrong. Even off by one errors are common enough to get their own category. When slices are incorrectly specified, it is much better that they fail than succeed, leading to later unexpected output or failures. So I am against this change. In order to justify the change, examples will need to be given of where a user would naturally write a range, which we should consider good, and which currently fails. |
Beta Was this translation helpful? Give feedback.
-
@nelson-wu I think your proposal makes sense and I like it. Note that I think I'm now generally more in favor of being weaker here, after doing more numeric work in Python and seeing how convenient it is compared to F#. Much of that is due to more features than we have, but this certainly would move the needle. Regarding negative indicies, I agree that Regarding the signature of |
Beta Was this translation helpful? Give feedback.
-
I'm struggling with the phenomenon where
Help! |
Beta Was this translation helpful? Give feedback.
-
@dsyme if it helps, @nelson-wu has a fantastic example here: https://github.com/fsharp/fslang-design/blob/13a043d23db9ca847f70ba5f21cfe7238c349639/FSharp.Core-4.4.3.0/FS-1036-consistent-slicing.md#sample-use-case |
Beta Was this translation helpful? Give feedback.
-
@nelson-wu 's example of splitting arbitrary images into blocks of 3 is valid. An indecisive option @dsyme would be to introduce a new methods for tolerant slices to some or all collections. |
Beta Was this translation helpful? Give feedback.
-
@nelson-wu Let's open the additional alterations as a separate RFC, as RFC FS-1036 is done and dusted Call it "tolerant slicing" I guess |
Beta Was this translation helpful? Give feedback.
-
@dsyme Ah that's reasonable. I'll break it out into its own RFC. |
Beta Was this translation helpful? Give feedback.
-
Discussion for https://github.com/fsharp/fslang-design/blob/master/FSharp.Core-4.4.3.0/FS-1036-consistent-slicing.md
Beta Was this translation helpful? Give feedback.
All reactions