Created
March 11, 2019 12:51
-
-
Save dadhi/27c44a264a61dfa1d8fce692bccac32e to your computer and use it in GitHub Desktop.
An F# toy example of Scala ZIO Environment
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
module EnvDemo | |
open System | |
open System.IO | |
[<Struct>] | |
type Nothing = | |
private | |
| Nothing | |
[<NoEquality;NoComparison>] | |
type Env<'r,'e,'a> = | |
| Env of ('r -> Result<'a,'e>) | |
module Env = | |
let run s (Env run) = run s | |
let unit a = Env (fun _ -> Ok a) | |
let get<'s,'e> : Env<'s,'e,'s> = Env Ok | |
let map (f:'a->'b) (env:Env<'r,'e,'a>) = | |
Env (fun s -> | |
match run s env with | |
| Ok a -> f a |> Ok | |
| Error e -> Error e | |
) | |
let bind (f:'a->Env<'r,'e,'b>) (env:Env<'r,'e,'a>) = | |
Env (fun s -> | |
match run s env with | |
| Ok a -> run s (f a) | |
| Error e -> Error e | |
) | |
let bindFromAny (f:'a->Env<'r,'e,'b>) (env:Env<'r,Nothing,'a>) = | |
Env (fun s -> | |
match run s env with | |
| Ok a -> run s (f a) | |
| _ -> Unchecked.defaultof<_> | |
) | |
let bindToAny (f:'a->Env<'r,Nothing,'b>) (env:Env<'r,'e,'a>) = | |
Env (fun s -> | |
match run s env with | |
| Ok a -> | |
match run s (f a) with | |
| Ok b -> Ok b | |
| _ -> Unchecked.defaultof<_> | |
| Error e -> Error e | |
) | |
let map2 s1 s2 f = | |
bind (fun a -> map (fun b -> f a b) s2) s1 | |
type EnvBuilder<'e>() = | |
member __.Bind(env:Env<'r,'e,'a>, f:'a -> Env<'r,Nothing,'b>) = | |
bindToAny f env | |
member __.Bind(env:Env<'r,Nothing,'a>, f:'a -> Env<'r,'e,'b>) = | |
bindFromAny f env | |
member __.Bind(env:Env<'r,'e,'a>, f:'a -> Env<'r,'e,'b>) = | |
bind f env | |
member __.Return(value) = unit value | |
member __.ReturnFrom(value) = value | |
member __.Yield(value) = unit value | |
member __.Zero() = unit() | |
member __.Combine(s1:Env<'s,'e,unit>, s2:Env<'s,'e,'a>) = map2 s1 s2 (fun _ s -> s) | |
member __.Delay(f) = f() | |
member __.For(xs:seq<'a>, f:'a -> Env<'s,'e,'a>) = xs |> Seq.map f | |
member __.Run(value) = value | |
[<AutoOpen>] | |
module EnvAutoOpen = | |
let env<'e> = Env.EnvBuilder<'e>() | |
type ConsoleService = | |
abstract member WriteLine : string -> Env<'r,Nothing,unit> | |
abstract member ReadLine : unit -> Env<'r,IOException,string> | |
type Console = | |
abstract member Console : ConsoleService | |
module Console = | |
let writeLine s = Env.bind (fun (c:#Console) -> c.Console.WriteLine s) Env.get | |
let readLine() = Env.bind (fun (c:#Console) -> c.Console.ReadLine()) Env.get | |
type LoggingService = | |
abstract member Log : string -> Env<'r,Nothing,unit> | |
type Logger = | |
abstract member Logging : LoggingService | |
module Logger = | |
let log s = Env.bind (fun (l:#Logger) -> l.Logging.Log s) Env.get | |
type PersistenceService = | |
abstract member Persist : 'a -> Env<'r,IOException,unit> | |
type Persistence = | |
abstract member Persistence : PersistenceService | |
module Persistence = | |
let persist a = Env.bind (fun (p:#Persistence) -> p.Persistence.Persist a) Env.get | |
let program() = | |
env<IOException> { | |
do! Logger.log "started" | |
do! Console.writeLine "Please enter your name:" | |
let! name = Console.readLine() | |
do! Logger.log ("got name = " + name) | |
do! Persistence.persist name | |
do! Console.writeLine ("Hi "+name) | |
do! Logger.log "finished" | |
return 0 | |
} | |
let testConsole = | |
{ new ConsoleService with | |
member __.WriteLine s = | |
Env.unit () | |
member __.ReadLine() = | |
Env.unit "hi" | |
} | |
let testLogging = | |
{ new LoggingService with | |
member __.Log s = | |
Env.unit () | |
} | |
let testPersistence = | |
{ new PersistenceService with | |
member __.Persist a = | |
Env.unit () | |
} | |
type TestEnv() = | |
interface Console with member __.Console = testConsole | |
interface Logger with member __.Logging = testLogging | |
interface Persistence with member __.Persistence = testPersistence | |
let test() = | |
Env.run (TestEnv()) (program()) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment