Skip to content

Improve Error Reporting : Return Offending Arguments with Error Messages (Parameterized Errors) #1438

@cloudRoutine

Description

@cloudRoutine

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 fmt
invalidArgf "array2" "%s\narray1.Length = %i, array2.Length = %i " "arraysHadDifferentLengths" array1.Length array2.Length

Since 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

@KevinRansom
@forki
@isaacabraham

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions