Skip to content

Instantly share code, notes, and snippets.

@sjmyuan
Created July 5, 2020 03:42
Show Gist options
  • Save sjmyuan/ed7d3439ddda35e840f5b703df6725e7 to your computer and use it in GitHub Desktop.
Save sjmyuan/ed7d3439ddda35e840f5b703df6725e7 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)
class DataJob[A](
source: DataSource[A],
store: DataStore[A],
encoder: DataEncoder){
def run: Reader[A, Unit] =
for {
data <- source.getData
val encodedData = encoder.encode(data)
_ <- store.save(encodedData)
} yield ()
}
object Main {
def main() {
val http = new LogHttpRequest()
val database = new LogDatabase()
val env = Env(http, database)
val source = new HttpDataSource
val store = new DatabaseStore
val encoder = new PlusOneEncoder()
val program = new DataJob[Env](source, store, encoder)
program.run.run(env)
}
}
Main.main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment