FP ⇒ OOP
F
O
P
Процедуры
Процедуры
PROCEDURE update_user_name(
    id    USER_ID,
    name  STRING
) 
BEGIN.
  VAR user         USER.
  VAR current_time TIMESTAMP.
  
  READ users INTO user WITH ID id.
  SET user.name TO name.
  GET TIME STAMP TO current_time.
  SET user.changed TO current_time.
  WRITE users FROM user WITH ID id.
END.Процедуры
PROCEDURE set_user_changed_now(
	user  USER
).
BEGIN.
  VAR current_time TIMESTAMP.
  GET TIME STAMP TO current_time.
END
PROCEDURE update_user_name(
    id    USER_ID,
    name  STRING
) 
BEGIN.
  VAR user         USER.
  
  READ users INTO user WITH ID id.
  SET user.name TO name.
  CALL set_user_changed_now( user )
  WRITE users FROM user WITH ID id.
END.SET a = b * 2 + 345.
SET full_name = firstName ++ " " ++ lastName.
SET capitalized = length( s ) > 0 && 
                  isUpperCase( s[0] ).
READ users TO user.
WRITE users FROM user.
GET TIME STAMP TO now.
PRINT 'Hello World!'.
FOR i = 1 TO 10 STEP 2.
 ...
END.
IF x > 2.
  ...
ELSE.
  ...
END.
TRY.
  ...
CATCH X.
  ...
FINALLY.
  ...
END.Операторы
Команды
Контроль
исполнения
Проблемы
- Глобальные переменные
- Пред- и пост- условия
- Изменения в зависимых блоках
PROCEDURE SAVE_USER( user USER )
BEGIN.
  SET user.id = g_next_id.
  ADD 1 TO g_next_id.
  
  IF user.name IS EMPTY.
    THROW USER_NAME_IS_EMPTY.
  END.
  
  LOOP AT user.friends INTO friend.
    CALL remove_frient_if_incorrent( user, friend ).
  ENDLOOP.
  
  CALL initialize_auth_data ( user ).
  
  UPDATE users FROM user.
END.
  
Объекты
ООП
def updateUserName(name: String): Unit = {
    this.name = name
    this.changed = time.now()
    this.save()
}
- Сохраняет процедурный стиль написания методов
- Не использует команды. Только методы
- 
	Разрешает прямой доступ к состоянию только методам этого объекта 
Single-responsibility principle
Каждый класс или модуль выполняет ровно одну задачу
GRASP
DDD
- Данные
- Утилиты
- Процессы
Данные
- Entity
- Value object
- Event
POJO
trait User{
    def getName: String
    def setName(s: String): Unit
    
    def getId: String
    
    def getFriends: List[User]
    def addFriend(user: User): Unit
    def removeFriend(user: User): Unit
}case class User(id: String, name: String, friends: List[User])Утилиты
- Pure Fabrication (GRASP)
- Service (DDD)
trait Iterator[A]{
    def next: Option[A]
}
trait Decorator[A]{
    def decorate(obj: A): A
}
trait Comparator[A]{
    def compare(x: A, y: A): Int
}
trait Predicate[A]{
    def check(x : A) : Boolean
}Процессы
- Factory
- Repository
trait Users{
    def getUser(id: String): User
    def remove(user: User): Unit
}
trait Rules{
    def validate(expr: String): Rule
    def activate(rule: Rule): Unit
}- Controller
- Session
- Facade
Функции
Операторы
val commissionQ = 1 + commissionPercents / 100
val newBalance = oldBalance + income * commissionQval estimatedDuration = elapsedTime
                         .multipliedBy(doneCount)
                         .dividedBy(totalCount)
                         
val estimatedEnd = now.plus(estimatedDuration)val estimatedDuration = elapsedTime * doneCount / totalCount
val estimatedEnd = now + estimatedDurationval ruleset = basic.or(privileged.when(user.vip)).and(countryRules)val ruleset = (basic || privileged ? user.vip) && countryRulesДанные
case class User(id: UserId, name: String, roles: List[Role])sealed trait User
case object Anonymous extends User
case class External(partner: PartnedId, extId: String) extends User
case class WebUser(id: UserId, name: String, roles: List[RoleId]) extends User
List[User], Vector[User], Set[User], Map[String, User]Преобразования
type Predicate[A] = A => Boolean
type Decorator[A] = A => A
type Comparator[A] = (A, A) => Ordering
type Iterator[S, A] = S => Option[(S, A)]Иммутабельность
def setUserName(user: User, name: String): User = 
	user.copy(name = name)
def setUserName(name: String): State[User, Unit] = 
	State.update(_.copy(name = name))
val userName: User Update String = 
	GenContains[User](_.name)
def setActive(user: User, active: Boolean): User = 
    user.copy(
      friends = user.friends.map(_.copy(active = true)), 
      active = true
    )
    
val userActive: User Update Boolean = 
	User.active then (User.friends > every > userActive.delayed)Полиморфизм
Интерфейсы
PROCEDURE update_user_name( user User, name String )
PROCEDURE update_product_name( product ProductId, company CompanyId, name String )
PROCEDURE update_dog_name( id DogId , name String)class User{
    def setName(name: String)
}
class Product{
    def setName(name: String)
}
class Dog{
    def setName(name: String)
}
trait Named{
    def getName: String
    def setName(name: String)
}Параметрический полиморфизм
PROCEDURE update_user_name( user User, name String )
PROCEDURE update_product_name( product ProductId, company CompanyId, name String )
PROCEDURE update_dog_name( id DogId , name String)def userName: User Contains String
def productName: Product Contains String
def dogName: Product Contains Stringtrait Named[A]{
    def name: A Contains String
}trait Named[A]{
    def updateName(f: String => String, old: A): A
    def getName(a: A): String
}Интерпретация
Консоль
sealed trait Console
case class PrintLn(s: String, next: Console) extends Console
case class ReadLn(continue: String => Console) extends Console
case object End extends Console// читает две строчки, затем печатает их конкатенацию
val echo2: Console = ReadLn( s1 => ReadLn( s2 => Println(s1 + s2, End) ) )Паттерн-матчинг
sealed trait Console
case class PrintLn(s: String, next: Console) extends Console
case class ReadLn(continue: String => Console) extends Console
case object End extends Consoledef runConsoleS(c: Console, in: Vector[String], out: Vector[String] = Vector()): Vector[String] = 
c match {
  case PrintLn(s, next) => runConsoleS(next, in, out :+ s)
  case ReadLn(k) =>
    in match {
      case s +: rest => runConsoleS(k(s), rest, out)
      case _         => out
    }
  case End => out
}def runConsole(c: Console): Unit = c match {
    case PrintLn(s, next) => 
    	println(s)
        runConsole(next)
    case ReadLn(k) => runConsole(k(StdIn.readLine()))
    case End => 
}Типы и Продолжения
sealed trait Console[+A] {
  def flatMap[B](f: A => Console[B]): Console[B] = Continue(this, f)
  def map[B](f: A => B) = flatMap(a => Now(f(a)))
}
case class PrintLn(s: String)                                 extends Console[Unit]
case object ReadLn                                            extends Console[String]
case class Now[+A](a: A)                                      extends Console[A]
case class Continue[A, +B](c: Console[A], f: A => Console[B]) extends Console[B]
val echo2: Console[Unit] = for{
    s1 <- ReadLn
    s2 <- ReadLn
    _  <- PrintLn(s1 + s2)
} yield ()Консоль
sealed trait Console[+A] {
  def flatMap[B](f: A => Console[B]): Console[B] = Continue(this, f)
  def map[B](f: A => B) = flatMap(a => Now(f(a)))
}
case class PrintLn(s: String)                                 extends Console[Unit]
case object ReadLn                                            extends Console[String]
case class Now[+A](a: A)                                      extends Console[A]
case class Continue[A, +B](c: Console[A], f: A => Console[B]) extends Console[B]
def runConsole[A](c: Console[A]): A = c match {
    case Now(a)     => a
    case ReadLn     => StdIn.readLine()
    case PrintLn(s) => println(s)
    case Continue(c1, f) =>
      c1 match {
        case Now(a) => runConsole(f(a))
        case PrintLn(s) =>
          println(s)
          runConsole(f(()))
        case ReadLn          => runConsole(f(StdIn.readLine()))
        case Continue(c2, g) => runConsole(c2.flatMap(g(_).flatMap(f)))
      }
}Монада
trait Monad[F[_]]{
    def pure(a: A): F[A]
    def flatMap[A, B](a: F[A], f: A => F[B]): F[B]
}
implicit object consoleMonad extends Monad[Console]{
    def pure(a: A) = Now(a)
    def flatMap[A, B](a: A, f: A => Console[B]) = Continue(a, f)
}  def printEachThrice[A](elements: List[String]): Console[Unit] = 
    elements.traverse_( a => for{
        _ <- PrintLn(a)
        _ <- PrintLn(a)
        _ <- PrintLn(a)
    } yield ())
    
  def printEven(x: Int): Console[Unit] = 
    if(x % 2 == 0) PrintLn(x.toString)
    else unit[Console]  def printEachThrice[A](elements: List[String]) =
    elements.traverse_(a => printLn(a).replicateA(3))
  def printEven(x: Int) =
    printLn(x.toString).whenA(x % 2 == 0)Free Монада
sealed trait ConsoleF[+A]
case class PrintLn(s: String) extends ConsoleF[Unit]
case object ReadLn            extends ConsoleF[String]
  
type Console[A] = Free[ConsoleF, A]
def printLn(s: String): Console[Unit] = Free.liftF(PrintLn(s))
val readLn: Console[String]           = Free.liftF(ReadLn)
val echo2: Console[Unit] = for {
  s1 <- readLn
  s2 <- readLn
  _  <- printLn(s1 + s2)
} yield ()
def runConsoleF[A](c: ConsoleF[A]): A = c match {
  case PrintLn(s) => println(s)
  case ReadLn     => StdIn.readLine()
}
def runConsole[A](c: Console[A]): A = c.foldMap[Id](funK(runConsoleF))
IO
IO
zio.ZIO
cats.effect.IO
monix.eval.TaskЭффекты
IO.pure(1)
IO(println("Hello world!!!"))
IO(runServer())
IO.async(cb => getData(a => cb(Right(a))))
IO.cancelable{ cb => 
    val cancelable = getData(a => cb(Right(a)))
    IO(cancelable.cancel())
}trait Timer {
  def realTime(unit: TimeUnit): IO[Long]
  def sleep(duration: FiniteDuration): IO[Unit]
}try ... finally ...
trait IO[+A]{
   //...
   def bracket[B](use: A => F[B])(release: (A, ExitCase[B]) => F[Unit]): F[B]
   //...
}openFile(name).bracket( file => for {
    line <- file.readLine
    _    <- processLine(line)
}){case (file, _) => file.close} OPEN name TO file.
TRY.
    READ LINE OF file TO line.
    CALL process_line( line ).
FINALLY.
    CLOSE file.
END.
  Concurrency
trait Fiber[+A] {
  def cancel: IO[Unit]
  def join: IO[A]
}
trait IO[+A]{
  //...
  def start: IO[Fiber[A]]
  def racePair[B](rb: IO[B]): IO[Either[(A, Fiber[B]), (Fiber[A], B)]]
  //...
}
for {
  ...
  fiber <- process.start
  ...
   _ <- fiber.cancel
  ...
}
processA.racePair(processB) match {
    case Left((a, bfib)) =>
    case Right((fiba, b)) => 
}
Синхронизация
trait Ref[A]{
   def get: IO[A]
   def set(a: A): IO[Unit]
   def modify[B](f: A => (A, B)): IO[B]
}abstract class Semaphore {
    def acquireN(n: Long): IO[Unit]
    def releaseN(n: Long): IO[Boolean]
    def withPermit[A](t: IO[A]): IO[A]
}object Ref{
    def of[A](a: A): IO[Ref[A]]
}object Semaphore{
  def apply(limit: Long): IO[Semaphore]
}Фабричные методы
object Ref{
  def of[A](a: A): IO[Ref[A]]
}
object Semaphore{
  def apply(limit: Long): IO[Semaphore]
}
object HttpClient{
  def apply(config: HttpConfig): IO[HttpClient]
}
object Cluster{
  def connect(config: ClusterConfig): IO[Cluster]
}Зависимость
def saveUser(user: User, db: DB, cache: UserCache): IO[Unit]

Reader
trait Reader[Env, +A]{
   def run(env: Env): IO[A]  
   
   def flatMap[B](f: A => Reader[Env, A]): Reader[Env, A] = 
   	 env => this.run(env).flatMap(a => f(a).run(env))
}
object Reader{
  def pure[Env, A](a : A): Reader[Env, A] = _ => IO.pure(a)
  
  def lift[Env, A](la: IO[A]): Reader[Env, A] = _ => la
}Контекст
case class Environment(
    config: Config
    opCache: OperationCache,
    users: UserRepo,
    httpClient: HttpClient,
    kafka: KafkaProducer,
)
object Environment{
   def apply(config: Config): IO[Environment] = for {
      opCache     <- OperationCache()
      users       <- UserRepo(config.users)
      httpClient  <- HttpClient(config.http)
      kafka       <- KafkaProducer(config.kafka)
    } yield Environment(
       config = config,
       opCache = opCache,
       users = users,
       httpClient = httpClient,
       kafka = kafka
     )
}
def application: Reader[Environment, Unit]
def execute: IO[Unit] = 
  for{
     config       <- readConfig
     environment  <- Environment(config)
     _            <- application.run(environment).foreverM
  } yield ()Tagless Final
Интерпретация
def printUserInfo(user: User): Console[Unit]
def promoteToAccout(login: login): Security[Account]
def getActiveFriends(userId: UserId): UserRepo[List[User]]Ограничения Free
3. Expression Problem
def checkUserAuthorized(user: User): (UserRepo with Security)[Boolean]
def printVerificationInfo(account: Acount): (Console with Verification)[Unit]1.Low Order
def doOnCommit[A](action: Transaction[A]): Transaction[A]
def forEachProcess[A](f: Process => Parallel[A]): Parallel[Unit]2. Boilerplate
def printLn(x: String): Console[A] = Free.lift(PrintLn(s))
def getRoles(userId: UserId, time: Instant): Security[List[Role]] = 
	Free.lift(GetRoles(userId, time))Free : Expression Problem
- Scala:
	- cats.EitherK + InjectK
- Eff
 
- Haskell
sealed trait Console
case class PrintLn(s: String, next: Console) extends Console
case class ReadLn(continue: String => Console) extends Console
case object End extends Console
val echo2: Console = ReadLn( s1 => ReadLn( s2 => Println(s1 + s2, End) ) )trait Console[A]{
    def printLn(s: String, next: A): A
    def readLn(continue: String => A): A
    def end: A
}
def echo2[A](c: Console[A]): A = c.readLn(s1 => c.readLn(s2 => c.println(s1 + s2, c.end)))object RunConsole extends Console[() => Unit]{
    def printLn(s: String, next: () => Unit) => () => {
    	println(s)
        next()
    }
    def readLn(k: String => () => Unit) => k(StdIn.readLine())()
    def end = () => () 
}Tagless Final
trait Console[A]{
    def printLn(s: A, next: A): A
    def readLn(continue: A => A): A
    def end: A
}
trait Arith[A]{
    def literal(i : Int) : A
    def parse(x: A): A
    def show(x: A): A
    def plus(x: A, y: A): A
    def times(x: A, y: A): A
}
def printSum[A](c: Console[A], a: Arith[A]): A = 
    c.readLn(s1 => 
       c.readLn(s2 => 
          c.printLn(
             a.show(a.plus(a.parse(s1), a.parse(s2))))))Expression Problem
trait Console[F[_]]{
    def printLn[A](s: F[String], next: F[A]): F[A]
    def readLn[A](continue: F[String] => F[A]): F[A]
    def end: F[Unit]
}
trait Arith[F[_]]{
    def literal(i : Int) : F[Int]
    def parse(x: F[String]): F[Int]
    def show(x: F[Int]): F[String]
    def plus(x: F[Int], y: F[Int]): F[Int]
    def times(x: F[Int], y: F[Int]): F[Int]
}
def printSum[F[_]](c: Console[F], a: Arith[F]): F[Unit] = 
    c.readLn(s1 => 
       c.readLn(s2 => 
          c.printLn(
             a.show(a.plus(a.parse(s1), a.parse(s2))))))Типизированые языки
trait Monad[F[_]]{
   def pure[A](a: A): F[A]
   def flatMap[A, B](fa: F[A], f: A => F[B]): F[B]
} 
trait Console[F[_]]{
    def printLn[A](s: String): F[Unit]
    def readLn[A]: F[String]
}
trait Arith[F[_]]{
    def literal(i : Int) : F[Int]
    def parse(x: String): F[Int]
    def show(x: Int): F[String]
    def plus(x: Int, y: Int): F[Int]
    def times(x: Int, y: Int): F[Int]
}
def printSum[F[_]](implicit m: Monad[F], c: Console[F], a: Arith[F]): F[Unit] = 
    for{
    	sx <- c.readLn
        sx <- c.readLn
        x  <- a.parse(sx)
        y  <- a.parse(sy)
        z  <- a.sum(x, y)
        sz <- a.show(z)
        _  <- c.printLn(sz)
    } yield ()Монады
def printSum[F[_]: Monad: Console: Arith]: F[Unit] = 
    for{
    	sx <- readLn[F]
        sx <- readLn[F]
        x  <- parse[F](sx)
        y  <- parse[F](sy)
        z  <- sum[F](x, y)
        sz <- show[F](z)
        _  <- printLn[F](sz)
    } yield ()Монады
SOLID
- 
	Single Responsibility
- 
	Open Close Principle
- 
	Liskov Substitution
- 
	Interface Segregation
- 
	Dependency Inversion

THE DEATH OF FINAL TAGLESS
THE DEATH OF FINAL TAGLESS
trait Sync[F[_]] extends Bracket[F, Throwable] with Defer[F] {
  def delay[A](thunk: => A): F[A]
}
trait Async[F[_]] extends Sync[F] with LiftIO[F] {
  def async[A](k: (Either[Throwable, A] => Unit) => Unit): F[A]
}
trait Concurrent[F[_]] extends Async[F] {
  def cancelable[A](k: (Either[Throwable, A] => Unit) => CancelToken[F]): F[A]
}object Ref {
  def of[F[_], A](a: A)(implicit F: Sync[F]): F[Ref[F, A]] = ...
}
object Semaphore
  def apply[F[_]](n: Long)(implicit F: Concurrent[F]): F[Semaphore[F]] = ...
}
final class Stream[F[_], O]{
  //...
  def merge(that: Stream[F, O])(implicit F: Concurrent[F]): Stream[F, O] = ...
  def compile[G[_]](implicit  compiler: Stream.Compiler[F, G]) = ...
  // ...
}
object Compiler{
   implicit def syncInstance[F[_]](implicit F: Sync[F]): Compiler[F, F] = ...
}Tofu

trait Fire[F[_]] {
  def fireAndForget[A](fa: F[A]): F[Unit]
}
trait Race[F[_]] extends Fire[F] {
  def race[A, B](fa: F[A], fb: F[B]): F[Either[A, B]]
  def never[A]: F[A]
}
trait Start[F[_]] extends Fire[F] with Race[F] {
  def start[A](fa: F[A]): F[Fiber[F, A]]
  def racePair[A, B](fa: F[A], fb: F[B]): F[Either[(A, Fiber[F, B]), (Fiber[F, A], B)]]
}
trait Guarantee[F[_]] {
  def bracket[A, B, C](init: F[A])(action: A => F[B])(release: (A, Boolean) => F[C]): F[B]
}
trait Daemonic[F[_], E] {
  def daemonize[A](process: F[A]): F[Daemon[F, E, A]]
}Concurrency
trait Raise[F[_], E] {
  def raise[A](err: E): F[A]
}
trait RestoreTo[F[_], G[_]] extends Lift[G, F] {
  def restore[A](fa: F[A]): G[Option[A]]
}
trait Restore[F[_]] extends RestoreTo[F, F] {
  def restoreWith[A](fa: F[A])(ra: => F[A]): F[A]
}
trait HandleTo[F[_], G[_], E] extends RestoreTo[F, G] {
  def handleWith[A](fa: F[A])(f: E => G[A]): G[A]
}
trait Handle[F[_], E] extends HandleTo[F, F, E] with Restore[F] {
  def tryHandleWith[A](fa: F[A])(f: E => Option[F[A]]): F[A]
}
trait ErrorsTo[F[_], G[_], E] extends Raise[F, E] with HandleTo[F, G, E]
trait Errors[F[_], E] extends Raise[F, E] with Handle[F, E] with ErrorsTo[F, F, E] Ошибки
trait MakeRef[I[_], F[_]] {
  def refOf[A](a: A): I[Ref[F, A]]
}
trait MakeSemaphore[I[_], F[_]] {
  def semaphore(count: Long): I[Semaphore[F]]
}
trait MakeMVar[I[_], F[_]] {
  def mvarOf[A](a: A): I[MVar[F, A]]
  def mvarEmpty[A]: I[MVar[F, A]]
}
trait MakeDeferred[I[_], F[_]] {
  def deferred[A]: I[Deferred[F, A]]
}
trait MakeAgent[I[_], F[_]] {
  def agentOf[A](a: A): I[Agent[F, A]]
}
trait MakeGatekeeper[I[_], F[_], A] {
  def gatekeeper(available: A): I[Gatekeeper[F, A]]
}
trait MakeQVar[I[_], F[_]] {
  def qvarOf[A](a: A): I[QVar[F, A]]
  def qvarEmpty[A]: I[QVar[F, A]]
}Фабрики
trait MakeSomething[I[_], F[_]]{
    def make1(arg1: Arg1, arg2: Arg2): I[Something[F]]
    def make2(arg3: Arg3): I[Something[F]]
}Фабрика
trait Maker[Struct[_[_]], Init[_], Effect[_], Input]{
   def make(input: Input): Init[Struct[F]]
}sealed trait MVarInput[A]
case class Empty[A]() extends MVarInput[A]
case class Of[A](a: A) extends MVarInput[A]
implicit def makeMVar[A]: Make[MVar[*[_], A, I[_], F[_], MVarInput[A]] Иногда
Абстракций
Паттерны
Удобнее
Контекст
Контекст
trait WithContext[F[_], C]{
	def context: F[C]
}
trait WithLocal[F[_], C] extends WithContext[F, C]{
	def local[A](fa: F[A])(project: C => C): F[A]
}
trait WithRun[F[_], G[_], C] extends WithLocal[F, C] with Unlift[G, F] {
	def runContext[A](fa: F[A])(ctx: C): G[A]
}Среда исполнения
type App[+A] = Env[Environment, A]
@ClasyOptics
case class Config(
  http: HttpConfig,
  db: DBConfig,
  kafka: KafkaConfig,
)
@ClassyOptics
case class Environment(
  @promote config: Config,
  httpClient: HttpClient[App],
  database : DB[App],
  kafka: Kafka[App]
)
WithRun[App, Task, Environment]
WithLocal[App, Environment]
WithContext[App, Environment]
WithLocal[App, Config]
WithLocal[App, HttpClient[App]]
WithLocal[App, DBConfig]
WithLocal[App, HttpConfig]Embed
def checkSecurity[F[_]: Monad](
    implicit hasUserRepo: WithContext[F, UserRepo[F]], 
             hasSecuriry: WithContext[F, Security[F]]): F[Unit] = 
    for{
    	userRepo <- hasUserRepo.context
        security <- hasSecurity.context
        ....
    }trait Embed[Module[f[_]]]{
   def embed[F[_]: Monad](fmod: F[Module[F]]): Module[F]
}Контейнер
@derive(embed)
trait Security[F[_]]{
   def authenticate(login: Login): F[User]
   def checkAuth(user: User, op: Operation): F[Bool]
}
class SecurityContainer[F[_]](fsec: F[Security[F]]) extends Security[F]{
      def authenticate(login: Login) = for{
        security <- fsec
        user     <- security.authenticate(login)
      } yield user
      
      def checkAuth(user: User, op: Operation) = for{
         security <- fsec
         success  <- security.checkAuth(user, op)
      } yield success
}
implicit val embedSecurity[F[_]] = new Embed[Security]{
   def embed[F[_]](fsec: F[Security[F]]) = new SecurityContainer(fsec)
}
implicit val contextualSecurity[F[_]: Monad](implicit has: F WithContext Security[F]): Security[F] =
	implicily[Embed[Security]].embed(has.context)Зависимости
object DBSecurity{
    def apply[I[_]: Monad, F[_]: Monad :DB :Profile]: I[Security[F]]
}
object DBProfiles{
    def apply[I[_]: Monad, F[_]: Monad: DB: Security]: I[Prodiles[F]]
}
def init: Task[Environment] = for{
   ...
   security <- DBSecurity[Task, App]
   ...
   profiles <- DBProfiles[Task, App]
   ...
}
DI
trait Embed[Module[f[_]]]{
   def embed[F[_]: Monad](fmod: F[Module[F]]): Module[F]
}=
DI Container
Комбинация
Абстракции высшего порядка
trait FunctorK[A[_[_]]]{
    def mapK[F[_], G[_]](af: A[F])(fk: F ~> G): A[G]
}
trait SemigroupalK[A[_[_]]]{
    def productK[F[_], G[_]](af: A[F], ag: A[G]): A[Tuple2K[F, G, ?]]
}
trait ApplyK[A[_[_]]] extends FunctorK[A] with SemigroupalK[A]{
    def map2K[F[_], G[_], H[_]](af: A[F], ag: A[G])(f: Tuple2K[F, G, ?] ~> H): A[H]
}Не-функторы
@derive(applyK)
trait Security[F[_]]{
   def authenticate(login: Login): F[User]
   def checkAuth(user: User, op: Operation): F[Bool]
}
type Mid[F[_], A] = F[A] => F[A]
class SecurityLogging[F[_]: Monad: Logging] extends Security[Mid[F, *]]{
   def authenticate(login: Login): F[User] => F[User] = 
   inner => for{
          _ <- debug"started authentication for $login"
          user <- inner
          _ <- debug"finished authentication for $login result $user"
       } yield user 
   def checkAuth(user: User, op: Operation): F[Bool] => F[Bool] = 
   inner => for {
           _      <- debug"checking authorization $op for $user"
           result <- inner
           _      <- debug"finished checking authorization of $op for $user result $result" 
         
       } yield result
}Аспекты
def attach[F[_]](plugin: Security[Mid[F, *]], impl: Security[F]): Security[F]
def combine[F[_]](s1: Security[Mid[F, *]], s2 : Security[Mid[F, *]]): Security[Mid[F, *]]val securityPlugins: Security[Mid[App, *]] = 
    securityTracing.when(config.tracing) combine
    securityCaching.when(config.caching) combine
    securityMonitoring.when(config.monitoring)
    
implicit val securiry: Security[App] = securityPlugins.attach(securityImpl)AOP
Фреймворк?
@derive(representableK)
trait Security[F[_]]{
   def authenticate(login: Login): F[User]
   def checkAuth(user: User, op: Operation): F[Bool]
}
object Security extends ContextEmbed[Security]trait RepresentableK[Module[f[_]]] extends Embed[Module] with ApplyK[Module]F
O
P
TF
Спасибо за внимание!


FP ⇒ OOP
By Oleg Nizhnik
FP ⇒ OOP
- 1,307
 
   
  