2

I'm new on F#, and can't see how extract the int value from:

let autoInc = FsCheck.Gen.choose(1,999)

The compiler say the type is Gen<int>, but can't get the int from it!. I need to convert it to decimal, and both types are not compatible.

mamcx
  • 15,916
  • 26
  • 101
  • 189
  • 2
    Have you looked through the functionality in the [`Gen` module](https://fsharp.github.io/FsCheck/reference/fscheck-gen.html)? – ildjarn Apr 17 '15 at 03:12
  • Yes. But I don't understand this code. The code say ` let choose (l, h) = rand |> map (range (l,h) >> fst) ` but can't dechiper what it mean – mamcx Apr 17 '15 at 03:16
  • 1
    The result of that expression is not a number, but a _generator_ of numbers. It doesn't "contain" any particular number inside of it, so there is nothing to "extract". If you just want to generate a random number, use `System.Random`. – Fyodor Soikin Apr 17 '15 at 03:20
  • 2
    @mamcx : Well, that's the source code for `Gen.choose`, but I didn't link you to the source code for a reason -- I linked you to the _documentation_, which you should read. – ildjarn Apr 17 '15 at 03:21

3 Answers3

3

From a consumer's point of view, you can use the Gen.sample combinator which, given a generator (e.g. Gen.choose), gives you back some example values.

The signature of Gen.sample is:

val sample : size:int -> n:int -> gn:Gen<'a> -> 'a list

(* `size` is the size of generated test data
   `n`    is the number of samples to be returned
   `gn`   is the generator (e.g. `Gen.choose` in this case) *)

You can ignore size because Gen.choose ignores it, as its distribution is uniform, and do something like:

let result = Gen.choose(1,999) |> Gen.sample 0 1 |> Seq.exactlyOne |> decimal

(* 0 is the `size` (gets ignored by Gen.choose)
   1 is the number of samples to be returned *)

The result should be a value in the closed interval [1, 999], e.g. 897.

Nikos Baxevanis
  • 10,868
  • 2
  • 46
  • 80
2

Hi to add to what Nikos already told you, this is how you can get an decimal between 1 and 999:

#r "FsCheck.dll"

open FsCheck

let decimalBetween1and999 : Gen<decimal> =
    Arb.generate |> Gen.suchThat (fun d -> d >= 1.0m && d <= 999.0m)

let sample () = 
    decimalBetween1and999
    |> Gen.sample 0 1 
    |> List.head 

you can now just use sample () to get a random decimal back.

In case you just want integers between 1 and 999 but have those converted to decimal you can just do:

let decimalIntBetween1and999 : Gen<decimal> =
    Gen.choose (1,999)
    |> Gen.map decimal

let sampleInt () = 
    decimalIntBetween1and999
    |> Gen.sample 0 1 
    |> List.head 

what you probably really want to do instead

Is use this to write you some nice types and check properties like this (here using Xunit as a test-framework and the FsCheck.Xunit package:

open FsCheck
open FsCheck.Xunit

type DecTo999 = DecTo999 of decimal

type Generators = 
    static member DecTo999 =
        { new Arbitrary<DecTo999>() with
            override __.Generator = 
                Arb.generate 
                |> Gen.suchThat (fun d -> d >= 1.0m && d <= 999.0m)
                |> Gen.map DecTo999
        }

[<Arbitrary(typeof<Generators>)>]
module Tests =

  type Marker = class end

  [<Property>]
  let ``example property`` (DecTo999 d) =
    d > 1.0m
Random Dev
  • 51,810
  • 9
  • 92
  • 119
1

Gen<'a> is a type that essentially abstracts a function int -> 'a (the actual type is a bit more complex, but let's ignore for now). This function is pure, i.e. when given the same int, you'll get the same instance of 'a back every time. The idea is that FsCheck generates a bunch of random ints, feeds them to the Gen function, out come random instances of the type 'a you're interested in, and feeds those to a test.

So you can't really get out the int. You have in your hands a function that given an int, generates another int.

Gen.sample as described in another answer essentially just feeds a sequence of random ints to the function and applies it to each, returning the results.

The fact that this function is pure is important because it guarantees reproducibility: if FsCheck finds a value for which a test fails, you can record the original int that was fed into the Gen function - rerunning the test with that seed is guaranteed to generate the same values, i.e. reproduce the bug.

Kurt Schelfthout
  • 8,880
  • 1
  • 30
  • 48