9장 반응형 웹 애플리케이션
3줄 요약
Iteratee: 반복할 놈
Enumerator: 열거할 놈
Enumeratee: 열거받을 놈?? Pipe line(Adapter) between Enumerator and Iteratee
Iteratee
Non-blocking 그리고 Asynchronous 한 데이터 청크 소비자.
- Enumerator 가 제공하는 동일한 타입의 데이터 청크를 소비
Iteratee[E, A]
- E: 입력 데이터 타입. Enumerator[E] 또한 같은 데이터 타입이여야만 한다.
- A: 결과 데이터 타입.
val members = Enumerator("Clint", "Jayden", "Ork", "Seed")
val filterWithE = Iteratee.fold(List[String]())(
(filtered: List[String], elem: String) => if (elem.contains("e")) elem :: filtered else filtered)
val result = members run filterWithE
=> scala.concurrent.Future[List[String]]
result onComplete println
=> Success(List(Seed, Jayden))
위 예제에서 Iteratee.fold 는 2가지를 포함하고 있다.
- 초기 상태: List[String()]
- 함수: 이전상태(filtered)와 입력(elem)
Iteratee 는 3가지 상태를 가지는 State Machine 이다.
- Done[E,A] (a: A, remaining: Input[E])
- a:A 이전상태
- remaining: Input[E] E 타입의 남은 데이터 청크
- Cont[E, A] (k: Input[E] => Iteratee[E, A])
- k 수행할 함수
- Error[E] (msg: String, input: Input[E])
- msg 에러 메세지
- input 남은 데이터 청크
State 가 변화하는 모습은 아래 예제에서 더 자세히 구현되어 있다.
def wordsWithE: Iteratee[String, List[String]] = {
def step(filtered:List[String])(input:Input[String]): Iteratee[String, List[String]] = input match {
case Input.EOF | Input.Empty => Done(filtered, Input.EOF)
case Input.El(elem) =>
if (elem.contains("e")) Cont[String, List[String]](i => step(elem::filtered)(i))
else Cont[String, List[String]](i => step(filtered)(i))
}
Cont[String, List[String]](i => step(List[String]())(i))
}
val result = members run wordsWithE
=> scala.concurrent.Future[List[String]]
result onComplete println
=> Success(List(Seed, Jayden))
step 함수를 마치 꼬리재귀함수처럼 반복을 수행하는 것을 알 수 있다.
Enumerator
trait Enumerator[E] {
/**
* Apply this Enumerator to an Iteratee
*/
def apply[A](i: Iteratee[E, A]): Future[Iteratee[E, A]]
}
E 타입의 데이터 청크를 Iteratee 에게 제공하는 생산자.
별거 없다. ㅋㅋ.
뭔가 정리할게 생각나면 더 쓰겠다.
Enumeratee
Enumerator 와 Iteratee 사이의 Pipe Adapter