Skip to content

Instantly share code, notes, and snippets.

@marklam
Last active June 25, 2020 12:45
Show Gist options
  • Save marklam/3693ea5ef64d25c36a4fff997e8e769b to your computer and use it in GitHub Desktop.
Save marklam/3693ea5ef64d25c36a4fff997e8e769b to your computer and use it in GitHub Desktop.
Change class to module
// How to deal with nested dependencies?
// Background : https://bartoszsypytkowski.com/dealing-with-complex-dependency-injection-in-f/
open System
// Services
type ILogger =
abstract LogMessage : string -> unit
type IWeb =
abstract Download : Uri -> string
type ISearch =
abstract TopHits : string -> string list // TODO - this _ isn't right
// Service locators I guess
type ILogger' = abstract Logger : ILogger with get
type IWeb' = abstract Web : IWeb with get
type ISearch' = abstract Search : ISearch with get
// How to use a service given your 'environment'
module Log =
let message (env : #ILogger') message =
env.Logger.LogMessage message
module Web =
let download (env : #IWeb') url =
env.Web.Download url
module Search =
let topHits (env : #ISearch') query =
env.Search.TopHits query
// Some implementations of the services
module ConsoleLogger =
let write (message : string) =
Console.WriteLine message
module CorporateNetwork =
let download url =
"Access denied"
module Yahoo =
let topHits env (query : string) =
Log.message env "Searching"
List.init 10 (fun i -> Web.download env (Uri("file://yahoo?"+query)))
#if before
type AltaVista(root) =
interface ISearch with
member _.TopHits env query =
// TODO - env won't work here
Log.message env "Searching"
List.init 10 (fun i -> Web.download env (Uri("file://altavista/" + root + "?"+query)))
#else // after
module AltaVista =
let topHits env root query =
Log.message env "Searching"
List.init 10 (fun i -> Web.download env (Uri("file://altavista/" + root + "?"+query)))
#endif
// Helper type to implement all the interfaces for the services the program requires
[<Struct>]
type Env =
{ Log : ILogger; Web : IWeb; Search : ISearch }
interface ILogger' with member this.Logger = this.Log
interface IWeb' with member this.Web = this.Web
interface ISearch' with member this.Search = this.Search
module Program =
// Program functionality
let newsService env =
let topStories = Search.topHits env "news"
for s in topStories do
Log.message env s
let main() =
// Composition for program
let rec env =
let log = { new ILogger with member _.LogMessage msg = ConsoleLogger.write msg }
let web = { new IWeb with member _.Download uri = CorporateNetwork.download uri }
//let search = { new ISearch with member _.TopHits query = Yahoo.topHits env query } // TODO - compiler warning
let search = { new ISearch with member _.TopHits query = AltaVista.topHits env "root" query } // TODO - compiler warning
{ Log = log; Web = web; Search = search }
// Run program
newsService env
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment