對於要內換理解,你真的只需要定義map
和flatMap
方法,它使用的類型返回相同類型的實例。在語法上,編譯器將for-comprehension轉換爲一系列flatMap
s,接着是yield
的最後一個map
。只要這些方法具有適當的簽名,它就可以工作。
我真的不知道你有你的例子後在做什麼,但這裏是一個簡單的例子,它等效於Option
:
sealed trait MaybeInt {
def map(f: Int => Int): MaybeInt
def flatMap(f: Int => MaybeInt): MaybeInt
}
case class SomeInt(i: Int) extends MaybeInt {
def map(f: Int => Int): MaybeInt = SomeInt(f(i))
def flatMap(f: Int => MaybeInt): MaybeInt = f(i)
}
case object NoInt extends MaybeInt {
def map(f: Int => Int): MaybeInt = NoInt
def flatMap(f: Int => MaybeInt): MaybeInt = NoInt
}
我有兩個子類型(我一個共同的特點雖然可以有我想要的儘可能多)。共同特徵MaybeInt
強制每個子類型符合map
/flatMap
接口。
scala> val maybe = SomeInt(1)
maybe: SomeInt = SomeInt(1)
scala> val no = NoInt
no: NoInt.type = NoInt
for {
a <- maybe
b <- no
} yield a + b
res10: MaybeInt = NoInt
for {
a <- maybe
b <- maybe
} yield a + b
res12: MaybeInt = SomeInt(2)
此外,您可以添加foreach
和filter
。如果你也想解決這個(沒有收益率):
for(a <- maybe) println(a)
你會添加foreach
。如果你想使用if
後衛:
for(a <- maybe if a > 2) yield a
您需要filter
或withFilter
。
完整的例子:
sealed trait MaybeInt { self =>
def map(f: Int => Int): MaybeInt
def flatMap(f: Int => MaybeInt): MaybeInt
def filter(f: Int => Boolean): MaybeInt
def foreach[U](f: Int => U): Unit
def withFilter(p: Int => Boolean): WithFilter = new WithFilter(p)
// Based on Option#withFilter
class WithFilter(p: Int => Boolean) {
def map(f: Int => Int): MaybeInt = self filter p map f
def flatMap(f: Int => MaybeInt): MaybeInt = self filter p flatMap f
def foreach[U](f: Int => U): Unit = self filter p foreach f
def withFilter(q: Int => Boolean): WithFilter = new WithFilter(x => p(x) && q(x))
}
}
case class SomeInt(i: Int) extends MaybeInt {
def map(f: Int => Int): MaybeInt = SomeInt(f(i))
def flatMap(f: Int => MaybeInt): MaybeInt = f(i)
def filter(f: Int => Boolean): MaybeInt = if(f(i)) this else NoInt
def foreach[U](f: Int => U): Unit = f(i)
}
case object NoInt extends MaybeInt {
def map(f: Int => Int): MaybeInt = NoInt
def flatMap(f: Int => MaybeInt): MaybeInt = NoInt
def filter(f: Int => Boolean): MaybeInt = NoInt
def foreach[U](f: Int => U): Unit =()
}
它會更好,而不是使用Scallaz單子類型定義你自己的? –
'filter' api也需要「解包」單子物品。例如,如果您使用與MaybeInt類似的MaybeAB,但對於'case class AB(a:Int,b:Int)',則需要使用filter來執行for(AB(a,b)< - maybAB)... ' –