-
Notifications
You must be signed in to change notification settings - Fork 842
Description
A pervasive issue throughout FSharp.Core is that when a function receives an argument that's invalid or violates a constraint the error message says what went wrong, but not how it went wrong.
For an example, let's look at Array.map3
> Array.map3 mapfn arr1 arr2 arr3;;
System.ArgumentException: The arrays have different lengths.
at Microsoft.FSharp.Collections.ArrayModule.Map3[T1,T2,T3,TResult](FSharpFunc`2 mapping, T1[] array1, T2[] array2, T3[] array3)
at <StartupCode$FSI_0005>.$FSI_0005.main@()Is the length of only one of the arrays different from the other two? If so which array is it? Are the lengths of all the arrays different? What is the degree of difference between these lengths?
This is all important information to quickly diagnose the source of the error, and we have this information, so let's stop hiding it.
A better error message would be -
> Array.map3 mapfn arr1 arr2 arr3;;
System.ArgumentException: The arrays have different lengths.
array1.Length = 45, array2.Length = 1, array3.Length = 44
at Microsoft.FSharp.Collections.ArrayModule.Map3[T1,T2,T3,TResult](FSharpFunc`2 mapping, T1[] array1, T2[] array2, T3[] array3)
at <StartupCode$FSI_0005>.$FSI_0005.main@()By exposing the values that caused the failure it becomes easy to see when an off by one error has occurred, when a sequence hasn't been truncated properly, when an earlier function call did not produce the result you expected it to, and a wide variety of other unexpected execution results. It might not seem as valuable with the trivial example of a single function call in the REPL, but when you have a long pipeline of functions the more informative error feedback makes it easier to hone in on the bug.
Unfortunately we can't implement a better invalidArg like
let inline invalidArgf (arg:string) fmt =
let error msg = raise (new System.ArgumentException(msg,arg))
Printf.ksprintf error fmtinvalidArgf "array2" "%s\narray1.Length = %i, array2.Length = %i " "arraysHadDifferentLengths" array1.Length array2.LengthSince Printf hasn't been defined in FSharp.Core at the earliest points we'd want to start using it (all of the collections modules)
String.Format could provide an alternative
let inline invalidArgFmt (arg:string) fmt argArray =
let msg = String.Format(fmt,argArray)
raise (new System.ArgumentException(msg,arg))invalidArgFmt "array2" "{0}\narray1.Length = {1}, array2.Length = {2} " [|"arraysHadDifferentLengths";array1.Length; array2.Length|]Or if someone else has a better (and more type safe) approach please suggest it.
Local.fs
-
List.init- return invalid count -
List.takeFreshConsTail- return the amount of elements it fell short -
List.take- return invalid count -
List.take- change to invalid arg, list can't be empty -
List.splitAtFreshConsTail- return the amount of elements it fell short -
List.splitAt- return invalid index -
List.splitAt- return the amount of elements it fell short -
List.window- return invalid windowsize -
List.chunkBySize- return invalid chunksize -
List.splitInto- return invalid count -
List.zipToFreshConsTail- return which list is shorter -
List.zip- return which list is shorter -
List.zip3ToFreshConsTail- return which list is shorter -
List.zip3- return which list is shorter -
Array.Sort- return difference in number of keys vs values -
Array.Sort- return difference in number of keys vs values #2 -
Array.Init- return invalid count