FsCheck currently has inconsistent support for generating indirectly recursive records.
If a record type is immediately recursive, FsCheck raises an exception. No problem; that makes sense.
#r "nuget: FsCheck,3.3.2"
open FsCheck.FSharp
type R = { R : R }
ArbMap.defaults
|> ArbMap.generate<R>
|> Gen.sample 3
// System.Reflection.TargetInvocationException: Exception has been thrown by the target of an invocation.
// ---> System.Exception: Recursive record types cannot be generated automatically: …
But if the record type is not immediately recursive, the user experience is not ideal: the user either sees a stack overflow exception, or, if FsCheck is being run via another test runner (xUnit, NUnit), the test seems simply not to run at all, because the test process crashes.
#r "nuget: FsCheck,3.3.2"
open FsCheck.FSharp
type R = { Rs : R list }
ArbMap.defaults
|> ArbMap.generate<R>
|> Gen.sample 3
// Stack overflow 😭.
FsCheck actually does support generating indirectly recursive records in one scenario: the record is recursive through a mutually-recursive multi-case union:
#r "nuget: FsCheck,3.3.2"
open FsCheck.FSharp
type R = { U : U }
and U = U of R | V of int
ArbMap.defaults
|> ArbMap.generate<R>
|> Gen.sample 3
// [|{ U = V -10 }; { U = U { U = U { U = U { U = V 27 } } } }; { U = V 8 }|]
Is there a good reason for FsCheck not to apply size-control logic similar to what it already does for mutually-recursive unions to all indirectly-recursive records by default, instead of just blowing the stack and forcing the user to write a custom generator? (There could well be a good reason. That's why I'm asking.)
FsCheck currently has inconsistent support for generating indirectly recursive records.
If a record type is immediately recursive, FsCheck raises an exception. No problem; that makes sense.
But if the record type is not immediately recursive, the user experience is not ideal: the user either sees a stack overflow exception, or, if FsCheck is being run via another test runner (xUnit, NUnit), the test seems simply not to run at all, because the test process crashes.
FsCheck actually does support generating indirectly recursive records in one scenario: the record is recursive through a mutually-recursive multi-case union:
Is there a good reason for FsCheck not to apply size-control logic similar to what it already does for mutually-recursive unions to all indirectly-recursive records by default, instead of just blowing the stack and forcing the user to write a custom generator? (There could well be a good reason. That's why I'm asking.)