Проблема
Решение
Бо́льшие возможности для композиции
Легче тестировать и дебажить
Иногда иммутабельный код читать сложнее, чем мутабельный
В редких случаях допустимо использовать мутабельность локально для более читабельного кода
Бо́льшие возможности для композиции
Легче тестировать и дебажить
Может быть неочевидно без практики
1. Принимают аргументы
2. Возвращают результат, полученный на основе переданных аргументов
3. Для одних и тех же аргументов результат всегда будет одинаков
4. Не выбрасывают исключений
1. Не читают состояние за пределами функции
2. Не пишут в состояние за пределами функции
5. Не работают с IO (БД, файлы, сеть, ...)
3. Не мутируют входные аргументы
def sum(a: Int, b: Int): Int = a + b
def mul(a: Int, b: Int): Int = a * b
def sum(a: Int, b: Int): Int = a + b
def mul(a: Int, b: Int): Int = a * b
val res1 = mul(sum(1, 2), sum(2, 3))
def sum(a: Int, b: Int): Int = a + b
def mul(a: Int, b: Int): Int = a * b
val res1 = mul(sum(1, 2), sum(2, 3))
val res2 = {
val s1 = sum(1, 2)
val s2 = sum(2, 3)
mul(s1, s2)
}
def getList() = List(1)
def getList() = List(1)
val res1 = getList().flatMap(_ => getList())
def getList() = List(1)
val res1 = getList().flatMap(_ => getList())
val res2 = {
val list = getList()
list.flatMap(_ => list)
}
def future() = Future { println(1); 1 }
def future() = Future { println(1); 1 }
def res1 = future().flatMap(_ => future())
def future() = Future { println(1); 1 }
def res1 = future().flatMap(_ => future())
def res2 = {
val f = future()
f.flatMap(_ => f)
}
def future() = Future { println(1); 1 }
def res1 = future().flatMap(_ => future())
def res2 = {
val f = future()
f.flatMap(_ => f)
}
println("res1")
println(Await.result(res1, 1.second))
println("res2")
println(Await.result(res2, 1.second))
res1
1
1
1
res2
1
1
def future1() = Future { println(1); 1 }
def future2() = Future { println(2); 2 }
val future1val = future1()
val future2val = future2()
for {
res1 <- future1val
res2 <- future2val
} yield {
val res = res1 + res2
println(res)
res
}
def future1() = Future { println(1); 1 }
def future2() = Future { println(2); 2 }
class LazyFuture[A] private (
val run: ExecutionContext => Future[A]
)
class LazyFuture[A] private (
val run: ExecutionContext => Future[A]
)
object LazyFuture {
def delay[A](body: => A): LazyFuture[A] = {
new LazyFuture[A](ec => Future(body)(ec))
}
def fromFuture[A](future: => Future[A]): LazyFuture[A] = {
new LazyFuture[A](_ => future)
}
}
val lazyFuture: LazyFuture[Int] =
LazyFuture.delay { println(1); 1 }
val lazyFuture: LazyFuture[Int] =
LazyFuture.delay { println(1); 1 }
val future: Future[Int] = lazyFuture.run(ec)
val lazyFuture: LazyFuture[Int] =
LazyFuture.delay { println(1); 1 }
val future: Future[Int] = lazyFuture.run(ec)
val res: Int = Await.result(future, 1.second)
val lazyFuture: LazyFuture[Int] =
LazyFuture.delay { println(1); 1 }
val future: Future[Int] = lazyFuture.run(ec)
val res: Int = Await.result(future, 1.second)
println(res)
1
1
class LazyFuture[A] private (
val run: ExecutionContext => Future[A]
) { self =>
def map[B](f: A => B): LazyFuture[B] = {
new LazyFuture[B]((ec: ExecutionContext) => {
self.run(ec).map(f)(ec)
})
}
}
class LazyFuture[A] private (
val run: ExecutionContext => Future[A]
) { self =>
def flatMap[B](f: A => LazyFuture[B]): LazyFuture[B] = {
new LazyFuture[B]((ec: ExecutionContext) => {
self.run(ec).flatMap { a =>
f(a).run(ec)
}(ec)
})
}
}
def lazyFuture1() = LazyFuture.delay { println(1); 1 }
def lazyFuture2() = LazyFuture.delay { println(2); 2 }
def lazyFuture1() = LazyFuture.delay { println(1); 1 }
def lazyFuture2() = LazyFuture.delay { println(2); 2 }
val lazyFuture = for {
res1: Int <- lazyFuture1()
res2: Int <- lazyFuture2()
} yield {
val res = res1 + res2
println(res)
res
}
def lazyFuture1() = LazyFuture.delay { println(1); 1 }
def lazyFuture2() = LazyFuture.delay { println(2); 2 }
val lazyFuture = for {
res1: Int <- lazyFuture1()
res2: Int <- lazyFuture2()
} yield {
val res = res1 + res2
println(res)
res
}
val future: Future[Int] = lazyFuture.run(ec)
def lazyFuture1() = LazyFuture.delay { println(1); 1 }
def lazyFuture2() = LazyFuture.delay { println(2); 2 }
val lazyFuture = for {
res1: Int <- lazyFuture1()
res2: Int <- lazyFuture2()
} yield {
val res = res1 + res2
println(res)
res
}
val future: Future[Int] = lazyFuture.run(ec)
println(Await.result(future, 1.second))
1
2
3
3
def lfuture() = LazyFuture.delay { println(1); 1 }
res1
1
1
1
res2
1
1
1
def lfuture() = LazyFuture.delay { println(1); 1 }
def res1 = lfuture().flatMap(_ => lfuture())
def lfuture() = LazyFuture.delay { println(1); 1 }
def res1 = lfuture().flatMap(_ => lfuture())
def res2 = {
val lf = lfuture()
lf.flatMap(_ => lf)
}
def lfuture() = LazyFuture.delay { println(1); 1 }
def res1 = lfuture().flatMap(_ => lfuture())
def res2 = {
val lf = lfuture()
lf.flatMap(_ => lf)
}
println("res1")
println(Await.result(res1.run(ec), 1.second))
println("res2")
println(Await.result(res2.run(ec), 1.second))
def lazyFuture1() = LazyFuture.delay { println(1); 1 }
def lazyFuture2() = LazyFuture.delay { println(2); 2 }
def lazyFuture1() = LazyFuture.delay { println(1); 1 }
def lazyFuture2() = LazyFuture.delay { println(2); 2 }
val future1started = lazyFuture1()
val future2started = lazyFuture2()
def lazyFuture1() = LazyFuture.delay { println(1); 1 }
def lazyFuture2() = LazyFuture.delay { println(2); 2 }
val future1started = lazyFuture1()
val future2started = lazyFuture2()
val lazyFuture = for {
res1 <- future1started
res2 <- future2started
} yield {
val res = res1 + res2
println(res)
res
}
def lazyFuture1() = LazyFuture.delay { println(1); 1 }
def lazyFuture2() = LazyFuture.delay { println(2); 2 }
val future1started = lazyFuture1()
val future2started = lazyFuture2()
val lazyFuture = for {
res1 <- future1started
res2 <- future2started
} yield {
val res = res1 + res2
println(res)
res
}
lazyFuture.run(ec)
class LazyFuture[A] private (
val run: ExecutionContext => Future[A]
) { self =>
def par[B](that: LazyFuture[B]): LazyFuture[(A, B)]
}
class LazyFuture[A] private (
val run: ExecutionContext => Future[A]
) { self =>
def par[B](that: LazyFuture[B]): LazyFuture[(A, B)] = {
new LazyFuture[(A, B)]((ec: ExecutionContext) => {
})
}
}
class LazyFuture[A] private (
val run: ExecutionContext => Future[A]
) { self =>
def par[B](that: LazyFuture[B]): LazyFuture[(A, B)] = {
new LazyFuture[(A, B)]((ec: ExecutionContext) => {
val selfStarted = self.run(ec)
val thatStarted = that.run(ec)
})
}
}
class LazyFuture[A] private (
val run: ExecutionContext => Future[A]
) { self =>
def par[B](that: LazyFuture[B]): LazyFuture[(A, B)] = {
new LazyFuture[(A, B)]((ec: ExecutionContext) => {
val selfStarted = self.run(ec)
val thatStarted = that.run(ec)
selfStarted.flatMap { selfRes =>
thatStarted.map { thatRes =>
(selfRes, thatRes)
}(ec)
}(ec)
})
}
}
val lazyFuture = {
(lazyFuture1() par lazyFuture2())
}
val lazyFuture = {
(lazyFuture1() par lazyFuture2()).map {
case (res1, res2) =>
val res = res1 + res2
println(res)
res
}
}
val lazyFuture = {
(lazyFuture1() par lazyFuture2()).map {
case (res1, res2) =>
val res = res1 + res2
println(res)
res
}
}
lazyFuture.run(ec)