Skip to content

Instantly share code, notes, and snippets.

@sjmyuan
Created July 5, 2020 03:37
Show Gist options
  • Save sjmyuan/2e16726a34b2b6b329954f03284a7afc to your computer and use it in GitHub Desktop.
Save sjmyuan/2e16726a34b2b6b329954f03284a7afc to your computer and use it in GitHub Desktop.
case class Reader[A, B](run: A => B) {
def map[C](f: B => C): Reader[A, C] = {
val runN: A => C = (x: A) => f(run(x))
Reader(runN)
}
def flatMap[C](f: B => Reader[A, C]): Reader[A, C] = {
val runN: A => C = (x: A) => f(run(x)).run(x)
Reader(runN)
}
}
object Reader {
def ask[A]: Reader[A, A] = Reader[A, A](identity)
}
trait HttpRequest {
def get(url: String): String
}
class LogHttpRequest extends HttpRequest {
override def get(url: String): String = {
println(s"send request to ${url}")
List(1, 2, 3, 4, 5, 6).mkString(",")
}
}
trait Database {
def runSql(sql: String): Unit
}
class LogDatabase extends Database {
override def runSql(sql: String): Unit = println(s"run sql ${sql}")
}
trait DataSource[A] {
def getData: Reader[A, List[Int]]
}
class HttpDataSource extends DataSource[Env] {
override def getData: Reader[Env, List[Int]] =
for {
env <- Reader.ask[Env]
data = env.http.get("http://example.com/data").split(",").map(_.toInt).toList
} yield data
}
trait DataStore[A] {
def save(data: List[Int]): Reader[A, Unit]
}
class DatabaseStore extends DataStore[Env] {
override def save(data: List[Int]): Reader[Env, Unit] =
for {
env <- Reader.ask[Env]
} yield env.database.runSql(
s"insert into data_table values(${data.mkString(",")})"
)
}
trait DataEncoder {
def encode(data: List[Int]): List[Int]
}
class PlusOneEncoder extends DataEncoder {
def encode(data: List[Int]): List[Int] = {
println(s"encoding ${data}")
data.map(_ + 1)
}
}
case class Env(http: HttpRequest, database: Database, source: DataSource[Env], store: DataStore[Env], encoder: DataEncoder)
class DataJob {
def run: Reader[Env, Unit] =
for {
env <- Reader.ask[Env]
data <- env.source.getData
val encodedData = env.encoder.encode(data)
_ <- env.store.save(encodedData)
} yield ()
}
object Main {
def main() {
val http = new LogHttpRequest()
val database = new LogDatabase()
val source = new HttpDataSource
val store = new DatabaseStore
val encoder = new PlusOneEncoder()
val env = Env(http, database, source, store, encoder)
val program = new DataJob
program.run.run(env)
}
}
Main.main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment