设定:
import scalaz._; import Scalaz._
case class Foo(map: Map[Int, String] = Map.empty, set: Set[Int] = Set.empty)
val `foo.map`: Lens[Foo, Map[Int, String]] = Lens.lensu((f, m) => f.copy(map = m), _.map)
val `foo.set`: Lens[Foo, Set[Int]] = Lens.lensu((f, s) => f.copy(set = s), _.set)
case class Bar(cond: Boolean)
val listOfBars = List(Bar(true), Bar(false))
现在没有编译
listOfBars.runTraverseS[Foo, Unit](Foo()) { bar =>
if (bar.cond)
`foo.map` += 1 -> "a"
else
`foo.set` += 2
}
此问题是Lens[Foo, Map[K, V]].+=
返回State[Foo, Map[K, V]]
而Lens[Foo, Set[A]].+=
返回State[Foo, Set[A]]
。回想一下,State[S, A]
A
是不变的
然而,这确实编译:
listOfBars.runTraverseS[Foo, Unit](Foo()) { bar =>
for {
_ <- State.init[Foo]
x <- {
if (bar.cond)
`foo.map` += 1 -> "a"
else
`foo.set` += 2
}
} yield ()
}
x
的类型是什么? (IDEA说它是type $_1
或类似的东西 - 我认为它不是可表示的类型)如果我将yield ()
更改为yield println(x)
并打印出for for comprehension的输出,我会得到:
Map(1 -> a)
Set(2)
(Foo(Map(1 -> a),Set(2)),List((), ()))
答案 0 :(得分:0)
问题是if表达式的类型
if (bar.cond)
`foo.map` += 1 -> "a"
else
`foo.set` += 2
是State[Foo, Serializable]
或类似的(scala可以统一Map
和Set
类型的公共基本类型);但是runTraverseS[Foo, Unit]
期望State[Foo, Unit]
(声明为Unit
),并且这些类型无法统一。
解决方案很简单,只需将结果映射到“void”,就像这样:
val unit = ()
listOfBars.runTraverseS[Foo, Unit](Foo()) { bar =>
(if (bar.cond)
`foo.map` += 1 -> "a"
else
`foo.set` += 2
).map(Function.const(unit))
}
这实际上是代码的“for”版本(它只是删除值x
,其类型类似于State[Foo, Serializable]
。