This type is an implementation of the Free Monad which is generic to any Functor.
The Free Monad is used typically to describe a pure program at high level and separately write different interpreters for it.
- Coproduct: A Functor used in conjunction with the Free Monad to combine different instruction sets.
Free monad-interpreter in F# from Mark Seemann's blog but encoded with Free.
open System
open FSharpPlus
open FSharpPlus.Data
type CommandLineInstruction<'t> =
| ReadLine of (string -> 't)
| WriteLine of string * 't
with static member Map (x, f) =
match x with
| ReadLine g -> ReadLine (f << g)
| WriteLine (s, g) -> WriteLine (s, f g)
let readLine = Free.liftF (ReadLine id)
let writeLine s = Free.liftF (WriteLine (s, ()))
let rec interpretCommandLine = Free.run >> function
| Pure x -> x
| Roll (ReadLine next) -> Console.ReadLine () |> next |> interpretCommandLine
| Roll (WriteLine (s, next)) ->
Console.WriteLine s
next |> interpretCommandLine
let rec readQuantity = monad {
do! writeLine "Please enter number of diners:"
let! l = readLine
match tryParse l with
| Some dinerCount -> return dinerCount
| None ->
do! writeLine "Not an integer."
return! readQuantity }
let rec readDate = monad {
do! writeLine "Please enter your desired date:"
let! l = readLine
match DateTimeOffset.TryParse l with
| true, dt -> return dt
| _ ->
do! writeLine "Not a date."
return! readDate }
let readName = monad {
do! writeLine "Please enter your name:"
return! readLine }
let readEmail = monad {
do! writeLine "Please enter your email address:"
return! readLine }
type Reservation = {
Date : DateTimeOffset
Name : string
Email : string
Quantity : int }
with static member Create (Quantity, Date, Name, Email) = { Date = Date; Name = Name; Email = Email; Quantity = Quantity }
let readReservationRequest =
curryN Reservation.Create
<!> readQuantity
<*> readDate
<*> readName
<*> readEmail
let mainFunc () =
readReservationRequest
>>= (writeLine << (sprintf "%A"))
|> interpretCommandLine
0
-
Highly recommended Matt Thornton's blog Grokking Free Monads and Interpreting Free Monads.
It contains examples using F#+ and an explanation from scratch.
-
Mark Seemann's blog has an article series which ends
up describing Free Monads although he doesn't use F#+ and therefore either repeats boilerplate code or switches to Haskell.
Anyways some code from those series (like the above fragment) can be found in our test suite for Free simplified using Free and Coproduct types.
Scott Wlaschin's 13 ways of looking at a turtle is also a series which ends up defining a Free Monad, without using F#+ but with boilerplate code instead.
namespace System
namespace FSharpPlus
namespace FSharpPlus.Data
't
union case CommandLineInstruction.ReadLine: (string -> 't) -> CommandLineInstruction<'t>
Multiple items
val string: value: 'T -> string
--------------------
type string = String
union case CommandLineInstruction.WriteLine: string * 't -> CommandLineInstruction<'t>
Multiple items
module Map
from FSharpPlus
<summary>
Additional operations on Map<'Key, 'Value>
</summary>
--------------------
module Map
from Microsoft.FSharp.Collections
--------------------
type Map<'Key,'Value (requires comparison)> =
interface IReadOnlyDictionary<'Key,'Value>
interface IReadOnlyCollection<KeyValuePair<'Key,'Value>>
interface IEnumerable
interface IStructuralEquatable
interface IComparable
interface IEnumerable<KeyValuePair<'Key,'Value>>
interface ICollection<KeyValuePair<'Key,'Value>>
interface IDictionary<'Key,'Value>
new: elements: ('Key * 'Value) seq -> Map<'Key,'Value>
member Add: key: 'Key * value: 'Value -> Map<'Key,'Value>
...
--------------------
new: elements: ('Key * 'Value) seq -> Map<'Key,'Value>
val x: CommandLineInstruction<'a>
val f: ('a -> 'b)
val g: (string -> 'a)
val s: string
val g: 'a
val readLine: Free<CommandLineInstruction<string>,string>
Multiple items
module Free
from FSharpPlus.Data
<summary>
Basic operations on Free Monads
</summary>
--------------------
type Free<'functor<'t>,'t> =
| Pure of 't
| Roll of obj
static member (<*>) : f: Free<'Functor<'T->'U>,('T -> 'U)> * x: Free<'Functor<'T>,'T> -> Free<'Functor<'U>,'U> (requires member Map and member Map and member Map and member Map and member Map)
static member (>>=) : x: Free<'Functor<'T>,'T> * f: ('T -> Free<'Functor<'U>,'U>) -> Free<'Functor<'U>,'U> (requires member Map and member Map and member Map)
static member Delay: x: (unit -> Free<'Functor<'T>,'T>) -> Free<'Functor<'T>,'T>
static member Return: x: 'a2 -> Free<'a3,'a2>
val liftF: x: 'Functor<'T> -> Free<'Functor<'T>,'T> (requires member Map and member Map)
<summary>
Lift any Functor into a Free structure.
</summary>
val id: x: 'T -> 'T
val writeLine: s: string -> Free<CommandLineInstruction<unit>,unit>
val interpretCommandLine: (Free<CommandLineInstruction<unit>,unit> -> unit)
val run: f: Free<'Functor<'T>,'T> -> Choice<'T,'Functor<Free<'Functor<'T>,'T>>> (requires member Map)
Multiple items
union case Free.Pure: 't -> Free<'functor<'t>,'t>
--------------------
active recognizer Pure: Choice<'a,'b> -> Choice<'a,'b>
val x: unit
Multiple items
val Roll: f: 'Functor<Free<'Functor<'T>,'T>> -> Free<'Functor<'T>,'T> (requires member Map)
--------------------
active recognizer Roll: Choice<'a,'b> -> Choice<'a,'b>
val next: (string -> Free<CommandLineInstruction<unit>,unit>)
type Console =
static member Beep: unit -> unit + 1 overload
static member Clear: unit -> unit
static member GetCursorPosition: unit -> struct (int * int)
static member MoveBufferArea: sourceLeft: int * sourceTop: int * sourceWidth: int * sourceHeight: int * targetLeft: int * targetTop: int -> unit + 1 overload
static member OpenStandardError: unit -> Stream + 1 overload
static member OpenStandardInput: unit -> Stream + 1 overload
static member OpenStandardOutput: unit -> Stream + 1 overload
static member Read: unit -> int
static member ReadKey: unit -> ConsoleKeyInfo + 1 overload
static member ReadLine: unit -> string
...
<summary>Represents the standard input, output, and error streams for console applications. This class cannot be inherited.</summary>
Console.ReadLine() : string
val next: Free<CommandLineInstruction<unit>,unit>
Console.WriteLine() : unit
(+0 other overloads)
Console.WriteLine(value: uint64) : unit
(+0 other overloads)
Console.WriteLine(value: uint32) : unit
(+0 other overloads)
Console.WriteLine(value: string) : unit
(+0 other overloads)
Console.WriteLine(value: float32) : unit
(+0 other overloads)
Console.WriteLine(value: obj) : unit
(+0 other overloads)
Console.WriteLine(value: int64) : unit
(+0 other overloads)
Console.WriteLine(value: int) : unit
(+0 other overloads)
Console.WriteLine(value: float) : unit
(+0 other overloads)
Console.WriteLine(value: decimal) : unit
(+0 other overloads)
val readQuantity: Free<CommandLineInstruction<int>,int>
val monad<'monad<'t>> : MonadFxBuilder<'monad<'t>>
<summary>
Creates a (lazy) monadic computation expression with side-effects (see http://fsprojects.github.io/FSharpPlus/computation-expressions.html for more information)
</summary>
val l: string
val tryParse: value: string -> 'T option (requires member TryParse)
<summary>
Converts to a value from its string representation. Returns None if the convertion doesn't succeed.
</summary>
<category index="21">Converter</category>
union case Option.Some: Value: 'T -> Option<'T>
val dinerCount: int
union case Option.None: Option<'T>
val readDate: Free<CommandLineInstruction<DateTimeOffset>,DateTimeOffset>
Multiple items
[<Struct>]
type DateTimeOffset =
new: dateTime: DateTime -> unit + 5 overloads
member Add: timeSpan: TimeSpan -> DateTimeOffset
member AddDays: days: float -> DateTimeOffset
member AddHours: hours: float -> DateTimeOffset
member AddMilliseconds: milliseconds: float -> DateTimeOffset
member AddMinutes: minutes: float -> DateTimeOffset
member AddMonths: months: int -> DateTimeOffset
member AddSeconds: seconds: float -> DateTimeOffset
member AddTicks: ticks: int64 -> DateTimeOffset
member AddYears: years: int -> DateTimeOffset
...
<summary>Represents a point in time, typically expressed as a date and time of day, relative to Coordinated Universal Time (UTC).</summary>
--------------------
DateTimeOffset ()
DateTimeOffset(dateTime: DateTime) : DateTimeOffset
DateTimeOffset(dateTime: DateTime, offset: TimeSpan) : DateTimeOffset
DateTimeOffset(ticks: int64, offset: TimeSpan) : DateTimeOffset
DateTimeOffset(year: int, month: int, day: int, hour: int, minute: int, second: int, offset: TimeSpan) : DateTimeOffset
DateTimeOffset(year: int, month: int, day: int, hour: int, minute: int, second: int, millisecond: int, offset: TimeSpan) : DateTimeOffset
DateTimeOffset(year: int, month: int, day: int, hour: int, minute: int, second: int, millisecond: int, calendar: Globalization.Calendar, offset: TimeSpan) : DateTimeOffset
DateTimeOffset.TryParse(input: string, result: byref<DateTimeOffset>) : bool
DateTimeOffset.TryParse(input: ReadOnlySpan<char>, result: byref<DateTimeOffset>) : bool
DateTimeOffset.TryParse(input: string, formatProvider: IFormatProvider, styles: Globalization.DateTimeStyles, result: byref<DateTimeOffset>) : bool
DateTimeOffset.TryParse(input: ReadOnlySpan<char>, formatProvider: IFormatProvider, styles: Globalization.DateTimeStyles, result: byref<DateTimeOffset>) : bool
val dt: DateTimeOffset
val readName: Free<CommandLineInstruction<string>,string>
val readEmail: Free<CommandLineInstruction<string>,string>
Reservation.Date: DateTimeOffset
Reservation.Name: string
Reservation.Email: string
Reservation.Quantity: int
Multiple items
val int: value: 'T -> int (requires member op_Explicit)
--------------------
type int = int32
--------------------
type int<'Measure> =
int
val Quantity: int
val Date: DateTimeOffset
val Name: string
val Email: string
val readReservationRequest: Free<CommandLineInstruction<Reservation>,Reservation>
val curryN: f: ('T1 * ^T2 * ... * ^Tn -> 'Result) -> t: 'T1 -> 'T2 -> ... -> 'Tn -> 'Result (requires member Curry)
<summary>
Takes a function expecting a tuple of any N number of elements and returns a function expecting N curried arguments.
</summary>
<category index="0">Common Combinators</category>
type Reservation =
{
Date: DateTimeOffset
Name: string
Email: string
Quantity: int
}
static member Create: Quantity: int * Date: DateTimeOffset * Name: string * Email: string -> Reservation
static member Reservation.Create: Quantity: int * Date: DateTimeOffset * Name: string * Email: string -> Reservation
val mainFunc: unit -> int
val sprintf: format: Printf.StringFormat<'T> -> 'T