Skip to content

Instantly share code, notes, and snippets.

@kfl
Last active January 13, 2022 11:04
Show Gist options
  • Save kfl/f3a91896845706e6fc7ff2dc77fbebe7 to your computer and use it in GitHub Desktop.
Save kfl/f3a91896845706e6fc7ff2dc77fbebe7 to your computer and use it in GitHub Desktop.
A simple monadic evaluator in F# with various monads
// Working with various monads in F#
type OptionBuilder() =
member _.Bind(a, f) = Option.bind f a
member _.Return a = Some a
member _.ReturnFrom a = a
member _.Zero () = None
member _.Combine (a, b) = Option.orElseWith b a
member _.Delay f = f
member _.Run f = f()
let optionM = OptionBuilder()
type MultiBuilder() =
member _.Bind(a, f) = Set.toSeq a |> Seq.map f |> Set.unionMany
member _.Return a = Set.singleton a
member _.ReturnFrom a = a
member _.Zero () = Set.empty
member _.Combine (a, b) = Set.union a b
member _.Delay f = f()
let multiM = MultiBuilder()
type expr =
| Const of int
| Plus of expr * expr
| Div of expr * expr
| Choose of int list
let evalOne e =
let rec yieldAll (ns : int list) =
optionM { if not (List.isEmpty ns) then
return List.head ns
return! yieldAll <| List.tail ns}
let rec eval e =
optionM {
match e with
| Const n -> return n
| Plus (e1, e2) ->
let! v1 = eval e1
let! v2 = eval e2
return v1 + v2
| Div (e1, e2) ->
let! v1 = eval e1
let! v2 = eval e2
if v2 <> 0 then return v1 / v2
| Choose ns -> return! yieldAll ns
}
eval e
let evalAll e =
let rec yieldAll (ns : int list) =
multiM { if not (List.isEmpty ns) then
return List.head ns
return! yieldAll <| List.tail ns}
let rec eval e =
multiM {
match e with
| Const n -> return n
| Plus (e1, e2) ->
let! v1 = eval e1
let! v2 = eval e2
return v1 + v2
| Div (e1, e2) ->
let! v1 = eval e1
let! v2 = eval e2
if v2 <> 0 then return v1 / v2
| Choose ns -> return! yieldAll ns
}
eval e
let ex1 = Div(Const 420, Choose [10; 0; 42])
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment