Last active
January 13, 2022 11:04
-
-
Save kfl/f3a91896845706e6fc7ff2dc77fbebe7 to your computer and use it in GitHub Desktop.
A simple monadic evaluator in F# with various monads
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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