No instances of class FsCheck.Arbitrary`1[a] for type System.Object with arguments set []

Sep 26, 2012 at 8:13 PM
Edited Sep 26, 2012 at 8:14 PM

Hi, I'm getting an exception when trying to use a KeyValuePair generator through forAll, and I can't for the life of me figure out what I'm doing wrong.
The exception is thrown in https://github.com/mausch/fsharpx/commit/ad5adde2ee736129cad58f1c832b2a92a7343323#L1R196
and it's this:

 

Test 'FSharpx.LensTests.LensKeyValuePairKey' failed: System.Exception : No instances of class FsCheck.Arbitrary`1[a] for type System.Object with arguments set []
	at FsCheck.TypeClass.mi@176.Invoke(String message)
	at Microsoft.FSharp.Core.PrintfImpl.go@512-3[b,c,d](String fmt, Int32 len, FSharpFunc`2 outputChar, FSharpFunc`2 outa, b os, FSharpFunc`2 finalize, FSharpList`1 args, Int32 i)
	at Microsoft.FSharp.Core.PrintfImpl.run@510[b,c,d](FSharpFunc`2 initialize, String fmt, Int32 len, FSharpList`1 args)
	at Microsoft.FSharp.Core.PrintfImpl.capture@529[b,c,d](FSharpFunc`2 initialize, String fmt, Int32 len, FSharpList`1 args, Type ty, Int32 i)
	at .$Reflect.Invoke@617-4.Invoke(T1 inp)
	at FsCheck.TypeClass.GetInstance@165.Invoke(Tuple`2 tupledArg)
	at FsCheck.Common.f@1[a,b](IDictionary`2 memo, FSharpFunc`2 f, a n, Unit _arg1)
	at FsCheck.Common.memoizeWith[a,b](IDictionary`2 memo, FSharpFunc`2 f, a n)
	at FsCheck.TypeClass.TypeClass`1.GetInstance(Type instance, FSharpOption`1 arguments)
	at FsCheck.TypeClass.TypeClass`1.InstanceFor[T,TypeClassT](FSharpOption`1 arguments)
	at FsCheck.Arb.generate[Value]()
	G:\prg\FSharpx\tests\FSharpx.Tests\LensTests.fs(174,0): at FSharpx.LensTests.keyValuePairGen[a,b]()
	G:\prg\FSharpx\tests\FSharpx.Tests\LensTests.fs(179,0): at FSharpx.LensTests.keyValuePairArb[a,b]()
	G:\prg\FSharpx\tests\FSharpx.Tests\LensTests.fs(196,0): at FSharpx.LensTests.checkLensKV[a,b,c](String name, Lens`2 lens)
	G:\prg\FSharpx\tests\FSharpx.Tests\LensTests.fs(201,0): at FSharpx.LensTests.LensKeyValuePairKey()

 

The "fsCheck" function is just a trivial wrapper over Check.One, and LensProperties.GetSet is Lens<'a,'b> -> 'a -> bool (the first argument is provided there, giving a "'Value -> 'Testable" of 'a -> bool).
I'm doing something very similar a few lines below that: https://github.com/mausch/fsharpx/blob/ad5adde2ee736129cad58f1c832b2a92a7343323/tests/FSharpx.Tests/LensTests.fs#L242 which works fine, so I'm not sure what's wrong here, or what the exception message means.


Thanks for any hints.


Mauricio

Coordinator
Sep 27, 2012 at 1:36 AM

I would guess one of the generic type arguments gets unexpectedly constrained to object somehow. Try adding them explicitly to the Arb.generate calls, then the compiler will warn about them.

The error basically says that it needs to generate an obj at some point but doesn't have an Arbitrary<obj> instance.

Sep 27, 2012 at 3:34 AM
Edited Sep 27, 2012 at 3:43 AM

I tried defining all type arguments to int in the final Arbs when used in Prop.forAll and got "No instances of class FsCheck.Arbitrary`1[a] for type System.Int32 with arguments set []" which is even weirder.

Then I noticed that the exception was thrown from keyValuePairGen... I switched to a monadic gen expression and it fixed it... still not sure why, might be some unexpected strict evaluation. https://github.com/mausch/fsharpx/commit/ffd9a13a77f519c789b5c118704878c25c283436

Cheers

Coordinator
Sep 27, 2012 at 6:40 PM

Oh, I think that's some initialization order/side effecting weirdness with Arbitrary registration hitting you. The Arbitrary instances are kept in a dictionary somewhere, and the dictionary is initialized with the default instances as part of the initialization of the module in which it is defined.

As per F# rules, this initialization is only run when a function is called in that module. I'm guessing it's in the Arb or Gen module. Anyway, somehow your earlier code didn't trigger that initialization so FsCheck didn't find any of its default generator instances (such as obj and int32, but I overlooked it in the case of obj and thought there just wasn't any). I'm thinking the other generators you wrote somehow did trigger it, so they worked fine subsequently.

This is a gnarly problem I can't seem to find a really good solution for - in QuickCheck typeclasses take care of this, but on the other hand doing it dynamically gives you some interesting flexibility too.