以下是一个玩具示例,展示现实生活遗留方法的形状怪异和问题的要点。
正如您可以看到anotherFunc
,在personList
上展开后,将展开类型扩展为\/[Throwable,List[\/[Throwable,String]]]
,这不是预期的返回类型,而是map
ing personList
的效果。再次,anotherFunc
中所示的内容仅用于演示目的(实际上有更多有意义的事情发生而不是Option("fakeString")
或其中任何一个。
要求是map
personList
,如果是right
,则对List[Person]
返回的right
的每个元素执行某些操作(从anotherFunc
返回分离)。
如何简化/展平\/[Throwable,List[String]]
的返回类型(返回case class Person(name :String)
def personList : \/[Throwable,List[Person]] ={
\/.fromTryCatch{
List(Person("John"))
}
}
def anotherFunc : \/[Throwable,List[\/[Throwable,String]]]= {
personList.map{ pl =>
pl.map{p =>
for{
s <- Option("fakeString").\/>(new Throwable("not found"))
} yield s
}
}
}
)。可能正在使用其他组合器?
{{1}}
答案 0 :(得分:8)
诺亚的回答基本上是正确的,但你应该总是使用traverse
而不是map
后跟sequence
- 这两个是等价的,但前者更清晰,效率更高一些:
def anotherFunc: Throwable \/ List[String] = personList.flatMap { pl =>
pl.traverseU { p =>
for {
// I assume you're doing something more interesting here...
s <- Option("fakeString").\/>(new Throwable("not found"))
} yield s
}
}
我这是一个答案而不是评论,但是,因为还有另一种方法可以解决这种在某些情况下更优雅的问题。如果您正在使用析取列表进行大量工作,则可以使用ListT
monad转换器使其看起来像是在处理单个级别:
type OrThrowable[A] = Throwable \/ A
def personList = ListT[OrThrowable, Person](
\/.fromTryCatch { List(Person("John")) }
)
def anotherFunc: ListT[OrThrowable, String] = personList.flatMap { p =>
Option("fakeString").\/>(new Throwable("not found")).liftM[ListT]
}
现在只需使用anotherFunc.run
来获得Throwable \/ List[Person]
,这与您当前的代码完全相同(但更简洁)。
答案 1 :(得分:5)
如果您flatMap
personList
然后sequenceU
内部列表,您基本上可以flatten
返回类型:
def anotherFunc: \/[Throwable, List[String]] = {
personList.flatMap(
pl =>
pl.map(
p =>
for {
s <- Option("fakeString").\/>(new Throwable("not found"))
} yield s
).sequenceU
)
}
Intellij用一些红线抱怨这个但是它为我\/-(List(fakeString))
正确编译并打印出来。
在我看来,这个版本看起来有点漂亮:
def anotherFunc2: \/[Throwable, List[String]] = {
for {
pl <- personList
res <- pl.map(p => Option("fakeString").\/>(new Throwable("not found"))).sequenceU
} yield res
}