Header menu logo Teaching

BinderScriptNotebook

This practice quiz emphasizes Discriminated Unions. They are useful for times when the data that you're representing has multiple mutually exclusive cases.

Here is some good background reading for before you do these quesitions, particularly the F# for fun and profit link.

Question 1

Create a discriminated union named Action with two cases: Buy and Sell.

  1. Create a value named 'bAction' and assign Buy to it.
  2. Create a value named 'sAction' and assign Sell to it.

answer

type Action = 
    | Buy 
    | Sell
let bAction = Buy
let sAction = Sell
type Action =
  | Buy
  | Sell
val bAction: Action = Buy
val sAction: Action = Sell

Question 2

Create a single case discriminated union to represent a particular kind of string:

  1. Create a discriminated union named Ticker with a single case Ticker of string.
  2. Then wrap the string "ABC" in your Ticker type and assign it to a value named 'aTicker'.
  3. Then use pattern matching to unwrap the string in aTicker and assign it to a value named aTickerString.

Discriminated unions like this are usful if you want to make sure that you don't accidentally mix up two strings that represent different things. A function that takes an input with type Ticker will not accept any string, it will only accept inputs that have time Ticker.

answer

type Ticker = Ticker of string
let aTicker = Ticker "ABC"
let (Ticker aTickerString) = aTicker
type Ticker = | Ticker of string
val aTicker: Ticker = Ticker "ABC"
val aTickerString: string = "ABC"

Question 3

Create a single case discriminated union to represent a particular kind of float:

  1. Create a discriminated union named Signal with a single case Signal of float.
  2. Then wrap the string float 1.0 in your Signal type and assign it to a value named 'aSignal'.
  3. Then use pattern matching to unwrap the float in aSignal and assign it to a value named aSignalFloat.

answer

type Signal = Signal of float
let aSignal = Signal 1.2
let (Signal aSignalFloat) = aSignal
type Signal = | Signal of float
val aSignal: Signal = Signal 1.2
val aSignalFloat: float = 1.2

Question 4

Create a discriminated union called called Funds with two cases: MutualFund of string and HedgeFund of string.

  1. Create a MutualFund case of the Fund union with the string "Fidelity Magellan". Assign it to a value named "magellan".
  2. Create a HedgeFund case of the Fund union with the string "Renaissance Medallion". Assign it to a value named "renaissance".

answer

type Funds =
    | MutualFund of string
    | HedgeFund of string

let magellan = MutualFund "Fidelity Magellan"
let renaissance = HedgeFund "Renaissance Medallion"
type Funds =
  | MutualFund of string
  | HedgeFund of string
val magellan: Funds = MutualFund "Fidelity Magellan"
val renaissance: Funds = HedgeFund "Renaissance Medallion"

Question 5

Define two types with the same cases.

type Ambiguous1 = Up | Down
type Ambiguous2 = Up | Down

If you try to assign Ambiguous1 to values, it can be hard for the compiler (and yourself) to figure out which of these types you mean. If you write Up the compiler will think that you meant to use whatever was defined last (Ambigous2).

Use fully qualified names to show how to assign the Up case from Ambiguous1 to a value named ambiguous1 and the Up case from Ambiguous2 to a value named ambiguous2.

answer

type Ambiguous1 = Up | Down
type Ambiguous2 = Up | Down
let ambiguous1 = Ambiguous1.Up
let ambiguous2 = Ambiguous2.Up
type Ambiguous1 =
  | Up
  | Down
type Ambiguous2 =
  | Up
  | Down
val ambiguous1: Ambiguous1 = Up
val ambiguous2: Ambiguous2 = Up

Question 6

Imagine that analyst recommendations have the form

type AnalystRec = Buy | Sell | Hold

You have recommendations from two analysts

let goldmanRec = Buy
let barclaysRec = Sell

You want to act on goldman recommendations as follows:

let actionOnGoldman (x: AnalystRec) =
    match x with
    | Buy | Hold -> "I am buying this!"
    | Sell -> "I am selling this!"

The problem is that this actionOnGoldman function will work for both goldmanRec and barclaysRec.

actionOnGoldman goldmanRec // evaluates to "I am buying this!"
actionOnGoldman barclaysRec // evaluates to "I am selling this!"
  1. Create a single case union called GoldmanRec where the single case is GoldmanRec of AnalystRec.
  2. Create a modified actionOnGoldman function called actionOnGoldmanOnly so that it will only work on recommendations with the type GoldmanRec.

If wrappedGoldmanRec is buy GoldmanRec, the result should be

actionOnGoldmanOnly wrappedGoldmanRec // evaluates to "I am buying this!"
actionOnGoldmanOnly barclaysRec // compiler error.

answer

type AnalystRec = Buy | Sell | Hold
type GoldmanRec = GoldmanRec of AnalystRec
let goldmanRec = Buy
let barclaysRec = Sell
let actionOnGoldman (x: AnalystRec) =
    match x with
    | Buy | Hold -> "I am buying this!"
    | Sell -> "I am selling this!"
actionOnGoldman goldmanRec // what we want.
actionOnGoldman barclaysRec // oops.
// constructing it from scratch.
let wrappedGoldmanRec = GoldmanRec Buy
// or, wrapping our previously created value
let wrappedGoldmanRec2 = GoldmanRec goldmanRec
wrappedGoldmanRec = wrappedGoldmanRec2 // true
// constructing it from scratch
let actionOnGoldmanOnly (x: GoldmanRec) =
    match x with
    | GoldmanRec Buy | GoldmanRec Hold -> "I am buying this!"
    | GoldmanRec Sell -> "I am selling this!"
// or, unwrapping the GoldmanRec with pattern matching
// in the input definition:
let actionOnGoldmanOnly2 (GoldmanRec x) =
    // Since we unwrapped the goldman recommendation,
    // now it is just the inner analyst recommendation.
    // We can leave off the GoldmanRec that was wrapping the
    // recomendation.
    match x with
    | Buy | Hold -> "I am buying this!"
    | Sell -> "I am selling this!"
// or, since you see above that once we unwrap the goldman rec,
// it is the same as our orignal function.
let actionOnGoldmanOnly3 (GoldmanRec x) = actionOnGoldman x

// check
actionOnGoldmanOnly wrappedGoldmanRec
actionOnGoldmanOnly2 wrappedGoldmanRec
actionOnGoldmanOnly3 wrappedGoldmanRec
// These would all give compiler errors. 
// Uncomment them (delete the // at the start) to test them yourself.
//actionOnGoldmanOnly barclaysRec
//actionOnGoldmanOnly2 barclaysRec
//actionOnGoldmanOnly3 barclaysRec
type AnalystRec =
  | Buy
  | Sell
  | Hold
type GoldmanRec = | GoldmanRec of AnalystRec
val goldmanRec: AnalystRec = Buy
val barclaysRec: AnalystRec = Sell
val actionOnGoldman: x: AnalystRec -> string
val wrappedGoldmanRec: GoldmanRec = GoldmanRec Buy
val wrappedGoldmanRec2: GoldmanRec = GoldmanRec Buy
val actionOnGoldmanOnly: x: GoldmanRec -> string
val actionOnGoldmanOnly2: GoldmanRec -> string
val actionOnGoldmanOnly3: GoldmanRec -> string
val it: string = "I am buying this!"

Question 7

Imagine that stock tips have the form

type StockTip = Buy | Sell | Hold

You have recommendations from two people

let friendRec = Buy
let professorRec = Sell

You want to actions as follows:

let actionOnFriend (x: StockTip) = x
let actionOnProfessor (x: StockTip) =
    match x with
    | Buy -> StockTip.Sell
    | Hold -> StockTip.Sell
    | Sell -> StockTip.Buy
  1. Create a two case union called FriendOrFoe where the two cases are Friend of StockTip and Professor of StockTip.
  2. Create a function called actionFriendOrFoe that will properly handle tips from friends and tips from professors.

Show that friendRec and professorRec wrapped in the FriendOrFoe type are handled properly by actionFriendOrFoe.

answer

type StockTip = Buy | Sell | Hold
let friendRec = Buy
let professorRec = Sell
let actionOnFriend (x: StockTip) = x
let actionOnProfessor (x: StockTip) =
    match x with
    | Buy -> StockTip.Sell
    | Hold -> StockTip.Sell
    | Sell -> StockTip.Buy
// or, since we're doing the same thing with a professor's
// Buy or Hold recommendation, this could also be written
let actionOnProfessor2 (x: StockTip) =
    match x with
    | Buy | Hold -> StockTip.Sell
    | Sell -> StockTip.Buy
type FriendOrFoe = 
    | Friend of StockTip
    | Professor of StockTip
let wrappedFriendRec = Friend friendRec
let wrappedProfessorRec = Professor professorRec
let actionOnFriendOrFoe (x: FriendOrFoe) =
    match x with
    | Friend tip -> actionOnFriend tip
    | Professor tip -> actionOnProfessor tip 
actionOnFriendOrFoe wrappedFriendRec // evaluates to Buy
actionOnFriendOrFoe wrappedProfessorRec // evaluates to Buy
actionOnFriendOrFoe (Professor Hold) // evaluates to Sell
type StockTip =
  | Buy
  | Sell
  | Hold
val friendRec: StockTip = Buy
val professorRec: StockTip = Sell
val actionOnFriend: x: StockTip -> StockTip
val actionOnProfessor: x: StockTip -> StockTip
val actionOnProfessor2: x: StockTip -> StockTip
type FriendOrFoe =
  | Friend of StockTip
  | Professor of StockTip
val wrappedFriendRec: FriendOrFoe = Friend Buy
val wrappedProfessorRec: FriendOrFoe = Professor Sell
val actionOnFriendOrFoe: x: FriendOrFoe -> StockTip
val it: StockTip = Sell

type Action = | Buy | Sell
val bAction: Action
union case Action.Buy: Action
val sAction: Action
union case Action.Sell: Action
Multiple items
union case Ticker.Ticker: string -> Ticker

--------------------
type Ticker = | Ticker of string
type Ticker = | Ticker of string
Multiple items
val string: value: 'T -> string

--------------------
type string = System.String
val aTicker: Ticker
val aTickerString: string
Multiple items
union case Signal.Signal: float -> Signal

--------------------
type Signal = | Signal of float
type Signal = | Signal of float
Multiple items
val float: value: 'T -> float (requires member op_Explicit)

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

--------------------
type float<'Measure> = float
val aSignal: Signal
val aSignalFloat: float
type Funds = | MutualFund of string | HedgeFund of string
val magellan: Funds
union case Funds.MutualFund: string -> Funds
val renaissance: Funds
union case Funds.HedgeFund: string -> Funds
type Ambiguous1 = | Up | Down
type Ambiguous2 = | Up | Down
union case Ambiguous1.Up: Ambiguous1
union case Ambiguous1.Down: Ambiguous1
val ambiguous1: Ambiguous1
val ambiguous2: Ambiguous2
union case Ambiguous2.Up: Ambiguous2
type AnalystRec = | Buy | Sell | Hold
val goldmanRec: AnalystRec
union case AnalystRec.Buy: AnalystRec
val barclaysRec: AnalystRec
union case AnalystRec.Sell: AnalystRec
val actionOnGoldman: x: AnalystRec -> string
val x: AnalystRec
union case AnalystRec.Hold: AnalystRec
Multiple items
union case GoldmanRec.GoldmanRec: AnalystRec -> GoldmanRec

--------------------
type GoldmanRec = | GoldmanRec of AnalystRec
type GoldmanRec = | GoldmanRec of AnalystRec
val wrappedGoldmanRec: GoldmanRec
val wrappedGoldmanRec2: GoldmanRec
val actionOnGoldmanOnly: x: GoldmanRec -> string
val x: GoldmanRec
val actionOnGoldmanOnly2: GoldmanRec -> string
val actionOnGoldmanOnly3: GoldmanRec -> string
type StockTip = | Buy | Sell | Hold
val friendRec: StockTip
union case StockTip.Buy: StockTip
val professorRec: StockTip
union case StockTip.Sell: StockTip
val actionOnFriend: x: StockTip -> StockTip
val x: StockTip
val actionOnProfessor: x: StockTip -> StockTip
union case StockTip.Hold: StockTip
val actionOnProfessor2: x: StockTip -> StockTip
type FriendOrFoe = | Friend of StockTip | Professor of StockTip
val wrappedFriendRec: FriendOrFoe
union case FriendOrFoe.Friend: StockTip -> FriendOrFoe
val wrappedProfessorRec: FriendOrFoe
union case FriendOrFoe.Professor: StockTip -> FriendOrFoe
val actionOnFriendOrFoe: x: FriendOrFoe -> StockTip
val x: FriendOrFoe
val tip: StockTip

Type something to start searching.