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 * commissionQ
val estimatedDuration = elapsedTime
.multipliedBy(doneCount)
.dividedBy(totalCount)
val estimatedEnd = now.plus(estimatedDuration)
val estimatedDuration = elapsedTime * doneCount / totalCount
val estimatedEnd = now + estimatedDuration
val 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 String
trait 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 Console
def 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,146