Created
July 5, 2020 03:35
-
-
Save sjmyuan/d01e0cd978822858b5ec8b087b6b0bdc to your computer and use it in GitHub Desktop.
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
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 | |
} | |
trait HasHttpRequest[A] { | |
def getHttpRequest(v: A): HttpRequest | |
} | |
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 | |
} | |
trait HasDatabase[A] { | |
def getDatabase(v: A): Database | |
} | |
class LogDatabase extends Database { | |
override def runSql(sql: String): Unit = println(s"run sql ${sql}") | |
} | |
case class Env(http: HttpRequest, database: Database) | |
object Env { | |
implicit object EnvHasHttpRequest extends HasHttpRequest[Env] { | |
override def getHttpRequest(v: Env): HttpRequest = v.http | |
} | |
implicit object EnvHasDatabase extends HasDatabase[Env] { | |
override def getDatabase(v: Env): Database = v.database | |
} | |
} | |
trait DataSource[A] { | |
def getData: Reader[A, List[Int]] | |
} | |
class HttpDataSource[A: HasHttpRequest] extends DataSource[A] { | |
override def getData: Reader[A, List[Int]] = | |
for { | |
env <- Reader.ask[A] | |
http = implicitly[HasHttpRequest[A]].getHttpRequest(env) | |
data = 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[A: HasDatabase] extends DataStore[A] { | |
override def save(data: List[Int]): Reader[A, Unit] = | |
for { | |
env <- Reader.ask[A] | |
database = implicitly[HasDatabase[A]].getDatabase(env) | |
} yield 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) | |
} | |
} | |
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[Env] | |
val store = new DatabaseStore[Env] | |
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