Created
September 29, 2016 16:04
-
-
Save fbrubacher/25d79299e3b3c9dffbed5e6b65f6d870 to your computer and use it in GitHub Desktop.
Reading de goes article
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
// -- in my opinion the wonderful polymorphism of monad type classes in MTL | |
// -- is the best thing about MTL (rather than transformers themselves), and clearly | |
// -- superior to how early Free programs were built | |
// | |
// -- Nonetheless Free has en equivalent mechanism, which I'm dubbing Free Transformers | |
// -- FT which goes head-to-head with MTL and even allows developers to target both MTL | |
// -- and free portions of their code | |
// | |
// -- Free Transformesr | |
// | |
// class Console f where | |
// readLine :: f String | |
// writeLine :: String -> f Unit | |
trait Cosonle[F[_]] { | |
def readLine : F[String] | |
def writeLine(line : String) : F[Unit] | |
} | |
def myProgram[F[_]: Console : Monad] : F[Unit] | |
// laws for these typeclaess can by specified by embedding the functor | |
// into a suitable computational context such as Free | |
// The outer functor transforms the inner functor to yield a composite fuctor, | |
// Free programs are usually built from compositional functors | |
case class From[A](value : A) | |
case class To[A](value :A) | |
type TransferResult = Either[Error, (From[Amount], To[Amount])] | |
trait Banking[F[_]] { | |
def accounts : F[NonEmptyList[Account]] | |
def balance(account : Account) : F[Account] | |
def tranfer(amount : Amount, from : From[Amount], to : From[Account]) | |
: F[TransferResult] | |
def withdraw(amount : Amount) : F[Amount] | |
} | |
sealed trait BankingF[A] | |
case class Accounts[A](next : NonEmptyList[Account] => A) extends BankingF[A] | |
case class Balance[A](next : Amount => A) extends BankingF[A] | |
case class Transfer[A]( amount : Amount, from : From[Account], to : To[Account], | |
next : TransferResult => A) extends BankingF[A] | |
case class Withdraw[A](amount : Amount, next : Amount => A) extends BankingF[A] | |
) | |
// now we can create a suitable instance of Free that can be automatically derived | |
// from any suitable function | |
implicit def BankingFree[F[_]](implicit F : Banking[F]) : Banking[Free[F, ?]] = | |
new Banking[Free[F, ?]] { | |
def accounts : Free[F, NonEmptyList[Account]] = Free.liftF(f.accounts) | |
def balance(account : Account) : Free[F, Amount] = Free.liftF[F.balance(account)] | |
def transfer(amount : Amount, from : From[Account], to : From[Acccount]) : | |
Free[F, TransferResult] = Free.liftF(F.transfer(amount, from, to)) | |
def withdraw(amount : Amount) : Free[F, Amount] = Free.liftF(F.withdraw(amount)) | |
} | |
// we can define high level programs that operate in our business domain without tangingling | |
// with banking protocols etc | |
def example[F[_]: Inject[Banking, ?]] : Free[F, Amount] = | |
for { | |
as <- F.accounts | |
b <- F.balance(as.head) | |
} yield b | |
trait Interpreter[F[_], G[_]] = F ~> Free[G, ?] | |
type ~<[F[_], G[_]] = Interpreter[F, G] | |
// when using this notio of sequential computation it's helpful to be able to define | |
// an interpreter that doesn't produce a value whcih can be done using the Const like constructor | |
type Halt[F[_], A] = F[Unit] | |
// then an interpreter from f to g which produces no value is simply f~< Halt g | |
// Now, let’s say that we create the following onion: | |
// Banking is defined in terms of its protocol, which we want to log. | |
// The protocol is defined in terms of socket communication. | |
// Logging is defined in terms of file IO. | |
val bankingLogging : BankingF ~< Halt LoggingF | |
val bankingProtocol : BankingF ~< ProtocolF | |
val protocolSocket : ProtocolF ~< SocketF | |
val loggingFile : LoggingF ~< FileF | |
val execFile : FileF ~> IO | |
val execSocket : SocketF ~> IO | |
// Denotational Semantics | |
// Denotational semantics is a mathematical and compositional way of giving meaning to programs. The meaning of the program as a whole is defined by the meaning of the terms comprising the program. | |
// Denotational semantics provide an unprecedented ability to reason about programs in a composable and modular fashion. | |
// The onion architecture provides a way of specifying whole programs using denotational semantics, where the meaning of one domain is precisely and compositionally defined in terms of another doma |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment