
http4s 오픈소스 프로젝트에서 다음 이슈에 대한 작업을 진행했다.
이슈: https://github.com/http4s/http4s/issues/2371
chuwy가 제기한 문제 분석
Stream[F, A] 를 인코딩한 결과가 올바른 JSON 배열로 표현되는 것.
{"name": "Bob"}{"name": "Fred"} 와 같이 JSON 배열 문법을 갖추지 않아서 다루기 어려움.[{"name": "Bob"},{"name": "Fred"}] rossabaker 의 보충 의견For arbitrary A, we don't necessarily have a way to concatenate A in a reasonable way. I'd say it should depend on a Monoid[A], but that would defeat streaming. Maybe the streaming encoder should be explicit with start, middle, and end delimiters, and then we could provide implicit ones that do the right thing for Byte, String, Json, and others that come to mind.
언급한 것들을 하나씩 분석해보자.
For arbitrary A, we don't necessarily have a way to concatenate A in a reasonable way.
임의의 타입 A 에 대해, 이것을 효과적으로 이어붙일만한 수단을 갖고 있지 않다.
I'd say it should depend on a Monoid[A], but that would defeat streaming.
Monoid[A]가 무엇인지 알아보자. 개념적으로 무엇을 뜻하고 어떤 역할을 하는 것인지 알아보자.
Monoid는 이항 연산을 추상화 한 개념이다. 이항 연산은 항이 두개인 연산이다. 예를 들어, 3 x 5 라는 연산이 있을때, 3과 5가 각 항이 되어 이항 연산이 된다.
Scala 에서는 Monoid를 타입으로 정의 할 수 있다.
다음은 Typelevel에서 Monoid를 정의하고 있는 방식이다.
trait Semigroup[A] {
def combine(x: A, y: A): A
}
trait Monoid[A] extends Semigroup[A] {
def empty: A
}
combine은 결합연산, empty는 항등원을 나타낸다.
항등원은 결합연산을 했을때, 원래 값이 나오게 하는 것이다. 따라서, 항등원 empty는 다음과 같은 조건을 만족해야 한다.
combine(x, empty) = combine(empty, x) = x
이제, Int 타입의 덧셈에 대한 Monoid 구현체를 만들어 볼 수 있다.
import cats.Monoid
implicit val intAdditionMonoid: Monoid[Int] = new Monoid[Int] {
def empty: Int = 0
def combine(x: Int, y: Int): Int = x + y
}
Monoid 가 Stream 을 연결하기에 적합하지 않은 이유Stream 은 지연된 배열로 이해할 수 있다. 즉, 모든 원소에 대한 평가가 일어나지 않았다. Monoid 는 이항 연산이기 때문에, 기본적으로 두개의 항을 필요로 한다. 즉, 두항 모두가 평가된 상태여야지만 결합될 수 있기 때문에, Stream 의 원소를 모두 평가한 후에 결합하는 방식을 취해야한다.