Created
June 2, 2016 23:41
-
-
Save Porges/df9b386a7e5383dbc857ea8fe49f4b95 to your computer and use it in GitHub Desktop.
Using reader monad in F# to pass around dependencies
This file contains hidden or 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 Implicit | |
type Implicitly<'i, 't> = private Implicitly of ('i -> 't) | |
let implicitly = Implicitly | |
let runImplicit impl (Implicitly f) = f impl | |
type ImplicitBuilder<'i> () = | |
member __.ReturnFrom x = x | |
member __.Return x = Implicitly (fun _ -> x) | |
member __.Bind (x : Implicitly<'i, 't>, f : 't -> Implicitly<'i, 'u>) : Implicitly<'i, 'u> = | |
Implicitly (fun impl -> runImplicit impl (f (runImplicit impl x))) |
This file contains hidden or 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 MyAPI | |
open Implicit | |
type QueryResult = unit // TODO | |
// dependencies | |
type InsertStuffDependencies = | |
{ | |
ReadText : string -> string; | |
ExecuteQuery : string -> string -> QueryResult | |
} | |
// give new names to the existing Implicit types | |
type InsertStuffAPI<'t> = Implicitly<InsertStuffDependencies, 't> | |
let api = ImplicitBuilder<InsertStuffDependencies>() | |
let runApi : InsertStuffDependencies -> _ = runImplicit | |
// here are the functions you can use: | |
let readText s : InsertStuffAPI<string> = implicitly (fun impl -> impl.ReadText s) | |
let executeQuery s1 s2 : InsertStuffAPI<QueryResult> = implicitly (fun impl -> impl.ExecuteQuery s1 s2) | |
// default implementation of dependencies | |
let defaults = { ReadText = raise <| System.NotImplementedException(); ExecuteQuery = raise <| System.NotImplementedException() } |
This file contains hidden or 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 MyProgram | |
open MyAPI | |
// here's your insertPerson function | |
let insertPerson firstName = api { | |
let! insertSql = readText "C:\InsertPersonQuery.txt" | |
return! executeQuery insertSql firstName | |
} | |
// to run it: | |
let result = runApi defaults (insertPerson "Hodor") | |
// but note that now we can decompose the insertPerson function | |
// and still not have to pass around the dependencies: | |
let doTheRead = api { | |
return! readText "C:\InsertPersonQuery.txt" | |
} | |
let doTheQuery query firstName = api { | |
return! executeQuery query firstName | |
} | |
let insertPersonDecomposed firstName = api { | |
let! insertSql = doTheRead | |
return! doTheQuery insertSql firstName | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment