문서의 선택한 두 판 사이의 차이를 보여줍니다.
양쪽 이전 판 이전 판 다음 판 | 이전 판 | ||
비동기_프로그래밍과_scala [2017/02/27 08:53] cumul0529 인용 문법 수정 |
비동기_프로그래밍과_scala [2020/08/09 07:39] (현재) cumul0529 [함수형 프로그래밍과 타입 클래스] 마크업 수정 |
||
---|---|---|---|
줄 3: | 줄 3: | ||
====== 비동기 프로그래밍과 Scala ====== | ====== 비동기 프로그래밍과 Scala ====== | ||
- | 비동기(asynchrony)는 어떤 곳에서도 사용될 수 있는, 병행성(concurrency)을 포괄하는 개념입니다. 이 글은 비동기 처리가 무엇인지, | + | 비동기(asynchrony)는 어떤 곳에서도 사용될 수 있는, 병행성(concurrency)을 포괄하는 개념입니다. 이 글은 비동기 처리가 무엇인지, |
===== 소개 ===== | ===== 소개 ===== | ||
줄 12: | 줄 12: | ||
Multithreading <: Asynchrony | Multithreading <: Asynchrony | ||
</ | </ | ||
+ | |||
비동기 연산은 다음과 같이 형(type)을 통해서 표현할 수도 있습니다. | 비동기 연산은 다음과 같이 형(type)을 통해서 표현할 수도 있습니다. | ||
줄 17: | 줄 18: | ||
type Async[A] = (Try[A] => Unit) => Unit | type Async[A] = (Try[A] => Unit) => Unit | ||
</ | </ | ||
+ | |||
여러 개의 '' | 여러 개의 '' | ||
- | - 프로그램의 메인 플로우 밖에서 실행되거나, | + | - (호출자의 입장에서) |
- 작업이 끝나고 나면 실행되는 콜백을 사용함 | - 작업이 끝나고 나면 실행되는 콜백을 사용함 | ||
- | - 결과가 | + | - 결과가 나올 것인지, 혹은 언제 |
- | 비동기가 병행성(concurrency)을 포괄하긴 하지만, 그것이 필연적으로 멀티스레딩을 포괄하는 것이 아님은 매우 중요합니다. 예를 들어, JavaScript에서는 대부분의 I/O 동작이 비동기적일 뿐만 아니라, 아주 무거운 비지니스 로직도 ('' | + | 비동기가 병행성(concurrency)을 포괄하긴 하지만, 그것이 필연적으로 멀티스레딩을 포괄하는 것이 아님은 매우 중요합니다. 예를 들어, JavaScript에서는 대부분의 I/O 동작이 비동기적일 뿐만 아니라, 아주 무거운 비지니스 로직도 ('' |
- | 프로그램에 비동기를 도입하는 일은, 병행성 문제(concurrency problems)를 일으킵니다. 우리는 비동기 연산이 언제 끝날지 절대로 알 수 없어서, 동시에 작동한 여러 개의 비동기 연산의 결과를 모으는 일은 동기화(synchronization)를 필요로 하게 되기 때문입니다. 동기화 작업은 프로그램이 더 이상 동작의 순서에 의존하지 않도 하는 작업이기도 한데, 순서로부터의 독립은 바로 비결정론적 알고리즘의 핵심 요소이기도 합니다. | + | 프로그램에 비동기를 도입하는 일은, 병행성 문제(concurrency problems)를 일으킵니다. 우리는 비동기 연산이 언제 끝날지 절대로 알 수 없어서, 동시에 작동한 여러 개의 비동기 연산의 결과를 모으는 일은 동기화(synchronization)를 필요로 하게 되기 때문입니다. 동기화 작업은 프로그램이 더 이상 동작의 순서에 의존하지 않도록 하는 작업이기도 한데, 순서로부터의 독립은 바로 비결정론적 알고리즘의 핵심 요소이기도 합니다. |
< | < | ||
줄 32: | 줄 34: | ||
</ | </ | ||
- | {{ 비동기_프로그래밍과_scala: | + | {{비동기_프로그래밍과_scala: |
실력 있는 독자라면, | 실력 있는 독자라면, | ||
줄 54: | 줄 56: | ||
def await[A](fa: | def await[A](fa: | ||
</ | </ | ||
+ | |||
그러나 우리는 비동기 처리가 다른 일반적인 함수들과 같다고 가정해서는 안됩니다. 그 이유는 CORBA의 실패로부터도 배울 수 있습니다. | 그러나 우리는 비동기 처리가 다른 일반적인 함수들과 같다고 가정해서는 안됩니다. 그 이유는 CORBA의 실패로부터도 배울 수 있습니다. | ||
줄 75: | 줄 78: | ||
* Scheme의 [[https:// | * Scheme의 [[https:// | ||
* C#, [[https:// | * C#, [[https:// | ||
- | * 런타임에 관리되는 [[https:// | + | * 런타임에 관리되는 [[https:// |
* Erlang과 Akka에 구현된 [[https:// | * Erlang과 Akka에 구현된 [[https:// | ||
* 순서를 지정하고 결과를 통합하는 모나드. Haskell에서 [[https:// | * 순서를 지정하고 결과를 통합하는 모나드. Haskell에서 [[https:// | ||
- | 이렇게 해결법이 많다는 것은, 그 중에 무엇도 일반적인 목적으로 비동기를 처리하기에 적합하지 않다는 것을 의미합니다. 메모리 관리와 | + | 이렇게 해결법이 많다는 것은, 그 중에 무엇도 일반적인 목적으로 비동기를 처리하기에 적합하지 않다는 것을 의미합니다. 메모리 관리와 |
- | > 원주: 경고 | + | > **원주** 경고: 개인적인 의견과 불평입니다. |
> | > | ||
> 어떤 사람들은 Golang과 같은 M:N 플랫폼을 자랑합니다만, | > 어떤 사람들은 Golang과 같은 M:N 플랫폼을 자랑합니다만, | ||
줄 149: | 줄 152: | ||
==== 병렬 실행 (비결정론의 림보) ==== | ==== 병렬 실행 (비결정론의 림보) ==== | ||
- | 위의 ' | + | 위의 ' |
안타깝게도 병렬 실행에서는 일이 조금 복잡해집니다. 아래와 같은 순진한 접근 방법은 완전히 틀렸습니다. | 안타깝게도 병렬 실행에서는 일이 조금 복잡해집니다. 아래와 같은 순진한 접근 방법은 완전히 틀렸습니다. | ||
<code scala> | <code scala> | ||
- | // 영 좋지 못한 | + | // 나쁜 |
def timesFourInParallel(n: | def timesFourInParallel(n: | ||
onFinish => { | onFinish => { | ||
줄 175: | 줄 177: | ||
</ | </ | ||
- | 바로 이것이 비결정론의 실례(實例)입니다. 두 개의 작업이 종료되는 순서가 보장되지 않기 때문에 우리는 동기화를 수행하는 소규모의 상태 기계(state machine)를 구현해야 합니다. | + | 바로 이것이 비결정론의 실제 예시입니다. 두 개의 작업이 종료되는 순서가 보장되지 않기 때문에 우리는 동기화를 수행하는 소규모의 상태 기계(state machine)를 구현해야 합니다. |
먼저 상태 기계의 ADT를 정의합니다. | 먼저 상태 기계의 ADT를 정의합니다. | ||
줄 193: | 줄 195: | ||
<code scala> | <code scala> | ||
- | // JVM에서는 | + | // JVM에서는 |
def timesFourInParallel(n: | def timesFourInParallel(n: | ||
onFinish => { | onFinish => { | ||
줄 301: | 줄 302: | ||
</ | </ | ||
- | > 원주: [[https:// | + | > **원주** [[https:// |
이제 조금 어려워졌나요? | 이제 조금 어려워졌나요? | ||
줄 307: | 줄 308: | ||
==== 재귀 실행 (분노의 StackOverflow) ==== | ==== 재귀 실행 (분노의 StackOverflow) ==== | ||
- | 위의 '' | + | 위의 '' |
말 그대로의 의미입니다. 앞서 작성한 코드를 제네릭한 방법(generic way)으로 다시 작성해 봅시다. | 말 그대로의 의미입니다. 앞서 작성한 코드를 제네릭한 방법(generic way)으로 다시 작성해 봅시다. | ||
줄 409: | 줄 410: | ||
</ | </ | ||
- | 앞서 말씀드린 것과 같이, 강제된 비동기 범위 없이 실행된 '' | + | 앞서 말씀드린 것과 같이, 강제된 비동기 범위 없이 실행된 '' |
===== Future와 Promise ===== | ===== Future와 Promise ===== | ||
- | '' | + | '' |
< | < | ||
줄 420: | 줄 421: | ||
</ | </ | ||
- | > 원주: 필자의 불평입니다. | + | > **원주** 필자의 불평: |
> | > | ||
- | > Future와 Promise에 관한 | + | > Future와 Promise에 관한 [[http:// |
> | > | ||
> " | > " | ||
줄 443: | 줄 444: | ||
// 값 변형 | // 값 변형 | ||
def map[U](f: T => U)(implicit ec: ExecutionContext): | def map[U](f: T => U)(implicit ec: ExecutionContext): | ||
+ | |||
// 연속 실행 ;-) | // 연속 실행 ;-) | ||
def flatMap[U](f: | def flatMap[U](f: | ||
+ | |||
// ... | // ... | ||
} | } | ||
줄 451: | 줄 454: | ||
'' | '' | ||
- | * [[https:// | + | * [[https:// |
- | * [[https:// | + | * [[https:// |
* 단일한 값을 흘려보내고(stream) 나타냅니다(show). 메모아이제이션이 적용되었기 때문입니다. 따라서 작업 완료에 대한 소비자(listener)는 최대 한 번까지만 호출됩니다. | * 단일한 값을 흘려보내고(stream) 나타냅니다(show). 메모아이제이션이 적용되었기 때문입니다. 따라서 작업 완료에 대한 소비자(listener)는 최대 한 번까지만 호출됩니다. | ||
줄 498: | 줄 501: | ||
} | } | ||
</ | </ | ||
- | 역시 아주 쉬워졌습니다. 위의 for-컴프리헨션은 아래와 같이 '' | + | 역시 아주 쉬워졌습니다. 위의 for-comprehension은 아래와 같이 '' |
<code scala> | <code scala> | ||
줄 526: | 줄 529: | ||
<code scala> | <code scala> | ||
- | // 영 좋지 못한 | + | // 나쁜 |
def sum(list: List[Future[Int]])(implicit ec; ExecutionContext): | def sum(list: List[Future[Int]])(implicit ec; ExecutionContext): | ||
async { | async { | ||
줄 547: | 줄 550: | ||
<code scala> | <code scala> | ||
def timesFourInParallel(n: | def timesFourInParallel(n: | ||
- | // Future는 | + | // Future는 |
val fa = timesTwo(n) | val fa = timesTwo(n) | ||
val fb = timesTwo(n) | val fb = timesTwo(n) | ||
줄 565: | 줄 568: | ||
</ | </ | ||
- | 이 방법도 초보자에게는 조금 충격적일 수도 있습니다. 여기서 '' | + | 이 방법도 초보자에게는 조금 충격적일 수도 있습니다. 여기서 '' |
==== 재귀 실행 ==== | ==== 재귀 실행 ==== | ||
줄 630: | 줄 633: | ||
CPU-bound 작업은 [[https:// | CPU-bound 작업은 [[https:// | ||
- | > 원주: 이 벤치마크 결과는 한정적입니다. 여전히 '' | + | > **원주** 이 벤치마크 결과에는 한계가 있습니다. 여전히 '' |
===== Task와 Scala의 IO 모나드 ===== | ===== Task와 Scala의 IO 모나드 ===== | ||
- | '' | + | '' |
- | The [[https:// | + | [[https:// |
- | > The '' | + | > 또 '' |
> | > | ||
- | > This is a matter of debate, as Scalaz | + | > Scalaz은 동기 작업만을 위한 별도의 |
> | > | ||
- | > On the JVM, with the Scalaz | + | > JVM를 위한 |
- | In summary the '' | + | 요약하자면 |
- | * models lazy & asynchronous evaluation | + | * Lazy하고 비동기적인 연산을 표현합니다. |
- | * models a producer | + | * 하나 혹은 여러 개의 소비자(consumer)에게 오직 하나의 값만을 전달하는 생산자(producer)를 표현합니다. |
- | * it is lazily evaluated, so compared with '' | + | * Lazy evaluation을 수행하기 때문에, '' |
- | * it is not memoized by default on evaluation, but the Monix Task can be | + | * 메모아이제이션이 가능합니다. |
- | * doesn’t necessarily execute on another logical thread | + | * 반드시 다른 논리 스레드에서 실행되어야 하는 것은 아닙니다. |
- | Specific to the Monix implementation: | + | 특히 |
- | * allows for cancelling of a running computation | + | * 연산을 도중에 취소할 수 있습니다. |
- | * never blocks any threads in its implementation | + | * 그 어떤 스레드도 절대 봉쇄(block)하지 않습니다. |
- | * does not expose any API calls that can block threads | + | * 스레드를 봉쇄(block)할 가능성이 있는 그 어떤 |
- | * all async operations are stack safe | + | * 모든 비동기 작업은 스택-안전(stack-safe)합니다. |
- | * A visual representation of where '' | + | |
- | ^ ^ Eager ^ Lazy ^ | + | Monix의 구현에서 '' |
- | | Synchronous | + | |
- | | ::: | ::: | [[https:// | + | |
- | | Asynchronous | (A => Unit) => Unit | (A => Unit) => Unit | | + | |
- | | ::: | Future[A] | + | |
- | ==== Sequencing ==== | + | ^ ^ Eager ^ Lazy ^ |
+ | | 동기 작업 | ||
+ | | ::: | ::: | [[https:// | ||
+ | | 비동기 작업 | (A => Unit) => Unit | (A => Unit) => Unit | | ||
+ | | ::: | Future[A] | ||
- | Redefining our function from [[비동기_프로그래밍과_scala&do=# | + | ==== 연속 실행 ==== |
+ | |||
+ | [[비동기_프로그래밍과_scala# | ||
<code scala> | <code scala> | ||
import monix.eval.Task | import monix.eval.Task | ||
+ | |||
def timesTwo(n: Int): Task[Int] = | def timesTwo(n: Int): Task[Int] = | ||
Task(n * 2) | Task(n * 2) | ||
- | + | ||
- | // Usage | + | // 사용법 |
{ | { | ||
- | // Our ExecutionContext | + | // 계산되는 시점에 |
import monix.execution.Scheduler.Implicits.global | import monix.execution.Scheduler.Implicits.global | ||
- | + | ||
- | timesTwo(20).foreach { result => println(s" | + | timesTwo(20).foreach { result => println(s" |
- | // | + | // |
} | } | ||
</ | </ | ||
- | The code seems to be almost the same as the '' | + | 이 코드는 |
- | Now to do sequencing like in [[비동기_프로그래밍과_scala# | + | 이제 |
<code scala> | <code scala> | ||
def timesFour(n: | def timesFour(n: | ||
for (a <- timesTwo(n); | for (a <- timesTwo(n); | ||
+ | |||
// Usage | // Usage | ||
{ | { | ||
- | // Our ExecutionContext | + | // 계산되는 시점에 |
import monix.execution.Scheduler.Implicits.global | import monix.execution.Scheduler.Implicits.global | ||
- | + | ||
- | timesFour(20).foreach { result => println(s" | + | timesFour(20).foreach { result => println(s" |
- | // | + | // |
} | } | ||
</ | </ | ||
- | And just like with the '' | + | '' |
<code scala> | <code scala> | ||
줄 713: | 줄 717: | ||
</ | </ | ||
- | ==== Parallelism | + | ==== 병렬 실행 |
- | The story for '' | + | '' |
- | But first, translating the sample using '' | + | 그러나 다음과 같이 단순히 |
<code scala> | <code scala> | ||
- | // BAD SAMPLE | + | // 나쁜 예 (이 코드는 여전히 연속 실행됩니다) |
def timesFour(n: | def timesFour(n: | ||
- | // Will not trigger execution b/c Task is lazy | + | // Task는 lazy하게 계산되므로 여기서는 아직 계산되지 않습니다. |
val fa = timesTwo(n) | val fa = timesTwo(n) | ||
val fb = timesTwo(n) | val fb = timesTwo(n) | ||
- | // Evaluation will be sequential b/c of laziness | + | // Lazy evaluation으로 인해 값이 연속으로 계산됩니다. |
for (a <- fa; b <- fb) yield a + b | for (a <- fa; b <- fb) yield a + b | ||
} | } | ||
</ | </ | ||
- | In order to achieve parallelism %%'' | + | '' |
<code scala> | <code scala> | ||
줄 737: | 줄 741: | ||
</ | </ | ||
- | Oh, does '' | + | '' |
- | ==== Recursivity | + | ==== 재귀 실행 |
- | Task is recursive and stack-safe (in '' | + | '' |
- | The '' | + | '' |
<code scala> | <code scala> | ||
줄 751: | 줄 755: | ||
.map(_.reverse) | .map(_.reverse) | ||
} | } | ||
- | + | ||
- | // Invocation | + | // 사용법 |
{ | { | ||
- | // Our ExecutionContext | + | // 계산되는 시점에 |
import monix.execution.Scheduler.Implicits.global | import monix.execution.Scheduler.Implicits.global | ||
+ | |||
sequence(List(timesTwo(10), | sequence(List(timesTwo(10), | ||
// => List(20, 40, 60) | // => List(20, 40, 60) | ||
줄 762: | 줄 766: | ||
</ | </ | ||
- | ===== Functional Programming and Type-classes ===== | + | ((**역주** 이 아래부터 6장에서는, |
- | When working with well grown functions such as '' | + | ===== 함수형 프로그래밍과 타입 클래스 ===== |
- | This is the great achievement of Haskell' | + | '' |
- | And can we build interfaces that abstract over such types as '' | + | 이것이 바로 Hakell의 |
- | Yes we can, we've already seen that '' | + | 그렇다면 |
- | Fortunately Scala is one of the very few languages capable of higher kinded types and with the ability to encode [[https:// | + | 가능합니다. 우리는 이미 순차 실행을 추상화하는 '' |
- | > Author's Rant: The dreaded | + | 마침 Scala는 상류 타입(higher kinded types)을 지원하는 몇 안되는 언어에 속하고 [[https:// |
+ | |||
+ | > **원주** 저자의 분노: '' | ||
> | > | ||
- | > But this is a disservice to both the Scala language and its users. In other languages they are only design patterns that are hard to explain primarily because they can't be expressed as types. You can count the languages having this expressive capability with one hand. And users suffer because in case of trouble they don't know how to search for existing literature on the subject, having been deprived of learning the correct jargon. | + | > 하지만 그러한 설명 방식은 |
> | > | ||
- | > I also feel this is a flavor of [[https:// | + | > 또 저는 이것이 모르는 것에 대한 본능적인 공포에서 나오는 일종의 |
- | ==== Monad (Sequencing and Recursivity) ==== | + | ==== Monad (연속 실행과 재귀 실행) ==== |
- | This article is not about explaining Monads. There are other great articles for that. But if you're looking to build an intuition, here's another one: in the context of data types such as '' | + | 이 글의 목적은 모나드에 대해 설명하는 것이 아닙니다. 모나드에 관해서는 다른 좋은 글들이 있습니다. 모나드는 잘 모르지만 비동기 프로그래밍에 대한 감을 쌓기 위해서 이 글을 읽고 있다면 적어도 알아두어야 할 것이 있습니다. '' |
- | > [[https:// | + | < |
+ | 관찰 결과: 명령형 언어(imperative language)로 병행성(concurrency)을 다루는 프로그래머들은 ";" | ||
+ | <cite> | ||
+ | </ | ||
- | A simple encoding of the '' | + | '' |
<code scala> | <code scala> | ||
- | // We shouldn' | + | // 이 줄을 적지 않아도 된다면 좋을텐데 말이죠 |
import scala.language.higherKinds | import scala.language.higherKinds | ||
+ | |||
trait Monad[F[_]] { | trait Monad[F[_]] { | ||
- | /** Constructor | + | /** 생성자 |
- | * monadic context). Also part of `Applicative`, see below. | + | * `Applicative`의 일부이기도 합니다. 아래를 보세요. |
*/ | */ | ||
def pure[A](a: A): F[A] | def pure[A](a: A): F[A] | ||
- | + | ||
- | /** FTW */ | + | /** 빰 */ |
def flatMap[A, | def flatMap[A, | ||
} | } | ||
</ | </ | ||
- | And providing an implementation for '' | + | 이어서 |
<code scala> | <code scala> | ||
줄 825: | 줄 834: | ||
</ | </ | ||
- | This is really powerful stuff. We can now describe a generic function that works with '' | + | '' |
<code scala> | <code scala> | ||
- | /** Calculates the N-th number in a Fibonacci series. */ | + | /** 피보나치 수열의 |
def fib[F[_]](n: | def fib[F[_]](n: | ||
def loop(n: Int, a: BigInt, b: BigInt): F[BigInt] = | def loop(n: Int, a: BigInt, b: BigInt): F[BigInt] = | ||
줄 835: | 줄 844: | ||
else loop(n - 1, b, a + b) | else loop(n - 1, b, a + b) | ||
} | } | ||
+ | |||
loop(n, BigInt(0), BigInt(1)) | loop(n, BigInt(0), BigInt(1)) | ||
} | } | ||
- | + | ||
- | // Usage: | + | // 사용법 |
{ | { | ||
- | // Needed in scope | + | // 유효범위(scope)를 만듭니다. |
import FutureMonad.instance | import FutureMonad.instance | ||
import scala.concurrent.ExecutionContext.Implicits.global | import scala.concurrent.ExecutionContext.Implicits.global | ||
- | + | ||
- | // Invocation | + | // 실행 |
- | fib[Future](40).foreach(r => println(s" | + | fib[Future](40).foreach(r => println(s" |
- | // | + | // |
} | } | ||
</ | </ | ||
- | > PRO-TIP: this is just a toy example. For getting serious, see [[http:// | + | > **원주** 이 코드는 그저 장난 수준입니다. 본격적인 프로젝트를 보고 싶다면 Typelevel의 |
- | ==== Applicative (Parallelism) ==== | + | ==== Applicative (병렬 실행) ==== |
- | Monads define sequencing of operations, but sometimes we want to compose the results of computations that are independent of each other, that can be evaluated at the same time, possibly in parallel. There's also a case to be made that applicatives are more composable than monads 😏 | + | 모나드는 실행의 순서를 정의합니다. 하지만 동시에 실행 가능한 서로 독립적인 연산들의 결과를 합쳐야 할 때도 있습니다. 그런 경우에는 |
- | Let's expand our mini Typeclassopedia | + | 먼저 간단한 타입백과사전(Typeclassopedia)을 만들어 봅시다. ((**역주** ' |
<code scala> | <code scala> | ||
trait Functor[F[_]] { | trait Functor[F[_]] { | ||
- | /** I hope we are all familiar with this one. */ | + | /** 이 코드는 독자 여러분 모두 이해하리라고 믿습니다. */ |
def map[A, | def map[A, | ||
} | } | ||
+ | |||
trait Applicative[F[_]] extends Functor[F] { | trait Applicative[F[_]] extends Functor[F] { | ||
- | /** Constructor | + | /** 생성자 |
def pure[A](a: A): F[A] | def pure[A](a: A): F[A] | ||
- | + | ||
- | /** Maps over two references at the same time. | + | /** 두 참조에 대해서 동시에 map을 실행합니다. |
* | * | ||
- | * In other implementations the applicative operation is `ap`, | + | * 다른 구현체에서는 Applicative 연산자가 |
- | * but `map2` | + | * 하지만 |
*/ | */ | ||
def map2[A, | def map2[A, | ||
} | } | ||
+ | |||
trait Monad[F[_]] extends Applicative[F] { | trait Monad[F[_]] extends Applicative[F] { | ||
def flatMap[A, | def flatMap[A, | ||
줄 882: | 줄 891: | ||
</ | </ | ||
- | And to expand our '' | + | 이어서 |
<code scala> | <code scala> | ||
- | // Supplying an instance for Future isn't clean, ExecutionContext needed | ||
class FutureMonad(implicit ec: ExecutionContext) | class FutureMonad(implicit ec: ExecutionContext) | ||
extends Monad[Future] { | extends Monad[Future] { | ||
+ | |||
def pure[A](a: A): Future[A] = | def pure[A](a: A): Future[A] = | ||
Future.successful(a) | Future.successful(a) | ||
+ | |||
def flatMap[A, | def flatMap[A, | ||
fa.flatMap(f) | fa.flatMap(f) | ||
+ | |||
def map2[A, | def map2[A, | ||
- | // For Future there' | + | // flatMap에 기반하지 않은 구현체를 위한 함수이지만, |
- | // not based on flatMap, but that's not the case for Task ;-) | + | // Task는 해당되지 않습니다 |
for (a <- fa; b <- fb) yield f(a,b) | for (a <- fa; b <- fb) yield f(a,b) | ||
} | } | ||
+ | |||
object FutureMonad { | object FutureMonad { | ||
implicit def instance(implicit ec: ExecutionContext): | implicit def instance(implicit ec: ExecutionContext): | ||
줄 907: | 줄 915: | ||
</ | </ | ||
- | So we can now define generic functions based on '' | + | 이제 |
<code scala> | <code scala> | ||
줄 919: | 줄 927: | ||
</ | </ | ||
- | > PRO-TIP: worth repeating, this is just a toy example. For getting serious, see [[http:// | + | > **원주** 다시 한 번 강조하지만, 이 코드는 단지 장난 수준일 뿐입니다. 본격적인 프로젝트를 보고 싶다면 |
- | ==== Can We Define a Type-class for Async Evaluation? ==== | + | ==== 비동기 계산을 위한 타입 클래스를 정의할 수 있을까요? ==== |
- | Missing from above is a way to actually trigger an evaluation and get a value out. Thinking of Scala' | + | 지금까지 살펴 본 내용에서 빠진 부분은 실제로 계산을 시작하고 결괏값을 받아오는 방법입니다. Scala의 '' |
- | The [[https:// | + | [[https:// |
<code scala> | <code scala> | ||
줄 933: | 줄 941: | ||
</ | </ | ||
- | This looks like our initial | + | 우리가 처음에 만들었던 |
+ | |||
+ | 하지만 이것은 다음의 이유로 진짜 타입 클래스라고 하기는 어렵습니다. | ||
- | However, this is not a real type-class because: | + | * 이 구현은 불규칙적(lawless)입니다. 그러나 이것만으로 타입 클래스가 아니라고 하기는 곤란합니다. ('' |
+ | * [[비동기_프로그래밍과_scala# | ||
- | - it is lawless and while that's not enough to disqualify it (after all, useful lawless type-classes like '' | + | 실행 문맥(execution context)을 지정하는 방법은 구현 방식에 따라 다릅니다. Java는 '' |
- | - as shown in [[비동기_프로그래밍과_scala# | + | |
- | And such an execution context is different from implementation to implementation. Java will use '' | + | 우리는 이와 같은 전략을 |
- | We could apply the same strategy as with '' | + | 그래서 처음 주어진 질문(" |
- | So I'm personally not sure - if you have suggestions for what should be introduced in [[http:// | + | 여러분이 이 사고 실험을 재밌게 즐기셨다면 좋겠습니다. 설계는 늘 즐겁죠. 😎 |
- | I do hope you enjoyed this thought experiment, designing things is fun 😎 | + | ===== 올바른 도구를 고르자 ===== |
- | ===== Picking the Right Tool ===== | + | 어떤 추상화는 다른 추상화보다 더 일반적인 목적을 가지고 있습니다. 저는 개인적으로 " |
- | Some abstractions are more general purpose than others and personally I think the mantra of " | + | 이 시점에서 우리는 Rúnar Bjarnason의 [[https:// |
- | That said, there's this wonderful presentation by Rúnar Bjarnason called [[https:// | + | 이미 말했듯이, 병행성(concurrency)을 처리함에 있어 모든 상황에 일반적으로 적용될 수 있는 |
- | As said, there is no silver bullet that can be generally applied for dealing with concurrency. The more high-level the abstraction, | + | 그리고 이 두 가지 규칙을 마음에 새기도록 합시다. |
- | Also learn by heart these 2 very simple rules: | + | * 콜백, 스레드와 스레드 봉쇄(block)를 지양하자. 오류를 일으키기 쉽고 결과를 합치는 것도 불가능하다. |
+ | * 병행성(concurrency)을 지양하자. 그것은 전염병과 같다. | ||
- | avoid dealing with callbacks, threads and locks, because they are very error prone and not composable at all | + | 마지막으로 이것만 말하겠습니다. 병행성(concurrency) 전문가들은 애초에 병행성이 생기지 않도록 하는 데 그 누구보다 전문적입니다. |
- | avoid concurrency like the plague it is | + | |
- | And let me tell you, concurrency experts are first of all experts in avoiding | + |