Tôi đang sử dụng EitherT của Scalaz 7 để xây dựng cho sự hiểu biết pha trộn trạng thái và \ /. Càng xa càng tốt; Tôi nhận được nội dung cơ bản:Cách trả lại một tuple bên trong EitherT
State[MyStateType, MyLeftType \/ MyRightType]
và cho phép tôi xây dựng hiểu thấu có biến số đẹp ở bên trái của < -.
Nhưng tôi không thể tìm ra cách trả về bộ dữ liệu từ một hành động của tiểu bang. Kết quả duy nhất chỉ là tốt - trong đoạn code dưới đây, "val comprehension" là chính xác những gì tôi muốn xảy ra.
Nhưng mọi thứ sẽ sụp đổ khi tôi muốn trả lại một bộ dữ liệu; "val otherComprehension" sẽ không cho phép tôi làm
(a, b) <- comprehension
Có vẻ như nó dự kiến bên trái của \/là một Monoid và tôi không hiểu tại sao. Tôi đang thiếu gì?
(Scalaz 7 2.0.0-THÔNG SỐ CHUNG, Scala 2.10.2)
object StateProblem {
case class MyStateType
case class MyRightType
case class MyLeftType
type StateWithFixedStateType[+A] = State[MyStateType, A]
type EitherTWithFailureType[F[+_], A] = EitherT[F, MyLeftType, A]
type CombinedStateAndFailure[A] = EitherTWithFailureType[StateWithFixedStateType, A]
def doSomething: CombinedStateAndFailure[MyRightType] = {
val x = State[MyStateType, MyLeftType \/ MyRightType] {
case s => (s, MyRightType().right)
}
EitherT[StateWithFixedStateType, MyLeftType, MyRightType](x)
}
val comprehension = for {
a <- doSomething
b <- doSomething
} yield (a, b)
val otherComprehension = for {
// this gets a compile error:
// could not find implicit value for parameter M: scalaz.Monoid[com.seattleglassware.StateProblem.MyLeftType]
(x, y) <- comprehension
z <- doSomething
} yield (x, y, z)
}
Edit: Tôi đã thêm bằng chứng cho thấy MyLeftType là một đơn nguyên, mặc dù nó không phải. Trong mã thật của tôi, MyLeftType là một lớp trường hợp (gọi tắt là EarlyReturn), vì vậy tôi có thể cung cấp một số không, nhưng thêm chỉ hoạt động nếu một trong các đối số là một số không:
implicit val partialMonoidForEarlyReturn = new Monoid[EarlyReturn] {
case object NoOp extends EarlyReturn
def zero = NoOp
def append(a: EarlyReturn, b: => EarlyReturn) =
(a, b) match {
case (NoOp, b) => b
case (a, NoOp) => a
case _ => throw new RuntimeException("""this isnt really a Monoid, I just want to use it on the left side of a \/""")
}
}
Tôi không thuyết phục đây là một ý tưởng hay, nhưng nó giải quyết được vấn đề.
Có điều gì đó kỳ lạ xảy ra theo cách thức 2.10.1+ xóa bỏ 'for'-comprehension ở đây — xem [câu hỏi này] (http://stackoverflow.com/q/17424763/334519) cho một phiên bản đơn giản của cùng một vấn đề. –
Và để rõ ràng, điều này xảy ra vì việc lọc 'EitherT' (hoặc' \/') yêu cầu một cá thể monoid ở phía bên trái, và vì lý do nào đó, 2.10.2 đang gắn một thao tác lọc trong' 'này. –