question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Can these Arbitraries (gen+shrinker) be simplified?

See original GitHub issue

I’m trying to implement Arbitraries (gens+shrinkers) for some constrained types, such as CloseDateInterval and NonEmptyNonWhitespaceString. I find the documentation a bit confusing and lacking in examples. I have something that seems to work, but I have no idea if this is the best way to do it.

I’d simply like anyone to take a quick look and tell me if I’m on the right track or if, as I suspect, I’ve missed some more elegant way of doing it.

Here’s the code for CloseDateInterval:

/// A tuple with two dates no more than 500 days apart
type CloseDateInterval = CloseDateInterval of DateTime * DateTime with
    // Do I need to implement this? What happens if not?
    static member op_Explicit(CloseDateInterval (x1, x2)) = (x1, x2)

let private gen2CloseDateTimes maxDaysApart =
    gen {
        let! d1 = Arb.generate<DateTime>
        let! d2 = Arb.generate<DateTime>
        let d2 = DateTime(d1.Year, d1.Month, d1.Day, d2.Hour, d2.Minute, d2.Second)
        let! daysApart = Gen.choose (-maxDaysApart, maxDaysApart)
        return (d1, d2.AddDays(float daysApart))
    }

type ArbitraryAdditions =
    static member CloseDateInterval() = 
        let shrinker (d1, d2) =
            let shrink = Arb.Default.DateTime().Shrinker
            (shrink d1, shrink d2) ||> Seq.zip
        Arb.fromGenShrink ((gen2CloseDateTimes 500), shrinker)
        |> Arb.convert CloseDateInterval (fun (CloseDateInterval (d1, d2)) -> (d1, d2))

And here’s the code for NonEmptyNonWhitespaceString:

type NonEmptyNonWhitespaceString = NonEmptyNonWhitespaceString of string with
    // Do I need to implement this? What happens if not?
    static member op_Explicit(NonEmptyNonWhitespaceString s) = s

type ArbitraryAdditions =
    static member NonEmptyNonWhitespaceString() =
        let filter s = 
            not (isNull s) && s <> "" && s |> Seq.forall (not << System.Char.IsWhiteSpace)
        let arb =
            Arb.from<string>
            |> Arb.filter filter
            |> Arb.convert NonEmptyNonWhitespaceString string
        let shrinker (NonEmptyNonWhitespaceString s) = 
            let shrink = Arb.Default.String().Shrinker
            shrink s |> Seq.filter filter |> Seq.map NonEmptyNonWhitespaceString
        Arb.fromGenShrink (arb.Generator, shrinker)

Whenever I need these types generated, I simply add

[<Properties(Arbitrary = [|typeof<TestHelpers.ArbitraryAdditions>|])>]

to the module containing the tests, or using the Property attribute if I only need it on a single test. (I know you can also register them, but I’m having trouble with that, see #390.)

Issue Analytics

  • State:closed
  • Created 6 years ago
  • Comments:21 (12 by maintainers)

github_iconTop GitHub Comments

2reactions
ploehcommented, Sep 16, 2017

It is there to make the function int:'a -> int work in the example.

Perhaps we should simplify that example so that it doesn’t confuse newcomers. It can be rewritten like this:

type EvenInt = EvenInt of int

type ArbitraryModifiers =
    static member EvenInt () = 
        Arb.from<int> 
        |> Arb.filter (fun i -> i % 2 = 0) 
        |> Arb.convert EvenInt (fun (EvenInt i) -> i)
1reaction
ploehcommented, Sep 16, 2017

For that particular example, you can just let FsCheck generate any ints : int list, and then partition them:

let evens, odds = ints |> List.partition (fun i -> (i % 2) = 0)
Read more comments on GitHub >

github_iconTop Results From Across the Web

How To Simplify Fractions
Mr. J will go through simplifying fractions examples and explain the steps of how to put fractions in their simplest form.
Read more >
How to Simplify Fractions - Definition, Steps, Examples
This type of fraction can be simplified by taking the numerator and denominator as the product of variables and then cancelling the common...
Read more >
Learn Simplifying Fractions & Equivalent Fractions - [5-4-13]
We use equivalent fractions when we add, subtract, multiply, or divide fractions. We always simplify the resulting fraction to lowest terms to ...
Read more >
04 - Simplify Fractions to Lowest Terms ...
We can simplify a fraction by dividing the numerator and denominator ... We will simplify fractions into lowest terms after every fraction ...
Read more >
1.10 Simplifying Fractions
A fraction can be simplified if the top and bottom numbers can both be divided by the same number, without leaving a remainder....
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found