Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update docs with new Arbitrary API based on ArbMap #644

Open
LAC-Tech opened this issue Sep 14, 2023 · 6 comments
Open

Update docs with new Arbitrary API based on ArbMap #644

LAC-Tech opened this issue Sep 14, 2023 · 6 comments

Comments

@LAC-Tech
Copy link

LAC-Tech commented Sep 14, 2023

Reading https://fscheck.github.io/FsCheck//TestData.html
It says:

"Arb.from<'a> returns the registered Arbitrary instance for the given type 'a"

But that's not available, this won't compile.

open FsCheck.FSharp
open Laterbase.Core
open System

Console.Clear ()

let genBytes = Arb.from<byte>

Nor is it in the API docs

https://fscheck.github.io/FsCheck/reference/fscheck-fsharp-arb.html

I'm basically trying to re-write the 2.0 function in 3.0, and I'm at a bit of a loss.

Arb.generate<byte> |> Gen.arrayOfLength 16
@kurtschelfthout
Copy link
Member

kurtschelfthout commented Sep 15, 2023 via email

@bartelink
Copy link
Contributor

Per #638, I have a:

module ArbMap =
    let defGen<'t> = ArbMap.defaults |> ArbMap.generate<'t>

That I sprinkle around

The changes are listed in https://github.com/fscheck/FsCheck/blob/master/FsCheck%20Release%20Notes.md

🤔 (I found it by searching for generate) Perhaps there could be an explicit long form summary of moved methods in there so people can search for the string Arb.generate etc ? Or maybe if the changelog was formatted a la KeepAChangeLog e.g. https://github.com/jet/equinox/blob/master/CHANGELOG.md#changed, then you could look up Arb in the changes ?

@LAC-Tech
Copy link
Author

LAC-Tech commented Sep 15, 2023

What would help me - as someone new to the library in general - is a very basic example of how to use the built in generators to build your own generators.

Per #638, I have a:

module ArbMap =
    let defGen<'t> = ArbMap.defaults |> ArbMap.generate<'t>

That I sprinkle around

This is equivalent to the old Arb.generate?

@bartelink
Copy link
Contributor

Yes - it's equivalent. See this PR for some code that uses it https://github.com/jet/equinox/pull/412/files

In general I'd recommend downloading the FsCheck solution and doing a text search within in - there's some examples and you also get to see the relationships. The changelog linked above actually does detail the transitions quite well if you read it carefully, but I've been in your shoes wondering where all the cheese has gone and when the yaks are going to part ways and let you move on, so I can definitely empathise...

@bartelink
Copy link
Contributor

I also learned/realised/figured out recently that the best/typical way to decorate the generation is by having a "factory" Arb that takes the IArbMap as an argument, i.e.:

type ModelArbs private () =
    // Builds based on Arbs in the ArbMap
    static member CatalogEntry map: Arbitrary<CatalogEntry> =
        ArbMap.generate map
        |> Gen.filter (not << System.String.IsNullOrWhiteSpace)
        |> Gen.map CatalogEntry.fromId
        |> Arb.fromGen
    // Some UoM types (such as tokens, which is a `float` with a UoM) won't serialize if NaN / Infinity
    static member NormalFloats map: Arbitrary<float> = Arb.fromGen <| gen {
        let! NormalFloat f = ArbMap.generate map
        return f }

https://stackoverflow.com/a/77982678/11635

cc @kurtschelfthout It took me ages to work this out as I don't believe the docs mention it in the samples. For people trying to use FsCheck via FsCheck.Xunit/Nunit (and without reading the source), I suspect the way that the Factory signature for an Arb works (and that you can build an ArbMap by adding them singly, adding a factory singly, scan a type (and that both signatures work in that context)) is far from obvious as it stands.

But I do appreciate that you said no to the ArbMap.defGen thing I have above - ultimately that's a misleading local maximum; I'll be replacing it with the factory technique intead in my normal usage

@kurtschelfthout
Copy link
Member

I know this is far from obvious. Besides the obvious problem that the documentation is out of date, the problem of automatic generation is actually pretty thorny. Because you often want to customize the generation of a type that's nested inside some other types, and has yet other types as parameters. So you could say I started with the idea of "just put all the types in a Type -> generator map, but then realized that doesn't quite work for common use cases like collections List<'a> and Dictionary<Tuple<'a,'b>, ('c,'d)> in that you need some sort of factory to put all the generic type args together, if that makes sense. So it ended up being a dependency injection type thing, which I otherwise hate...so yeah.

@kurtschelfthout kurtschelfthout changed the title Docs mention functions that don't seem to be there. Update docs with new Arbitrary API based on ArbMap Mar 3, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants