Quantcast
Channel: ReScript Forum - Latest posts
Viewing all articles
Browse latest Browse all 2592

Module functors vs. records-of-functions for interface/implementation separation

$
0
0

experimenting with this, i threw this contrived but i think relevant example together:


// src/Deps.res

module Clock = SystemClock
module BaseLog = TimestampedLog.Make(Clock, ConsoleLog, SecsSinceStartTimestampedLogFmt)

let mkLog = (name): module(Log.T) =>
  module(
    NamedLog.Make(
      BaseLog,
      BracketedNamedLogFmt,
      {
        let name = name
      },
    )
  )

module FileRepo = InMemoryFileRepo.Make(Clock, unpack(mkLog("bosse")))

// src/DoSomething.res

module Make = (Clock: Clock.T, Log: Log.T) => {
  let foo = () => {
    let now = Clock.now()->Float.toString
    Log.info(`now is ${now}`)
  }
}

// src/Main.res

let main = () => {
  module DS = DoSomething.Make(Deps.Clock, unpack(Deps.mkLog("DoSomething")))
  DS.foo()
}

main()

// src/_Adapters/BracketedNamedLogFmt.res

let fmt = (~name, ~text) => {
  `[${name}] ${text}`
}

// src/_Adapters/ConsoleLog.res

let info = Console.log

// src/_Adapters/InMemoryFileRepo.res

module Make = (Clock: Clock.T, Log: Log.T) => {
  let store = dict{}

  let getById = async id => {
    let now = Clock.now()->Float.toString
    Log.info(`looking for file: ${id->Id.toString} now is ${now}`)
    store->Dict.get(id->Id.toString)
  }

  let save = async (file: File.t) => {
    store->Dict.set(file.id->Id.toString, file)
  }
}

// src/_Adapters/NamedLog.res

module Fmt = {
  module type T = {
    let fmt: (~name: string, ~text: string) => string
  }
}

module Make = (
  Log: Log.T,
  Fmt: Fmt.T,
  Conf: {
    let name: string
  },
) => {
  let info = s => {
    Log.info(Fmt.fmt(~name=Conf.name, ~text=s))
  }
}

// src/_Adapters/SecsSinceStartTimestampedLogFmt.res

let start = Date.now()

let fmt = (~ts, ~text) => {
  let s = (ts - start) / 1000.0
  `(${s->Float.toString}s) ${text}`
}

// src/_Adapters/SystemClock.res

let now = () => {
  Date.now()
}

// src/_Adapters/TimestampedLog.res

module Fmt = {
  module type T = {
    let fmt: (~ts: float, ~text: string) => string
  }
}

module Make = (Clock: Clock.T, Log: Log.T, Fmt: Fmt.T) => {
  let info = text => {
    let ts = Clock.now()
    Log.info(Fmt.fmt(~ts, ~text))
  }
}

// src/_Domain/File.res

type t = {
  id: Id.t,
  name: string,
}

let make = (~id, ~name) => {
  id,
  name,
}

// src/_Domain/Id.res

type t = string

let fromString = s => s
let toString = s => s

// src/_Ports/Clock.res

module type T = {
  let now: unit => float
}

// src/_Ports/FileRepo.res

module type T = {
  let getById: Id.t => promise<option<File.t>>
  let save: File.t => promise<unit>
}

// src/_Ports/Log.res

module type T = {
  let info: string => unit
}

it does get sort of verbose and i could inline a few things here and there. i dont like using unpack however (it seems to be entirely undocumented)

also @tsnobip your input welcome if i’m doing something crazy here


Viewing all articles
Browse latest Browse all 2592

Trending Articles