Last active
November 1, 2022 23:58
-
-
Save isaacabraham/0849128cc72f26779d3bbd160a97114e to your computer and use it in GitHub Desktop.
F# port of the first half of John De Goes "FP to the max" (https://www.youtube.com/watch?v=sxudIMiOo68)
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
#load @".paket\load\net452\FSharpPlus.fsx" | |
open FSharpPlus | |
open System | |
[<AutoOpen>] | |
module rec IO = | |
let run (IO computation) = computation() | |
type IO<'T> = | |
| IO of (unit -> 'T) | |
static member (>>=) (x, f) = IO(fun () -> run x |> f |> run) | |
static member Return (x:'T) = IO(fun () -> x) | |
[<AutoOpen>] | |
module SideEffects = | |
let private r = Random() | |
let printLn text = IO (fun () -> printfn "%s" text) | |
let readLn() = IO(fun () -> Console.ReadLine()) | |
let random upper = IO(fun () -> r.Next(0, upper)) | |
let parseInt s = Int32.TryParse s |> function | true, x -> Some x | false, _ -> None | |
let rec checkContinue name = monad { | |
do! printLn ("Do you want to continue, " + name + "?") | |
let! answer = readLn() |> map String.toLower | |
return! | |
match answer with | |
| "y" -> IO.Return true | |
| "n" -> IO.Return false | |
| _ -> checkContinue name } | |
let rec gameLoop name = monad { | |
let! secret = random 5 |> map ((+) 1) | |
do! printLn ("Dear " + name + ", please guess a number from 1 to 5:") | |
let! input = readLn() | |
do! | |
match parseInt input with | |
| None -> printLn "You did not enter a number!" | |
| Some x when x = secret -> printLn ("You guessed right, " + name + "!") | |
| Some _ -> printLn (sprintf "You guessed wrong, %s! The number was: %d" name secret) | |
let! shouldContinue = checkContinue name | |
return! | |
if shouldContinue then gameLoop name | |
else IO.Return() } | |
let main = monad { | |
do! printLn "What is your name?" | |
let! name = readLn() | |
do! printLn ("Hello, " + name + " welcome to the game!") | |
do! gameLoop name | |
return() } | |
run main |
I think that the port of the second part corresponds to translating Scala "capabilities" into F# "effect handlers", as they are defined in this snippet http://www.fssnip.net/7TM/title/TypeSafe-effect-builder
It is the most generic concept of a dsl.
My version here:
https://gist.github.com/giuliohome/7cabc15c38ce22d3532e8046241e0ed7
Here's my simplified version https://gist.github.com/gusty/3a9d654de320225e4a17d92049646160/revisions
Using FSharpPlus tryParse
and the async
monad.
You probably know it already, you can use async
instead of io
(that's the reason why there's no IO monad in F#+).
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
@dpraimeyuu thank you :-) but I really just followed Jon's video step by step! I've never used FSharpPlus that much - it was just a time saver here instead of creating a full computation expression - but I know some developers who swear by it. You can get quite far in F# with such libraries which take advantage of SRTPs - kind of a hack in my opinion and there are sharp edges, but they work. Also there's FSharpX as well. But most F# developers and codebases that I've seen don't really use it.