Integrated Random Testing

For a while I've been annoyed by the performance of testing libraries in general, but also with how random testing and performance testing are not better integrated and multithreaded. Testing libraries like Expecto do a great job of improving performance by running unit tests in parallel while also opening up useful functionality like stress testing. I want to take this further with a new prototype.

The goal is a simpler, more lightweight testing library with faster, more integrated parallel random testing with automatic parallel shrinking.

The library aims to encourage the shift from a number of unit and regression tests with hard coded input and output data to fewer more general random tests. This idea is covered well by John Hughes in Don't write tests! and the idea of One test to rule them all. Key takeaways are general random tests can provide more coverage for less test code, and larger test cases have a higher probability of finding a failure for a given execution time.

Features

test "PCG demo 1" {
    let pcg = PCG.TryParse "36185706b82c2e03f8" |> Option.get
    Test.equal pcg.Stream 54 "stream"
    Test.equal pcg.State 0x185706b82c2e03f8UL "state"
    let expectedNext = [|0xa15c02b7u;0x7b47f409u|] // ...
    let expectedState = [|0x2b47fed88766bb05UL;0x8b33296d19bf5b4eUL|] // ...
    for i = 0 to expectedNext.Length-1 do
        Test.equal (pcg.Next()) expectedNext.[i] ("n"+string i)
        Test.equal pcg.State expectedState.[i] ("s"+string i)
}
    test "gen" {
        test "int" {
            let! s = Gen.int
            let! l = Gen.int.[0..Int32.MaxValue-s]
            let! i = Gen.int.[s..s+l]
            Test.between i s (s+l) "between"
        }
        test "int distribution" {
            let freq = 10
            let buckets = 1000
            let! ints = Gen.seq.[freq * buckets] Gen.int.[0..buckets-1]
            let actual = Array.zeroCreate buckets
            Seq.iter (fun i -> actual.[i] <- actual.[i] + 1) ints
            let expected = Array.create buckets freq
            Test.chiSquared actual expected "chi-squared"
        }
test "list rev does nothing not" {
    let! list =
        let f ma mi bu = Version (int ma,int mi,int bu)
        Gen.map3 f Gen.byte Gen.byte Gen.byte
        |> Gen.list.[0..100]
    Test.equal (List.rev list) list "rev equal"
}
test "multithreading" {
    let n = 10
    let ms = MapSlim()
    test "update" {
        let! i = Gen.int.[..n-1]
        let v = &ms.GetRef i
        v <- 1-v
    }
    test "get" {
        let! i = Gen.int.[..n-1]
        let v = defaultValueArg (ms.GetOption i) 0
        Test.isTrue (v=0 || v=1) "get is 0 or 1"
    }
    test "item" {
        if ms.Count > 0 then
            let! i = Gen.int.[..ms.Count-1]
            let k,v = ms.Item i
            Test.lessThan k n "key is ok"
            Test.isTrue (v=0 || v=1) "value is 0 or 1"
    }
}
test "mapslim" {
    test "performance" {
        test "get" {
            let ms = MapSlim()
            let dict = Dictionary()
            let! n = Gen.int.[1..100]
            let! keys =
                Gen.array.[n] Gen.int.[-1000..1000]
                |> Gen.map (
                    Array.mapi (fun i h ->
                        let k = KeyWithHash(uint64 i, h)
                        ms.Set(k,k)
                        dict.Add(k,k)
                        k
                    )
                )
            Test.faster
                (fun () ->
                    for i = 0 to n-1 do
                        ms.GetOption keys.[i] |> ignore
                )
                (fun () ->
                    for i = 0 to n-1 do
                        dict.TryGetValue keys.[i] |> ignore
                )
                "get"
        }
        // ...
    }
    // ...
}
test "reference" {
    let getTest name (gen:Gen<'a>) = test name {
        let! items = Gen.tuple gen Gen.int
                        |> Gen.list.[..100]
        let! check = gen
        let! expectedFork =
            io {
                return
                List.fold (fun m (k,v) -> Map.add k v m) Map.empty items
                |> Map.tryFind check
            } |> IO.fork
        let actual =
            let ms = MapSlim()
            List.iter ms.Set items
            ms.GetOption check
            |> function | ValueSome i -> Some i | ValueNone -> None
        let! expected = expectedFork
        Test.equal actual expected "check value equal"
    }
    getTest "get byte" Gen.byte
    getTest "get char" Gen.char
    getTest "get int" Gen.int.[..10]
    getTest "get uint" Gen.uint.[..10u]
    getTest "get string" Gen.string
}

Conclusion

The prototype currently has no dependencies and is a single file for the library, one for Gen and one for Test. They can easily be copied into a project to try them out, and new Gen and Test functions added.

The library is still in an early stage. It is missing more tests, label functionality, async io and needs a tidy and performance work.

I am keen to share it now to see if there are further ideas. Some of the functionality if successful could be added to existing projects or it could form its own. It is unclear at this point.

The code can be found here.

module IRT
namespace System
namespace System.Threading
namespace System.Diagnostics
namespace System.Globalization
namespace System.Runtime
namespace System.Runtime.CompilerServices
namespace System.Runtime.InteropServices
namespace Microsoft
namespace Microsoft.FSharp
namespace Microsoft.FSharp.Core
PCG.Inc: uint64
Multiple items
val uint64 : value:'T -> uint64 (requires member op_Explicit)

--------------------
type uint64 = UInt64
PCG.State: uint64
val inc : uint64
val state : uint64
val stream : int
Multiple items
val int : value:'T -> int (requires member op_Explicit)

--------------------
type int = int32

--------------------
type int<'Measure> = int
val seed : uint64
Multiple items
type PCG =
  new : stream:int -> PCG
  private new : inc:uint64 * state:uint64 -> PCG
  new : stream:int * seed:uint64 -> PCG
  val Inc: uint64
  val mutable State: uint64
  member Next : unit -> uint32
  member Next : maxExclusive:int -> int
  member Next64 : unit -> uint64
  member Next64 : maxExclusive:int64 -> int64
  override ToString : unit -> string
  ...

--------------------
new : stream:int -> PCG
new : stream:int * seed:uint64 -> PCG
private new : inc:uint64 * state:uint64 -> PCG
Multiple items
type Stopwatch =
  new : unit -> Stopwatch
  member Elapsed : TimeSpan
  member ElapsedMilliseconds : int64
  member ElapsedTicks : int64
  member IsRunning : bool
  member Reset : unit -> unit
  member Restart : unit -> unit
  member Start : unit -> unit
  member Stop : unit -> unit
  static val Frequency : int64
  ...

--------------------
Stopwatch() : Stopwatch
Stopwatch.GetTimestamp() : int64
val i : PCG
val sprintf : format:Printf.StringFormat<'T> -> 'T
val s : string
Multiple items
val string : value:'T -> string

--------------------
type string = String
val l : int
property String.Length: int
val mutable stream : int
module Unchecked

from Microsoft.FSharp.Core.Operators
val defaultof<'T> : 'T
val mutable state : uint64
type Int32 =
  struct
    member CompareTo : value:obj -> int + 1 overload
    member Equals : obj:obj -> bool + 1 overload
    member GetHashCode : unit -> int
    member GetTypeCode : unit -> TypeCode
    member ToString : unit -> string + 3 overloads
    static val MaxValue : int
    static val MinValue : int
    static member Parse : s:string -> int + 3 overloads
    static member TryParse : s:string * result:int -> bool + 1 overload
  end
Int32.TryParse(s: string, result: byref<int>) : bool
Int32.TryParse(s: string, style: NumberStyles, provider: IFormatProvider, result: byref<int>) : bool
String.Substring(startIndex: int) : string
String.Substring(startIndex: int, length: int) : string
type NumberStyles =
  | None = 0
  | AllowLeadingWhite = 1
  | AllowTrailingWhite = 2
  | AllowLeadingSign = 4
  | AllowTrailingSign = 8
  | AllowParentheses = 16
  | AllowDecimalPoint = 32
  | AllowThousands = 64
  | AllowExponent = 128
  | AllowCurrencySymbol = 256
  ...
field NumberStyles.HexNumber: NumberStyles = 515
type UInt64 =
  struct
    member CompareTo : value:obj -> int + 1 overload
    member Equals : obj:obj -> bool + 1 overload
    member GetHashCode : unit -> int
    member GetTypeCode : unit -> TypeCode
    member ToString : unit -> string + 3 overloads
    static val MaxValue : uint64
    static val MinValue : uint64
    static member Parse : s:string -> uint64 + 3 overloads
    static member TryParse : s:string * result:uint64 -> bool + 1 overload
  end
UInt64.TryParse(s: string, result: byref<uint64>) : bool
UInt64.TryParse(s: string, style: NumberStyles, provider: IFormatProvider, result: byref<uint64>) : bool
union case Option.Some: Value: 'T -> Option<'T>
union case Option.None: Option<'T>
val p : PCG
val oldstate : uint64
val xorshifted : uint32
Multiple items
val uint32 : value:'T -> uint32 (requires member op_Explicit)

--------------------
type uint32 = UInt32
val rot : int
member PCG.Next : unit -> uint32
member PCG.Next : maxExclusive:int -> int
val maxExclusive : int
val bound : uint32
val threshold : uint32
val find : (unit -> int)
val r : uint32
val maxExclusive : int64
Multiple items
val int64 : value:'T -> int64 (requires member op_Explicit)

--------------------
type int64 = Int64

--------------------
type int64<'Measure> = int64
val bound : uint64
val threshold : uint64
val find : (unit -> int64)
val r : uint64
member PCG.Next64 : unit -> uint64
member PCG.Next64 : maxExclusive:int64 -> int64
Multiple items
module Result

from Microsoft.FSharp.Core

--------------------
[<Struct>]
type Result<'T,'TError> =
  | Ok of ResultValue: 'T
  | Error of ErrorValue: 'TError
val private traverse : f:('a -> Result<'b,'c>) -> list:'a list -> Result<'b list,'c list>
val f : ('a -> Result<'b,'c>)
Multiple items
val list : 'a list

--------------------
type 'T list = List<'T>
Multiple items
module List

from Microsoft.FSharp.Collections

--------------------
type List<'T> =
  | ( [] )
  | ( :: ) of Head: 'T * Tail: 'T list
    interface IReadOnlyList<'T>
    interface IReadOnlyCollection<'T>
    interface IEnumerable
    interface IEnumerable<'T>
    member GetSlice : startIndex:int option * endIndex:int option -> 'T list
    member Head : 'T
    member IsEmpty : bool
    member Item : index:int -> 'T with get
    member Length : int
    member Tail : 'T list
    ...
val fold : folder:('State -> 'T -> 'State) -> state:'State -> list:'T list -> 'State
[<Struct>]
val s : Result<'b list,'c list>
val i : 'a
union case Result.Ok: ResultValue: 'T -> Result<'T,'TError>
val l : 'b list
val h : 'b
union case Result.Error: ErrorValue: 'TError -> Result<'T,'TError>
val l : 'c list
val e : 'c
val h : 'c
val private sequence : list:Result<'a,'b> list -> Result<'a list,'b list>
Multiple items
val list : Result<'a,'b> list

--------------------
type 'T list = List<'T>
val id : x:'T -> 'T
namespace System.Text
union case Text.For: string -> Text
union case Text.Grey: string -> Text
union case Text.Red: string -> Text
union case Text.BrightRed: string -> Text
union case Text.Green: string -> Text
union case Text.BrightGreen: string -> Text
union case Text.Yellow: string -> Text
union case Text.BrightYellow: string -> Text
union case Text.Blue: string -> Text
union case Text.BrightBlue: string -> Text
union case Text.Magenta: string -> Text
union case Text.BrightMagenta: string -> Text
union case Text.Cyan: string -> Text
union case Text.BrightCyan: string -> Text
Multiple items
union case Text.Text: struct (Text * Text) -> Text

--------------------
namespace System.Text

--------------------
type Text =
  | For of string
  | Grey of string
  | Red of string
  | BrightRed of string
  | Green of string
  | BrightGreen of string
  | Yellow of string
  | BrightYellow of string
  | Blue of string
  | BrightBlue of string
  ...
    static member ( + ) : t1:Text * t2:Text -> Text
val t1 : Text
val t2 : Text
Multiple items
type LiteralAttribute =
  inherit Attribute
  new : unit -> LiteralAttribute

--------------------
new : unit -> LiteralAttribute
val private reset : string
val private toANSIList : t:Text -> string list
val t : Text
val toANSI : t:Text -> string
Multiple items
type String =
  new : value:char -> string + 7 overloads
  member Chars : int -> char
  member Clone : unit -> obj
  member CompareTo : value:obj -> int + 1 overload
  member Contains : value:string -> bool
  member CopyTo : sourceIndex:int * destination:char[] * destinationIndex:int * count:int -> unit
  member EndsWith : value:string -> bool + 2 overloads
  member Equals : obj:obj -> bool + 2 overloads
  member GetEnumerator : unit -> CharEnumerator
  member GetHashCode : unit -> int
  ...

--------------------
String(value: nativeptr<char>) : String
String(value: nativeptr<sbyte>) : String
String(value: char []) : String
String(c: char, count: int) : String
String(value: nativeptr<char>, startIndex: int, length: int) : String
String(value: nativeptr<sbyte>, startIndex: int, length: int) : String
String(value: char [], startIndex: int, length: int) : String
String(value: nativeptr<sbyte>, startIndex: int, length: int, enc: Text.Encoding) : String
String.Concat([<ParamArray>] values: string []) : string
   (+0 other overloads)
String.Concat(values: Collections.Generic.IEnumerable<string>) : string
   (+0 other overloads)
String.Concat<'T>(values: Collections.Generic.IEnumerable<'T>) : string
   (+0 other overloads)
String.Concat([<ParamArray>] args: obj []) : string
   (+0 other overloads)
String.Concat(arg0: obj) : string
   (+0 other overloads)
String.Concat(str0: string, str1: string) : string
   (+0 other overloads)
String.Concat(arg0: obj, arg1: obj) : string
   (+0 other overloads)
String.Concat(str0: string, str1: string, str2: string) : string
   (+0 other overloads)
String.Concat(arg0: obj, arg1: obj, arg2: obj) : string
   (+0 other overloads)
String.Concat(str0: string, str1: string, str2: string, str3: string) : string
   (+0 other overloads)
ListSlim.count: int
ListSlim.entries: 'k []
type Array =
  member Clone : unit -> obj
  member CopyTo : array:Array * index:int -> unit + 1 overload
  member GetEnumerator : unit -> IEnumerator
  member GetLength : dimension:int -> int
  member GetLongLength : dimension:int -> int64
  member GetLowerBound : dimension:int -> int
  member GetUpperBound : dimension:int -> int
  member GetValue : [<ParamArray>] indices:int[] -> obj + 7 overloads
  member Initialize : unit -> unit
  member IsFixedSize : bool
  ...
val empty<'T> : 'T []
val capacity : int
val zeroCreate : count:int -> 'T []
val m : ListSlim<'k>
val i : int
val set : elements:seq<'T> -> Set<'T> (requires comparison)
val v : 'k
val key : 'k
property Array.Length: int
val newEntries : 'k []
Array.Copy(sourceArray: Array, destinationArray: Array, length: int64) : unit
Array.Copy(sourceArray: Array, destinationArray: Array, length: int) : unit
Array.Copy(sourceArray: Array, sourceIndex: int64, destinationArray: Array, destinationIndex: int64, length: int64) : unit
Array.Copy(sourceArray: Array, sourceIndex: int, destinationArray: Array, destinationIndex: int, length: int) : unit
val init : count:int -> initializer:(int -> 'T) -> 'T []
val get : array:'T [] -> index:int -> 'T
val init : length:int -> initializer:(int -> 'T) -> 'T list
Multiple items
type StructAttribute =
  inherit Attribute
  new : unit -> StructAttribute

--------------------
new : unit -> StructAttribute
type private Entry<'k,'v> =
  struct
    val mutable bucket: int
    val mutable key: 'k
    val mutable value: 'v
    val mutable next: int
  end
Entry.bucket: int
Entry.key: 'k
Entry.value: 'v
Entry.next: int
Multiple items
type private InitialHolder<'k,'v> =
  new : unit -> InitialHolder<'k,'v>
  static member Initial : Entry<'k,'v> []

--------------------
private new : unit -> InitialHolder<'k,'v>
val initial : Entry<'k,'v> []
type IEquatable<'T> =
  member Equals : other:'T -> bool
MapSlim.count: int
MapSlim.entries: Entry<'k,'v> []
property InitialHolder.Initial: Entry<'k,'v> []
val powerOf2 : (int -> int)
val v : int
val twos : (int -> int)
val m : MapSlim<'k,'v> (requires equality and 'k :> IEquatable<'k>)
val oldEntries : Entry<'k,'v> [] (requires equality and 'k :> IEquatable<'k>)
val entries : Entry<'k,'v> [] (requires equality and 'k :> IEquatable<'k>)
val bi : int
Multiple items
type MethodImplAttribute =
  inherit Attribute
  new : unit -> MethodImplAttribute + 2 overloads
  val MethodCodeType : MethodCodeType
  member Value : MethodImplOptions

--------------------
MethodImplAttribute() : MethodImplAttribute
MethodImplAttribute(methodImplOptions: MethodImplOptions) : MethodImplAttribute
MethodImplAttribute(value: int16) : MethodImplAttribute
type MethodImplOptions =
  | Unmanaged = 4
  | ForwardRef = 16
  | PreserveSig = 128
  | InternalCall = 4096
  | Synchronized = 32
  | NoInlining = 8
  | AggressiveInlining = 256
  | NoOptimization = 64
field MethodImplOptions.NoInlining: MethodImplOptions = 8
val key : 'k (requires equality and 'k :> IEquatable<'k>)
val hashCode : int
member private MapSlim.Resize : unit -> unit
val bucketIndex : int
val value : 'v
Object.GetHashCode() : int
val mutable i : int
val not : value:bool -> bool
Object.Equals(obj: obj) : bool
IEquatable.Equals(other: 'k) : bool
val v : byref<'v>
member private MapSlim.AddKey : key:'k * hashCode:int -> byref<'v>
type byref<'T> = (# "<Common IL Type Omitted>" #)
val added : outref<bool>
type bool = Boolean
type outref<'T> = outref<'T>
[<Struct>]
type 'T voption = ValueOption<'T>
union case ValueOption.ValueSome: 'T -> ValueOption<'T>
union case ValueOption.ValueNone: ValueOption<'T>
val entries : Entry<'k,'v> (requires equality and 'k :> IEquatable<'k>)
Multiple items
type AllowNullLiteralAttribute =
  inherit Attribute
  new : unit -> AllowNullLiteralAttribute
  new : value:bool -> AllowNullLiteralAttribute
  member Value : bool

--------------------
new : unit -> AllowNullLiteralAttribute
new : value:bool -> AllowNullLiteralAttribute
Size.I: uint64
Size.L: Size list
Multiple items
type Size =
  interface IComparable
  interface IComparable<Size>
  new : i:uint64 * l:Size list -> Size
  val I: uint64
  val L: Size list
  override Equals : y:obj -> bool
  override GetHashCode : unit -> int
  static member zero : Size

--------------------
new : i:uint64 * l:Size list -> Size
type 'T list = List<'T>
val i : uint64
val l : Size list
val x : Size
val y : obj
Multiple items
type IComparable =
  member CompareTo : obj:obj -> int

--------------------
type IComparable<'T> =
  member CompareTo : other:'T -> int
val y : Size
val compare : (Size -> Size -> int64)
val i : int64
val fold2 : folder:('State -> 'T1 -> 'T2 -> 'State) -> state:'State -> list1:'T1 list -> list2:'T2 list -> 'State
val s : int64
val sign : value:'T -> int (requires member get_Sign)
type Gen<'a> =
  interface
    abstract member Gen : PCG -> 'a * Size
  end
Multiple items
type PCG =
  new : stream:int -> PCG
  private new : inc:uint64 * state:uint64 -> PCG
  new : stream:int * seed:uint64 -> PCG
  val Inc: uint64
  val mutable State: uint64
  member Next : unit -> uint32
  member Next : maxExclusive:int -> int
  member Next64 : unit -> uint64
  member Next64 : maxExclusive:int64 -> int64
  override ToString : unit -> string
  ...

--------------------
new : stream:int -> PCG
new : stream:int * seed:uint64 -> PCG
type GenRange<'a,'b> =
  interface
    inherit Gen<'b>
    abstract member GetSlice : 'a option * 'a option -> Gen<'b>
  end
type 'T option = Option<'T>
Multiple items
type GenBuilder =
  new : unit -> GenBuilder
  new : Gen:obj -> GenBuilder

--------------------
new : unit -> GenBuilder
new : Gen:obj -> GenBuilder
val g : unit * Size
property Size.zero: Size
Multiple items
val Gen : obj

--------------------
type Gen<'a> =
  interface
    abstract member Gen : PCG -> 'a * Size
  end
val fst : tuple:('T1 * 'T2) -> 'T1
type obj = Object
Multiple items
union case Text.Text: struct (Text * Text) -> Text

--------------------
module Text

from IRT

--------------------
namespace System.Text

--------------------
type Text =
  | For of string
  | Grey of string
  | Red of string
  | BrightRed of string
  | Green of string
  | BrightGreen of string
  | Yellow of string
  | BrightYellow of string
  | Blue of string
  | BrightBlue of string
  ...
    static member ( + ) : t1:Text * t2:Text -> Text
Multiple items
val Failure : message:string -> exn

--------------------
active recognizer Failure: exn -> string option
Multiple items
type Exception =
  new : unit -> Exception + 2 overloads
  member Data : IDictionary
  member GetBaseException : unit -> Exception
  member GetObjectData : info:SerializationInfo * context:StreamingContext -> unit
  member GetType : unit -> Type
  member HResult : int with get, set
  member HelpLink : string with get, set
  member InnerException : Exception
  member Message : string
  member Source : string with get, set
  ...

--------------------
Exception() : Exception
Exception(message: string) : Exception
Exception(message: string, innerException: exn) : Exception
type exn = Exception
Multiple items
type ListSlim<'k> =
  new : unit -> ListSlim<'k>
  new : capacity:int -> ListSlim<'k>
  val mutable private count: int
  val mutable private entries: 'k []
  member Add : key:'k -> int
  member ToArray : unit -> 'k []
  member ToList : unit -> 'k list
  member Count : int
  member Item : i:int -> 'k with get
  member Item : i:int -> 'k with set

--------------------
new : unit -> ListSlim<'k>
new : capacity:int -> ListSlim<'k>
val exists : predicate:('T -> bool) -> list:'T list -> bool
namespace System.IO
val failwith : message:string -> 'T
Multiple items
type AutoOpenAttribute =
  inherit Attribute
  new : unit -> AutoOpenAttribute
  new : path:string -> AutoOpenAttribute
  member Path : string

--------------------
new : unit -> AutoOpenAttribute
new : path:string -> AutoOpenAttribute
type unit = Unit
Multiple items
type MapSlim<'k,'v (requires equality and 'k :> IEquatable<'k>)> =
  new : unit -> MapSlim<'k,'v>
  new : capacity:int -> MapSlim<'k,'v>
  val mutable private count: int
  val mutable private entries: Entry<'k,'v> []
  member private AddKey : key:'k * hashCode:int -> byref<'v>
  member GetOption : key:'k -> 'v voption
  member GetRef : key:'k -> byref<'v>
  member GetRef : key:'k * added:outref<bool> -> byref<'v>
  member Item : i:int -> 'k * 'v
  member Key : i:int -> 'k
  ...

--------------------
new : unit -> MapSlim<'k,'v>
new : capacity:int -> MapSlim<'k,'v>
val map : mapping:('T -> 'U) -> list:'T list -> 'U list
Multiple items
type CallerLineNumberAttribute =
  inherit Attribute
  new : unit -> CallerLineNumberAttribute

--------------------
CallerLineNumberAttribute() : CallerLineNumberAttribute
Multiple items
type OptionalAttribute =
  inherit Attribute
  new : unit -> OptionalAttribute

--------------------
OptionalAttribute() : OptionalAttribute
Multiple items
type DefaultParameterValueAttribute =
  inherit Attribute
  new : value:obj -> DefaultParameterValueAttribute
  member Value : obj

--------------------
DefaultParameterValueAttribute(value: obj) : DefaultParameterValueAttribute
val lock : lockObject:'Lock -> action:(unit -> 'T) -> 'T (requires reference type)
val isNull : value:'T -> bool (requires 'T : null)
type IDisposable =
  member Dispose : unit -> unit
Multiple items
val seq : sequence:seq<'T> -> seq<'T>

--------------------
type seq<'T> = Collections.Generic.IEnumerable<'T>
Multiple items
val float : value:'T -> float (requires member op_Explicit)

--------------------
type float = Double

--------------------
type float<'Measure> = float
Multiple items
module Result

from IRT

--------------------
module Result

from Microsoft.FSharp.Core

--------------------
[<Struct>]
type Result<'T,'TError> =
  | Ok of ResultValue: 'T
  | Error of ErrorValue: 'TError
val map : mapping:('T -> 'U) -> result:Result<'T,'TError> -> Result<'U,'TError>
val rev : list:'T list -> 'T list
val mapError : mapping:('TError -> 'U) -> result:Result<'T,'TError> -> Result<'T,'U>
String.Join(separator: string, values: Collections.Generic.IEnumerable<string>) : string
String.Join<'T>(separator: string, values: Collections.Generic.IEnumerable<'T>) : string
String.Join(separator: string, [<ParamArray>] values: obj []) : string
String.Join(separator: string, [<ParamArray>] value: string []) : string
String.Join(separator: string, value: string [], startIndex: int, count: int) : string
type IFormatProvider =
  member GetFormat : formatType:Type -> obj
field NumberStyles.Any: NumberStyles = 511
Multiple items
type CultureInfo =
  new : name:string -> CultureInfo + 3 overloads
  member Calendar : Calendar
  member ClearCachedData : unit -> unit
  member Clone : unit -> obj
  member CompareInfo : CompareInfo
  member CultureTypes : CultureTypes
  member DateTimeFormat : DateTimeFormatInfo with get, set
  member DisplayName : string
  member EnglishName : string
  member Equals : value:obj -> bool
  ...

--------------------
CultureInfo(name: string) : CultureInfo
CultureInfo(culture: int) : CultureInfo
CultureInfo(name: string, useUserOverride: bool) : CultureInfo
CultureInfo(culture: int, useUserOverride: bool) : CultureInfo
property CultureInfo.InvariantCulture: CultureInfo
static member PCG.TryParse : s:string -> PCG option
val append : array1:'T [] -> array2:'T [] -> 'T []
val tryFind : predicate:('T -> bool) -> list:'T list -> 'T option
Multiple items
type StringBuilder =
  new : unit -> StringBuilder + 5 overloads
  member Append : value:string -> StringBuilder + 19 overloads
  member AppendFormat : format:string * arg0:obj -> StringBuilder + 7 overloads
  member AppendLine : unit -> StringBuilder + 1 overload
  member Capacity : int with get, set
  member Chars : int -> char with get, set
  member Clear : unit -> StringBuilder
  member CopyTo : sourceIndex:int * destination:char[] * destinationIndex:int * count:int -> unit
  member EnsureCapacity : capacity:int -> int
  member Equals : sb:StringBuilder -> bool
  ...

--------------------
Text.StringBuilder() : Text.StringBuilder
Text.StringBuilder(capacity: int) : Text.StringBuilder
Text.StringBuilder(value: string) : Text.StringBuilder
Text.StringBuilder(value: string, capacity: int) : Text.StringBuilder
Text.StringBuilder(capacity: int, maxCapacity: int) : Text.StringBuilder
Text.StringBuilder(value: string, startIndex: int, length: int, capacity: int) : Text.StringBuilder
val ignore : value:'T -> unit
module Seq

from Microsoft.FSharp.Collections
val map : mapping:('T -> 'U) -> source:seq<'T> -> seq<'U>
val max : source:seq<'T> -> 'T (requires comparison)
val append : source1:seq<'T> -> source2:seq<'T> -> seq<'T>
val iter : action:('T -> unit) -> source:seq<'T> -> unit
val countBy : projection:('T -> 'Key) -> source:seq<'T> -> seq<'Key * int> (requires equality)
val choose : chooser:('T -> 'U option) -> source:seq<'T> -> seq<'U>
val toList : source:seq<'T> -> 'T list
val collect : mapping:('T -> 'U list) -> list:'T list -> 'U list
val isEmpty : list:'T list -> bool
val where : predicate:('T -> bool) -> list:'T list -> 'T list
val distinctBy : projection:('T -> 'Key) -> list:'T list -> 'T list (requires equality)
val toArray : list:'T list -> 'T []
val unzip : array:('T1 * 'T2) [] -> 'T1 [] * 'T2 []
val contains : value:'T -> source:'T list -> bool (requires equality)
val choose : chooser:('T -> 'U option) -> list:'T list -> 'U list
val sortInPlace : array:'T [] -> unit (requires comparison)
val sqrt : value:'T -> 'U (requires member Sqrt)
val reduce : reduction:('T -> 'T -> 'T) -> list:'T list -> 'T
type Console =
  static member BackgroundColor : ConsoleColor with get, set
  static member Beep : unit -> unit + 1 overload
  static member BufferHeight : int with get, set
  static member BufferWidth : int with get, set
  static member CapsLock : bool
  static member Clear : unit -> unit
  static member CursorLeft : int with get, set
  static member CursorSize : int with get, set
  static member CursorTop : int with get, set
  static member CursorVisible : bool with get, set
  ...
Console.WriteLine() : unit
   (+0 other overloads)
Console.WriteLine(value: string) : unit
   (+0 other overloads)
Console.WriteLine(value: obj) : unit
   (+0 other overloads)
Console.WriteLine(value: uint64) : unit
   (+0 other overloads)
Console.WriteLine(value: int64) : unit
   (+0 other overloads)
Console.WriteLine(value: uint32) : unit
   (+0 other overloads)
Console.WriteLine(value: int) : unit
   (+0 other overloads)
Console.WriteLine(value: float32) : unit
   (+0 other overloads)
Console.WriteLine(value: float) : unit
   (+0 other overloads)
Console.WriteLine(value: decimal) : unit
   (+0 other overloads)
event Console.CancelKeyPress: IEvent<ConsoleCancelEventHandler,ConsoleCancelEventArgs>
Multiple items
module Event

from Microsoft.FSharp.Control

--------------------
type Event<'T> =
  new : unit -> Event<'T>
  member Trigger : arg:'T -> unit
  member Publish : IEvent<'T>

--------------------
type Event<'Delegate,'Args (requires delegate and 'Delegate :> Delegate)> =
  new : unit -> Event<'Delegate,'Args>
  member Trigger : sender:obj * args:'Args -> unit
  member Publish : IEvent<'Delegate,'Args>

--------------------
new : unit -> Event<'T>

--------------------
new : unit -> Event<'Delegate,'Args>
val add : callback:('T -> unit) -> sourceEvent:IEvent<'Del,'T> -> unit (requires delegate and 'Del :> Delegate)
Console.Write(value: string) : unit
   (+0 other overloads)
Console.Write(value: obj) : unit
   (+0 other overloads)
Console.Write(value: uint64) : unit
   (+0 other overloads)
Console.Write(value: int64) : unit
   (+0 other overloads)
Console.Write(value: uint32) : unit
   (+0 other overloads)
Console.Write(value: int) : unit
   (+0 other overloads)
Console.Write(value: float32) : unit
   (+0 other overloads)
Console.Write(value: decimal) : unit
   (+0 other overloads)
Console.Write(value: float) : unit
   (+0 other overloads)
Console.Write(buffer: char []) : unit
   (+0 other overloads)
val length : list:'T list -> int
Multiple items
val ref : value:'T -> 'T ref

--------------------
type 'T ref = Ref<'T>
type Int64 =
  struct
    member CompareTo : value:obj -> int + 1 overload
    member Equals : obj:obj -> bool + 1 overload
    member GetHashCode : unit -> int
    member GetTypeCode : unit -> TypeCode
    member ToString : unit -> string + 3 overloads
    static val MaxValue : int64
    static val MinValue : int64
    static member Parse : s:string -> int64 + 3 overloads
    static member TryParse : s:string * result:int64 -> bool + 1 overload
  end
field int64.MaxValue: int64 = 9223372036854775807L
type Interlocked =
  static member Add : location1:int * value:int -> int + 1 overload
  static member CompareExchange : location1:int * value:int * comparand:int -> int + 6 overloads
  static member Decrement : location:int -> int + 1 overload
  static member Exchange : location1:int * value:int -> int + 6 overloads
  static member Increment : location:int -> int + 1 overload
  static member MemoryBarrier : unit -> unit
  static member Read : location:int64 -> int64
Interlocked.Increment(location: byref<int64>) : int64
Interlocked.Increment(location: byref<int>) : int
type ThreadPool =
  static member BindHandle : osHandle:nativeint -> bool + 1 overload
  static member GetAvailableThreads : workerThreads:int * completionPortThreads:int -> unit
  static member GetMaxThreads : workerThreads:int * completionPortThreads:int -> unit
  static member GetMinThreads : workerThreads:int * completionPortThreads:int -> unit
  static member QueueUserWorkItem : callBack:WaitCallback -> bool + 1 overload
  static member RegisterWaitForSingleObject : waitObject:WaitHandle * callBack:WaitOrTimerCallback * state:obj * millisecondsTimeOutInterval:uint32 * executeOnlyOnce:bool -> RegisteredWaitHandle + 3 overloads
  static member SetMaxThreads : workerThreads:int * completionPortThreads:int -> bool
  static member SetMinThreads : workerThreads:int * completionPortThreads:int -> bool
  static member UnsafeQueueNativeOverlapped : overlapped:NativeOverlapped -> bool
  static member UnsafeQueueUserWorkItem : callBack:WaitCallback * state:obj -> bool
  ...
ThreadPool.UnsafeQueueUserWorkItem(callBack: WaitCallback, state: obj) : bool
module Option

from Microsoft.FSharp.Core
val isNone : option:'T option -> bool
val set : array:'T [] -> index:int -> value:'T -> unit
val tryPick : chooser:('T -> 'U option) -> list:'T list -> 'U option
val pick : chooser:('T -> 'U option) -> list:'T list -> 'U
val defaultValue : value:'T -> option:'T option -> 'T
field Stopwatch.Frequency: int64
val defaultWith : defThunk:(unit -> 'T) -> option:'T option -> 'T
type Environment =
  static member CommandLine : string
  static member CurrentDirectory : string with get, set
  static member CurrentManagedThreadId : int
  static member Exit : exitCode:int -> unit
  static member ExitCode : int with get, set
  static member ExpandEnvironmentVariables : name:string -> string
  static member FailFast : message:string -> unit + 1 overload
  static member GetCommandLineArgs : unit -> string[]
  static member GetEnvironmentVariable : variable:string -> string + 1 overload
  static member GetEnvironmentVariables : unit -> IDictionary + 1 overload
  ...
  nested type SpecialFolder
  nested type SpecialFolderOption
Interlocked.Decrement(location: byref<int64>) : int64
Interlocked.Decrement(location: byref<int>) : int
val min : e1:'T -> e2:'T -> 'T (requires comparison)
val iteri : action:(int -> 'T -> unit) -> array:'T [] -> unit
val iter : action:('T -> unit) -> option:'T option -> unit
type GC =
  static member AddMemoryPressure : bytesAllocated:int64 -> unit
  static member CancelFullGCNotification : unit -> unit
  static member Collect : unit -> unit + 4 overloads
  static member CollectionCount : generation:int -> int
  static member EndNoGCRegion : unit -> unit
  static member GetGeneration : obj:obj -> int + 1 overload
  static member GetTotalMemory : forceFullCollection:bool -> int64
  static member KeepAlive : obj:obj -> unit
  static member MaxGeneration : int
  static member ReRegisterForFinalize : obj:obj -> unit
  ...
GC.GetTotalMemory(forceFullCollection: bool) : int64
Multiple items
type Thread =
  inherit CriticalFinalizerObject
  new : start:ThreadStart -> Thread + 3 overloads
  member Abort : unit -> unit + 1 overload
  member ApartmentState : ApartmentState with get, set
  member CurrentCulture : CultureInfo with get, set
  member CurrentUICulture : CultureInfo with get, set
  member DisableComObjectEagerCleanup : unit -> unit
  member ExecutionContext : ExecutionContext
  member GetApartmentState : unit -> ApartmentState
  member GetCompressedStack : unit -> CompressedStack
  member GetHashCode : unit -> int
  ...

--------------------
Thread(start: ThreadStart) : Thread
Thread(start: ParameterizedThreadStart) : Thread
Thread(start: ThreadStart, maxStackSize: int) : Thread
Thread(start: ParameterizedThreadStart, maxStackSize: int) : Thread
Thread.Sleep(timeout: TimeSpan) : unit
Thread.Sleep(millisecondsTimeout: int) : unit
val max : e1:'T -> e2:'T -> 'T (requires comparison)
val iter : action:('T -> unit) -> list:'T list -> unit
Multiple items
val byte : value:'T -> byte (requires member op_Explicit)

--------------------
type byte = Byte
val defaultArg : arg:'T option -> defaultValue:'T -> 'T
field int.MaxValue: int = 2147483647
type BitConverter =
  static val IsLittleEndian : bool
  static member DoubleToInt64Bits : value:float -> int64
  static member GetBytes : value:bool -> byte[] + 9 overloads
  static member Int64BitsToDouble : value:int64 -> float
  static member ToBoolean : value:byte[] * startIndex:int -> bool
  static member ToChar : value:byte[] * startIndex:int -> char
  static member ToDouble : value:byte[] * startIndex:int -> float
  static member ToInt16 : value:byte[] * startIndex:int -> int16
  static member ToInt32 : value:byte[] * startIndex:int -> int
  static member ToInt64 : value:byte[] * startIndex:int -> int64
  ...
BitConverter.Int64BitsToDouble(value: int64) : float
type 'T array = 'T []
val toList : array:'T [] -> 'T list
val unzip : list:('T1 * 'T2) list -> 'T1 list * 'T2 list
val init : count:int -> initializer:(int -> 'T) -> seq<'T>
Multiple items
val char : value:'T -> char (requires member op_Explicit)

--------------------
type char = Char
module Operators

from Microsoft.FSharp.Core
val int : value:'T -> int (requires member op_Explicit)
val uint64 : value:'T -> uint64 (requires member op_Explicit)
Multiple items
type CompilationRepresentationAttribute =
  inherit Attribute
  new : flags:CompilationRepresentationFlags -> CompilationRepresentationAttribute
  member Flags : CompilationRepresentationFlags

--------------------
new : flags:CompilationRepresentationFlags -> CompilationRepresentationAttribute
type CompilationRepresentationFlags =
  | None = 0
  | Static = 1
  | Instance = 2
  | ModuleSuffix = 4
  | UseNullAsTrueValue = 8
  | Event = 16
CompilationRepresentationFlags.ModuleSuffix: CompilationRepresentationFlags = 4
module Printf

from Microsoft.FSharp.Core
val kbprintf : continuation:(unit -> 'Result) -> builder:Text.StringBuilder -> format:Printf.BuilderFormat<'T,'Result> -> 'T
val box : value:'T -> obj
val exists : predicate:('T -> bool) -> array:'T [] -> bool
val fold2 : folder:('State -> 'T1 -> 'T2 -> 'State) -> state:'State -> array1:'T1 [] -> array2:'T2 [] -> 'State
val abs : value:'T -> 'T (requires member Abs)
val get : option:'T option -> 'T
val create : count:int -> value:'T -> 'T []
Multiple items
type Version =
  new : unit -> Version + 4 overloads
  member Build : int
  member Clone : unit -> obj
  member CompareTo : version:obj -> int + 1 overload
  member Equals : obj:obj -> bool + 1 overload
  member GetHashCode : unit -> int
  member Major : int
  member MajorRevision : int16
  member Minor : int
  member MinorRevision : int16
  ...

--------------------
Version() : Version
Version(version: string) : Version
Version(major: int, minor: int) : Version
Version(major: int, minor: int, build: int) : Version
Version(major: int, minor: int, build: int, revision: int) : Version
val defaultValueArg : arg:'T voption -> defaultValue:'T -> 'T
val dict : keyValuePairs:seq<'Key * 'Value> -> Collections.Generic.IDictionary<'Key,'Value> (requires equality)
val mapi : mapping:(int -> 'T -> 'U) -> array:'T [] -> 'U []
Multiple items
module Map

from Microsoft.FSharp.Collections

--------------------
type Map<'Key,'Value (requires comparison)> =
  interface IReadOnlyDictionary<'Key,'Value>
  interface IReadOnlyCollection<KeyValuePair<'Key,'Value>>
  interface IEnumerable
  interface IComparable
  interface IEnumerable<KeyValuePair<'Key,'Value>>
  interface ICollection<KeyValuePair<'Key,'Value>>
  interface IDictionary<'Key,'Value>
  new : elements:seq<'Key * 'Value> -> Map<'Key,'Value>
  member Add : key:'Key * value:'Value -> Map<'Key,'Value>
  member ContainsKey : key:'Key -> bool
  ...

--------------------
new : elements:seq<'Key * 'Value> -> Map<'Key,'Value>
val add : key:'Key -> value:'T -> table:Map<'Key,'T> -> Map<'Key,'T> (requires comparison)
val empty<'Key,'T (requires comparison)> : Map<'Key,'T> (requires comparison)
val tryFind : key:'Key -> table:Map<'Key,'T> -> 'T option (requires comparison)