Lecture 6 — Recursive data types

From lists to trees

Lists

In Lecture 4, ICons expects one recursive argument (of type IntList):

type IntList = INil
             | ICons of int * IntList
ICons 1 ICons 2 ICons 3 INil

Today we ask what happens when a constructor expects a number of such recursive arguments?

Binary trees

type 'a BinTree = Empty
                | Node of 'a BinTree * 'a * 'a BinTree

There are two constructors for constructing values of type 'a BinTree: one for an empty tree and one for a node. Constructors can be used as patterns to deconstruct values when matching.

The F# documentation calls them union cases; Empty and Node are their case identifiers, the names written in the declaration.

We can read the declaration as rules: Empty is a tree; if l and r are trees and x is a value, then Node (l, x, r) is a tree.

Drawing a tree

let small =
  Node (Node (Empty, 1, Empty), 2,
        Node (Empty, 3, Empty))
2 1 Empty Empty 3 Empty Empty

Here we have drawn every Empty. From now on, we will omit them and the figures show the nodes only.

Some terminology

let leaf x = Node (Empty, x, Empty)

let t = Node (Node (leaf 2, 7, Empty),
              9,
              Node (leaf 13, 21, leaf 25))
9 7 2 21 13 25

The root of t is 9. Its left and right subtrees are rooted at 7 and 21, its children.

A leaf is a node whose both subtrees are empty: 2, 13 and 25.

size: the number of nodes. height: the number of nodes on a longest path from the root.

The empty tree has size 0 and height 0.

Structural recursion

let rec size t =
  match t with
  | Empty -> 0
  | Node (l, _, r) -> size l + 1 + size r

let rec height t =
  match t with
  | Empty -> 0
  | Node (l, _, r) ->
      1 + max (height l) (height r)

Recursive calls on recursive substructures (subtrees).

What are size t and height t?

let sizeT = size t
let heightT = height t
6
3

Not every tree is finite

Interpreting the constructors of 'a BinTree as (inductive) rules, we can only define finite trees. F# also accepts cyclic definitions:

let rec allOne = Node (allOne, 1, allOne)

allOne is an int BinTree: both subtrees are allOne itself, so the tree is cyclic and infinite. F# Interactive prints it as

val allOne: int BinTree = Node (..., 1, ...)

What is size allOne?

It never returns: the recursion never reaches Empty (stack overflow).

> size allOne;;
Stack overflow.

Recall that let rec xs = 1 :: 2 :: xs is rejected:

error FS0260: Recursive values cannot appear directly as a construction
of the type 'List`1' within a recursive binding. This feature has been
removed from the F# language. Consider using a record instead.

Functions defined by structural recursion terminate on finite trees.

Binary search trees

The search-tree condition

For every Node (l, x, r) in the tree: every value in l is less than x, and every value in r is greater than x.

Thus, a value occurs at most once. The tree t is indeed a search tree.

Insertion

let rec insert x t =
  match t with
  | Empty -> Node (Empty, x, Empty)
  | Node (l, y, r) ->
      match compare x y with
      | 0 -> t
      | c when c < 0 -> Node (insert x l, y, r)
      | _ -> Node (l, y, insert x r)

Structural recursion; the constructors are used as patterns.

If x is already in t, then t itself is returned: no duplicates.

What type does F# infer for insert?

val insert: x: 'a -> t: 'a BinTree -> 'a BinTree when 'a: comparison

compare requires the comparison constraint.

Membership

let rec contains x t =
  match t with
  | Empty -> false
  | Node (l, y, r) ->
      if   x < y then contains x l
      elif x > y then contains x r
      else true

Only one path from the root is explored. A comparison chain can also be written with if/elif.

What are contains 13 t and contains 8 t?

let contains13 = contains 13 t
let contains8 = contains 8 t
true
false

BST invariant not enforced by the type

let bad = Node (leaf 5, 3, Empty)
3 5

bad type-checks, but it is not a search tree.

Is contains 5 bad true?

let containsBad = contains 5 bad
false

At 3 the search goes right, but 5 is on the left.

Preserving the invariant

A function f preserves an invariant if, for every x that satisfies the invariant, the result f x also satisfies the invariant.

In other words, f preserves the search tree invariant when, for every t that is a search tree, the result f t is also a search tree.

This means that if t is not a search tree, then we do not require anything from f t ("garbage in, garbage out").

If f and g preserve the invariant and t is a search tree, then any sequence of f and g applications (e.g., f (g (g (f t)))) results in a search tree.

Task: implement a Set (Lecture 5) library that internally uses an 'a BinTree to represent the collection of elements as a search tree.

What should the library expose to its users and what should be kept hidden?

In-order traversal

let rec inOrder t =
  match t with
  | Empty -> []
  | Node (l, x, r) ->
      inOrder l @ [x] @ inOrder r

The left subtree, then the node, then the right subtree.

What is inOrder t?

let inOrderT = inOrder t
[2; 7; 9; 13; 21; 25]

For a search tree, the result is sorted. "Garbage in, garbage out": inserting into bad gives a tree whose in-order list is not sorted.

let inOrderBad4 = inOrder (insert 4 bad)
[5; 3; 4]

Removing the minimum

// Deliberate warning FS0025: Empty is not covered.
let rec removeMin t =
  match t with
  | Node (Empty, x, r) -> (x, r)
  | Node (l, x, r) ->
      let m, l' = removeMin l
      (m, Node (l', x, r))

F# warns that the match is incomplete, since Empty is not covered:

warning FS0025: Incomplete pattern matches on this expression. For example,
the value 'Empty' may indicate a case not covered by the pattern(s).

removeMin Empty fails at run time:

> removeMin (Empty: int BinTree);;
Microsoft.FSharp.Core.MatchFailureException: The match cases were incomplete

The second version is total; the caller must handle None:

let rec tryRemoveMin t =
  match t with
  | Empty -> None
  | Node (Empty, x, r) -> Some (x, r)
  | Node (l, x, r) ->
      tryRemoveMin l
      |> Option.map (fun (m, l') ->
           (m, Node (l', x, r)))

let minOfEmpty = tryRemoveMin (Empty: int BinTree)
None

Which one would you use? Can we guarantee that t is not Empty?

Removal

let rec remove x t =
  match t with
  | Empty -> Empty
  | Node (l, y, r) ->
      match compare x y with
      | c when c < 0 -> Node (remove x l, y, r)
      | c when c > 0 -> Node (l, y, remove x r)
      | _ -> match tryRemoveMin r with
             | None -> l
             | Some (m, r') -> Node (l, m, r')

If the value is at a node, we put the smallest value of its right subtree in its place. With tryRemoveMin, an empty right subtree is just the None case.

What is inOrder (remove 9 t)?

let removed9 = inOrder (remove 9 t)
[2; 7; 13; 21; 25]
13 7 2 21 25

Mapping over a tree

let rec map f t =
  match t with
  | Empty -> Empty
  | Node (l, x, r) ->
      Node (map f l, f x, map f r)

let neg = map (fun x -> -x) t
-9 -7 -2 -21 -13 -25

map always preserves the shape of the tree but not necessarily the search tree invariant.

Is contains (-21) neg true?

let containsNeg = contains (-21) neg
false

The search for -21 goes left at -9, but -21 is on the right.

map sometimes preserves the invariant

A function f is monotone wrt a relation \(R\) if \(x \mathrel{R} y\) implies \((f\,x) \mathrel{R} (f\,y)\).

If t is a search tree and f is monotone wrt <, then map f t is a search tree.

Why do we require f to be monotone wrt <?

map f always preserves the shape of the tree.

map f where f is monotone (wrt <) also preserves the search-tree invariant.

A monotone function: doubling

map (fun x -> 2 * x) t is drawn below. Is the result a search tree?

let doubledTree = map (fun x -> 2 * x) t
let doubled = inOrder doubledTree
18 14 4 42 26 50

Yes: fun x -> 2 * x is monotone with respect to <. Thus, if, for some values x and y in t, we have x < y, then we have f x < f y in the result.

In order:

[4; 14; 18; 26; 42; 50]

Why the relation is <

map (fun x -> x / 10) t is drawn below. fun x -> x / 10 is monotone with respect to <=, but not <. Is the result a search tree?

let tenthsTree = map (fun x -> x / 10) t
let tenths = inOrder tenthsTree
0 0 0 2 1 2

No: the ordering is nondecreasing and allows duplicates.

In order:

[0; 0; 0; 1; 2; 2]

The invariant is strict, so f must be monotone with respect to <.

The condition is sufficient, not necessary: f only needs to be monotone on the values in the tree. fun x -> x * x is not monotone with respect to < on all integers, but it is on the positive values of t:

let squares = inOrder (map (fun x -> x * x) t)
[4; 49; 81; 169; 441; 625]

Folding a tree

let rec foldBack f t e =
  match t with
  | Empty -> e
  | Node (l, x, r) ->
      foldBack f l (f x (foldBack f r e))

Specification: foldBack f t e should compute the same result as:

List.foldBack f (inOrder t) e

without building the intermediate list inOrder t.

What is foldBack (-) t 0?

let foldT = foldBack (-) t 0
-13

The specification gives the same:

let foldList = List.foldBack (-) (inOrder t) 0
-13

as patterns

Inserting 3, 2 and 1 results in a chain; a rotation rebalances it:

let chain = Empty |> insert 3 |> insert 2 |> insert 1

let rotateRight t =
  match t with
  | Node (Node ((Node _ as ll), y, c), z, d) ->
      Node (ll, y, Node (c, z, d))
  | _ -> t

let rotated = rotateRight chain
3 2 1
2 1 3

(Node _ as ll) checks that the grandchild is a Node and names it ll: reused, not rebuilt.

Recommendation: put an as pattern in parentheses.

The goal: look deeper into a value, and keep the part you inspected as it is.

The mirror image rotates a chain leaning right. Here the parentheses are needed: inside a tuple, as names everything to its left.

let rightChain = Empty |> insert 1 |> insert 2 |> insert 3

let rotateLeft t =
  match t with
  | Node (a, x, Node (b, y, (Node _ as rr))) ->
      Node (Node (a, x, b), y, rr)
  | _ -> t

let rotatedLeft = rotateLeft rightChain = Node (leaf 1, 2, leaf 3)
true

Without the parentheses, rr would name the whole tuple (b, y, Node _):

| Node (a, x, Node (b, y, Node _ as rr)) ->
error FS0727: This union case expects 3 arguments in tupled form, but
was given 1. The missing field arguments may be any of:
    'a
    'a BinTree

Expression trees

The type of expressions

type Expr =
  | Const of float
  | X
  | Add of Expr * Expr
  | Sub of Expr * Expr
  | Mul of Expr * Expr
  | Div of Expr * Expr

let ex = Mul (X, Add (Const 2.0, X))
Mul X Add Const 2.0 X

Six constructors; as functions:

Const : float -> Expr
X : Expr
Add : Expr * Expr -> Expr

and Sub, Mul and Div like Add.

ex is a representation of \(x \cdot (2 + x)\).

Why does Expr have no constructor like Empty in 'a BinTree?

Evaluation

let rec eval e x =
  match e with
  | Const c -> c
  | X -> x
  | Add (e1, e2) -> eval e1 x + eval e2 x
  | Sub (e1, e2) -> eval e1 x - eval e2 x
  | Mul (e1, e2) -> eval e1 x * eval e2 x
  | Div (e1, e2) -> eval e1 x / eval e2 x

Given a value for X, every expression denotes a number.

For e : Expr and x : float, evaluating e at x, eval e x, is the value of e with x for X.

What is eval ex 4.0?

let evalEx = eval ex 4.0
24.0

Symbolic differentiation

let rec deriv e =
  match e with
  | Const _ -> Const 0.0
  | X -> Const 1.0
  | Add (e1, e2) -> Add (deriv e1, deriv e2)
  | Sub (e1, e2) -> Sub (deriv e1, deriv e2)
  | Mul (e1, e2) ->
      Add (Mul (deriv e1, e2),
           Mul (e1, deriv e2))
  | Div (e1, e2) ->
      Div (Sub (Mul (deriv e1, e2),
                Mul (e1, deriv e2)),
           Mul (e2, e2))

With respect to X. Structural recursion.

What is deriv ex?

let derivEx = deriv ex
Add (Mul (Const 1.0, Add (Const 2.0, X)), Mul (X, Add (Const 0.0, Const 1.0)))

The derivative as a tree

Add Mul Const 1.0 Add Const 2.0 X Mul X Add Const 0.0 Const 1.0

The result could be simplified (for example, \(0 + y = y\), \(1 \cdot y = y\)).

What is eval (deriv ex) 4.0?

let slopeEx = eval (deriv ex) 4.0
10.0

Mutual recursion: file systems

A file system

A file system is a list of entries. An entry is a file, or a directory: a name and a file system.

Mutually recursive types

type FileSys = Entry list
and Entry =
  | File of string
  | Dir of string * FileSys

let d1 =
  Dir ("d1",
       [ File "a1"
         Dir ("d2",
              [ File "a2"
                Dir ("d3", [ File "a3" ]) ])
         File "a4"
         Dir ("d3", [ File "a5" ]) ])
d1 a1 d2 a2 d3 a3 a4 d3 a5

Each declaration mentions the other, so they are declared together, with and. Without the name FileSys, Dir could take an Entry list.

What is the type of d1?

val d1: Entry =
  Dir
    ("d1",
     [File "a1"; Dir ("d2", [File "a2"; Dir ("d3", [File "a3"])]); File "a4";
      Dir ("d3", [File "a5"])])

Mutually recursive functions

let rec namesFileSys fs =
  match fs with
  | [] -> []
  | e :: es -> namesEntry e @ namesFileSys es
and namesEntry e =
  match e with
  | File s -> [s]
  | Dir (s, fs) -> s :: namesFileSys fs

Functions, too, are declared together with and.

What is namesEntry d1?

let namesD1 = namesEntry d1
["d1"; "a1"; "d2"; "a2"; "d3"; "a3"; "a4"; "d3"; "a5"]

What is the pattern used in namesFileSys?

Summary

Recursive discriminated unions are trees.

The type definition determines the shape of the trees we can represent.

Today we saw binary trees, expression trees (four different binary nodes), and "file systems" where a node can have any number of children.

Types in F# are not expressive enough to enforce all invariants (search tree).

Homework and reading

Homework 4 is out this week. Reading: Hansen & Rischel, Functional Programming Using F#, chapter "Finite trees".

Exercises

Exercise 1: checking the search-tree condition

inBounds lb t ub checks that every value in t lies strictly between lb and ub, and that t is a search tree. Going into the left subtree, the value at the node becomes the new upper bound; going into the right subtree, it becomes the new lower bound. Every node is visited at most once.

let rec inBounds (lb: 'a) (t: 'a BinTree) (ub: 'a) : bool =
  match t with
  | Empty -> true
  | Node (l, x, r) ->
    lb < x && x < ub
    && inBounds lb l x
    && inBounds x r ub

For int trees, the smallest and the largest int serve as the initial bounds:

let isBst t =
  inBounds System.Int32.MinValue t System.Int32.MaxValue

let isBstT = isBst t
let isBstBad = isBst bad
let isBstDuplicate = isBst (Node (Empty, 2, leaf 2))
true
false
false

isBst has two problems.

A tree with a single node is always a search tree, but a tree holding System.Int32.MinValue is rejected: no int is smaller than it.

let isBstMin = isBst (leaf System.Int32.MinValue)
false

isBst works only for int trees. For another element type we would need its smallest and largest values, and a type such as string has no largest value:

> isBst (leaf "a");;
error FS0001: This expression was expected to have type
    'int'
but here has type
    'string'

(a) Define a function

bounds : 'a option -> 'a BinTree -> 'a option -> bool

that checks the same condition as inBounds, but where a bound may be missing: None as the lower bound means that there is no lower bound, and None as the upper bound means that there is no upper bound.

(b) Define isBst' : 'a BinTree -> bool using bounds, without giving any concrete initial bound. For example:

Exercise 2: positions

The function tryItem returns the element at a given index of a list. It recurses on the index as on a natural number, and it corresponds to List.tryItem.

let rec tryItem (n: int) (xs: 'a list) : 'a option =
  match n, xs with
  // a negative index is not a position in any list
  | n, _ when n < 0 -> None
  // the index is a position, but this list is too short
  | _, [] -> None
  | 0, x :: _ -> Some x
  | n, _ :: xs' -> tryItem (n - 1) xs'

let item2 = tryItem 2 [10; 20; 30]
let item5 = tryItem 5 [10; 20; 30]
let itemMinus1 = tryItem (-1) [10; 20; 30]
Some 30
None
None

tryItem and List.tryItem agree on every index from -2 to 5:

let tryItemAgrees =
  [-2 .. 5]
  |> List.forall (fun n ->
    tryItem n [10; 20; 30] = List.tryItem n [10; 20; 30])
true

A position in a tree describes how to get from the root to a node.

(a) Define a type for positions in a binary tree ('a BinTree).

(b) Define a function

tryLookup : <your position type> -> 'a BinTree -> 'a option

tryLookup p t is Some x if the position p leads to a node of t holding x, and None if it does not. For the search tree t:

9 7 2 21 13 25

(c) A list is a tree that leans to the right (lecture 4). Define a type for positions in a list in which the -1 case of tryItem cannot be represented: every value of your type must be a position that some list has. Then define

tryLookupList : <your position type> -> 'a list -> 'a option

with the same results as tryItem for every position that exists.

(d) Define a function

positions : 'a BinTree -> <your position type> list

that returns every position p for which tryLookup p t is not None, without duplicates and in any order. If your position type is P, the result has type P list. To check your answer:

Exercise 3: describing positions in a list

A value of the following type describes positions in a list:

type Sel =
  | Here
  | Next
  | Seq of Sel * Sel
  | Star of Sel

A selector is applied to a list and selects some of its elements:

Selector

Applied to ['a' .. 'e']

Here

['a']

Next

['b']

Seq (Here, Next)

['b']

Seq (Next, Next)

['c']

Star Next

['a'; 'b'; 'c'; 'd'; 'e']

Star (Seq (Next, Next))

['a'; 'c'; 'e']

Seq (Next, Star (Seq (Next, Next)))

['b'; 'd']

Seq (Star Next, Star Next)

['a'; 'b'; 'c'; 'd'; 'e']

You may assume that in every Star a, the selector a never selects the head of the list it is applied to: each iteration moves at least one step. If a selector breaks this, your functions may return anything or never return ("garbage in, garbage out").

Both functions below have the type Sel -> 'a list -> 'a list. They return the selected elements in list order, each selected element once, even when it can be reached in several ways. Equal values at different places are different elements:

Star Next applied to [1; 1; 1] gives [1; 1; 1].

(a) Define selectByDesc by recursion on the selector: one case for each constructor.

(b) Define selectByList by recursion on the list. For a list x :: xs, answer two questions:

Things to watch:

(c) Run both functions on Seq (Star Next, Star Next) and a long list. How often does each function visit an element? How large does the representation of the futures in selectByList get? When would you prefer one recursion over the other? How would you improve the two functions, for example by keeping the representation of the possible futures more compact?

namespace Itt8060
module Diagrams from Itt8060
val scaled: factor: float -> Svg -> Svg
val factor: float
Multiple items
val float: value: 'T -> float (requires member op_Explicit)

--------------------
type float = System.Double

--------------------
type float<'Measure> = float
Multiple items
union case Svg.Svg: string -> Svg

--------------------
type Svg = | Svg of string
 A rendered SVG fragment (inline-embeddable, standalone-saveable).
val s: string
val widthAttr: System.Text.RegularExpressions.Regex
namespace System
namespace System.Text
namespace System.Text.RegularExpressions
Multiple items
type Regex = interface ISerializable new: pattern: string -> unit + 2 overloads member Count: input: string -> int + 8 overloads member EnumerateMatches: input: ReadOnlySpan<char> -> ValueMatchEnumerator + 4 overloads member EnumerateSplits: input: ReadOnlySpan<char> -> ValueSplitEnumerator + 5 overloads member GetGroupNames: unit -> string array member GetGroupNumbers: unit -> int array member GroupNameFromNumber: i: int -> string member GroupNumberFromName: name: string -> int member IsMatch: input: ReadOnlySpan<char> -> bool + 9 overloads ...
<summary>Represents an immutable regular expression.</summary>

--------------------
System.Text.RegularExpressions.Regex( pattern: string) : System.Text.RegularExpressions.Regex
System.Text.RegularExpressions.Regex( pattern: string, options: System.Text.RegularExpressions.RegexOptions) : System.Text.RegularExpressions.Regex
System.Text.RegularExpressions.Regex( pattern: string, options: System.Text.RegularExpressions.RegexOptions, matchTimeout: System.TimeSpan) : System.Text.RegularExpressions.Regex
val w: int
Multiple items
val int: value: 'T -> int (requires member op_Explicit)

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

--------------------
type int<'Measure> = int
System.Text.RegularExpressions.Regex.Match(input: string) : System.Text.RegularExpressions.Match
System.Text.RegularExpressions.Regex.Match(input: string, startat: int) : System.Text.RegularExpressions.Match
System.Text.RegularExpressions.Regex.Match(input: string, beginning: int, length: int) : System.Text.RegularExpressions.Match
System.Text.RegularExpressions.Regex.Replace(input: string, evaluator: System.Text.RegularExpressions.MatchEvaluator) : string
System.Text.RegularExpressions.Regex.Replace(input: string, replacement: string) : string
System.Text.RegularExpressions.Regex.Replace(input: string, evaluator: System.Text.RegularExpressions.MatchEvaluator, count: int) : string
System.Text.RegularExpressions.Regex.Replace(input: string, replacement: string, count: int) : string
System.Text.RegularExpressions.Regex.Replace(input: string, evaluator: System.Text.RegularExpressions.MatchEvaluator, count: int, startat: int) : string
System.Text.RegularExpressions.Regex.Replace(input: string, replacement: string, count: int, startat: int) : string
val saveScaled: path: string -> factor: float -> svg: Svg -> unit
val path: string
Multiple items
val string: value: 'T -> string

--------------------
type string = System.String
val svg: Svg
val save: path: string -> Svg -> unit
 Save a diagram for use from slide decks: `Diagrams.save "img/list567.svg" (consCells ...)`.
 Paths are relative to the lecture directory when evaluated by the build.
val evenlySpaced: Svg -> Svg
val viewBox: System.Text.RegularExpressions.Regex
val m: System.Text.RegularExpressions.Match
val h: int
property System.Text.RegularExpressions.Match.Groups: System.Text.RegularExpressions.GroupCollection with get
<summary>Gets a collection of groups matched by the regular expression.</summary>
<returns>The character groups matched by the pattern.</returns>
val named: name: string -> Svg -> Svg
val name: string
val text: string
System.String.Replace(oldValue: string, newValue: string) : string
System.String.Replace(oldChar: char, newChar: char) : string
System.String.Replace(oldValue: string, newValue: string, comparisonType: System.StringComparison) : string
System.String.Replace(oldValue: string, newValue: string, ignoreCase: bool, culture: System.Globalization.CultureInfo) : string
val figIntList: Svg
val tree: root: Tree -> Svg
 Renders a labelled tree top-down, e.g.
 `tree (Node("+", [leaf "1"; Node("*", [leaf "2"; leaf "3"])]))`.
union case Tree.Node: label: string * children: Tree list -> Tree
val leaf: label: string -> Tree
 Shorthand for a leaf.
type IntList = | INil | ICons of int * IntList
'a
type 'a BinTree = | Empty | Node of 'a BinTree * 'a * 'a BinTree
val small: int BinTree
union case BinTree.Node: 'a BinTree * 'a * 'a BinTree -> 'a BinTree
union case BinTree.Empty: 'a BinTree
val shape: t: 'a BinTree -> BinShape
val t: 'a BinTree
union case BinShape.BinEmpty: BinShape
val l: 'a BinTree
val x: 'a
val r: 'a BinTree
union case BinShape.BinNode: BinShape * string * BinShape -> BinShape
val figSmall: Svg
val binTreeShowingEmpty: emptyLabel: string -> root: BinShape -> Svg
 A binary tree with every empty child drawn as a node labelled `emptyLabel`.
val leaf: x: 'a -> 'a BinTree
val t: int BinTree
val figT: Svg
val binTree: root: BinShape -> Svg
 A binary tree with empty children left out (their positions are kept).
val size: t: 'a BinTree -> int
val height: t: 'a BinTree -> int
val max: e1: 'T -> e2: 'T -> 'T (requires comparison)
val sizeT: int
val heightT: int
val allOne: int BinTree
val insert: x: 'a -> t: 'a BinTree -> 'a BinTree (requires comparison)
val x: 'a (requires comparison)
val t: 'a BinTree (requires comparison)
val l: 'a BinTree (requires comparison)
val y: 'a (requires comparison)
val r: 'a BinTree (requires comparison)
val compare: e1: 'T -> e2: 'T -> int (requires comparison)
val c: int
val contains: x: 'a -> t: 'a BinTree -> bool (requires comparison)
val contains13: bool
val contains8: bool
val bad: int BinTree
val figBad: Svg
val containsBad: bool
val inOrder: t: 'a BinTree -> 'a list
val inOrderT: int list
val inOrderBad4: int list
val removeMin: t: 'a BinTree -> 'a * 'a BinTree
val m: 'a
val l': 'a BinTree
val tryRemoveMin: t: 'a BinTree -> ('a * 'a BinTree) option
union case Option.None: Option<'T>
union case Option.Some: Value: 'T -> Option<'T>
module Option from Microsoft.FSharp.Core
val map: mapping: ('T -> 'U) -> option: 'T option -> 'U option
val minOfEmpty: (int * int BinTree) option
val remove: x: 'a -> t: 'a BinTree -> 'a BinTree (requires comparison)
val m: 'a (requires comparison)
val r': 'a BinTree (requires comparison)
val removed9: int list
val figRemoved9: Svg
val map: f: ('a -> 'b) -> t: 'a BinTree -> 'b BinTree
val f: ('a -> 'b)
val neg: int BinTree
val x: int
val figNeg: Svg
val containsNeg: bool
val doubledTree: int BinTree
val doubled: int list
val figDoubled: Svg
val tenthsTree: int BinTree
val tenths: int list
val figTenths: Svg
val squares: int list
val foldBack: f: ('a -> 'b -> 'b) -> t: 'a BinTree -> e: 'b -> 'b
val f: ('a -> 'b -> 'b)
val e: 'b
val foldT: int
val foldList: int
Multiple items
module List from Microsoft.FSharp.Collections

--------------------
type List<'T> = | op_Nil | op_ColonColon of Head: 'T * Tail: 'T list interface IReadOnlyList<'T> interface IReadOnlyCollection<'T> interface IEnumerable interface IEnumerable<'T> member GetReverseIndex: rank: int * offset: int -> int member GetSlice: startIndex: int option * endIndex: int option -> 'T list static member Cons: head: 'T * tail: 'T list -> 'T list member Head: 'T member IsEmpty: bool member Item: index: int -> 'T with get ...
val foldBack: folder: ('T -> 'State -> 'State) -> list: 'T list -> state: 'State -> 'State
val chain: int BinTree
val rotateRight: t: 'a BinTree -> 'a BinTree
val ll: 'a BinTree
val y: 'a
val c: 'a BinTree
val z: 'a
val d: 'a BinTree
val rotated: int BinTree
val figChain: Svg
val figRotated: Svg
val rightChain: int BinTree
val rotateLeft: t: 'a BinTree -> 'a BinTree
val a: 'a BinTree
val b: 'a BinTree
val rr: 'a BinTree
val rotatedLeft: bool
type Expr = | Const of float | X | Add of Expr * Expr | Sub of Expr * Expr | Mul of Expr * Expr | Div of Expr * Expr
union case Expr.X: Expr
val ex: Expr
union case Expr.Mul: Expr * Expr -> Expr
union case Expr.Add: Expr * Expr -> Expr
union case Expr.Const: float -> Expr
val exprShape: e: Expr -> Tree
val e: Expr
val c: float
type Tree = | Node of label: string * children: Tree list
 A labelled tree; leaves are nodes with no children.
val sprintf: format: Printf.StringFormat<'T> -> 'T
val a: Expr
val b: Expr
union case Expr.Sub: Expr * Expr -> Expr
union case Expr.Div: Expr * Expr -> Expr
val figEx: Svg
val eval: e: Expr -> x: float -> float
val x: float
val e1: Expr
val e2: Expr
val evalEx: float
val deriv: e: Expr -> Expr
val derivEx: Expr
val figDeriv: Svg
val slopeEx: float
type FileSys = Entry list
type Entry = | File of string | Dir of string * FileSys
type 'T list = List<'T>
val d1: Entry
union case Entry.Dir: string * FileSys -> Entry
union case Entry.File: string -> Entry
val entryShape: e: Entry -> Tree
val e: Entry
val fs: FileSys
val map: mapping: ('T -> 'U) -> list: 'T list -> 'U list
val figD1: Svg
val namesFileSys: fs: Entry list -> string list
val fs: Entry list
val es: Entry list
val namesEntry: e: Entry -> string list
val namesD1: string list
val inBounds: lb: 'a -> t: 'a BinTree -> ub: 'a -> bool (requires comparison)
val lb: 'a (requires comparison)
val ub: 'a (requires comparison)
type bool = System.Boolean
val isBst: t: int BinTree -> bool
type Int32 = member CompareTo: value: int -> int + 1 overload member Equals: obj: int -> bool + 1 overload member GetHashCode: unit -> int member GetTypeCode: unit -> TypeCode member ToString: unit -> string + 3 overloads member TryFormat: utf8Destination: Span<byte> * bytesWritten: byref<int> * ?format: ReadOnlySpan<char> * ?provider: IFormatProvider -> bool + 1 overload static member Abs: value: int -> int static member BigMul: left: int * right: int -> int64 static member Clamp: value: int * min: int * max: int -> int static member CopySign: value: int * sign: int -> int ...
<summary>Represents a 32-bit signed integer.</summary>
field int.MinValue: int = -2147483648
field int.MaxValue: int = 2147483647
val isBstT: bool
val isBstBad: bool
val isBstDuplicate: bool
val isBstMin: bool
val refCheck: what: string -> ok: bool -> unit
val what: string
val ok: bool
val failwith: message: string -> 'T
val refBounds: lb: 'a option -> t: 'a BinTree -> ub: 'a option -> bool (requires comparison)
val lb: 'a option (requires comparison)
type 'T option = Option<'T>
val ub: 'a option (requires comparison)
val forall: predicate: ('T -> bool) -> option: 'T option -> bool
val b: 'a (requires comparison)
val refIsBst: t: 'a BinTree -> bool (requires comparison)
val tryItem: n: int -> xs: 'a list -> 'a option
val n: int
val xs: 'a list
val xs': 'a list
val item2: int option
val item5: int option
val itemMinus1: int option
val tryItemAgrees: bool
val forall: predicate: ('T -> bool) -> list: 'T list -> bool
val tryItem: index: int -> list: 'T list -> 'T option
val refTryLookup: p: bool list -> t: 'a BinTree -> 'a option
val p: bool list
val p': bool list
type Sel = | Here | Next | Seq of Sel * Sel | Star of Sel
module Seq from Microsoft.FSharp.Collections
val refSelectByDesc: d: Sel -> xs: 'a list -> 'a list
val d: Sel
val reach: d: Sel -> ys: 'a list -> 'a list list
val ys: 'a list
union case Sel.Here: Sel
union case Sel.Next: Sel
val rest: 'a list
Multiple items
union case Sel.Seq: Sel * Sel -> Sel

--------------------
module Seq from Microsoft.FSharp.Collections
val a: Sel
val b: Sel
val collect: mapping: ('T -> 'U list) -> list: 'T list -> 'U list
union case Sel.Star: Sel -> Sel
val filter: predicate: ('T -> bool) -> list: 'T list -> 'T list
val isEmpty: list: 'T list -> bool
val length: list: 'T list -> int
val distinct: list: 'T list -> 'T list (requires equality)
val sort: list: 'T list -> 'T list (requires comparison)
val i: int
val item: index: int -> list: 'T list -> 'T
val refAtHere: d: Sel -> bool
val refStep: d: Sel -> Sel list
val a': Sel
val refSelectByList: d: Sel -> xs: 'a list -> 'a list
val go: futures: Sel list -> ys: 'b list -> 'b list
val futures: Sel list
val ys: 'b list
val y: 'b
val rest: 'b list
val exists: predicate: ('T -> bool) -> list: 'T list -> bool
val refTable: (Sel * string) list
val expected: string
val want: char list
val ofSeq: source: 'T seq -> 'T list