Ваш код haskell и ваш код scala не эквивалентны:
do
arg <- s
return . toIntSafe $ arg
соответствует
for {
arg <- args
} yield toIntSafe(arg)
Который отлично компилируется.
Чтобы понять, почему ваш пример не компилируется, мы можем его отключить:
for {
arg <- args
result <- toIntSafe(arg)
} yield result
=
args.flatMap { arg =>
toIntSafe(arg).map {result => result}
}
Сейчас смотрим на типы:
args: List[String]
args.flatMap: (String => List[B]) => List[B]
arg => toIntSafe(arg).map {result => result} : String => ErrorM[Int]
Что показывает проблему. flatMap
ожидает функцию, возвращающую List
, но вы даете ему функцию, возвращающую ErrorM
.
код на Haskell в соответствии с:
do
arg <- s
result <- toIntSafe arg
return result
не скомпилируется ни по одной и той же причине: пытается связать две разные монады, List и Either.
A для понимания в scala will или выражение do в haskell будет работать только для одной и той же монады, лежащей в основе, потому что они оба в основном являются синтаксическими переводами в серии flatMap
s и >>=
s соответственно. И те еще должны проверить тип.
Если вы хотите составить монады, то вы можете использовать монадные преобразователи (EitherT), хотя в вышеприведенном примере я не думаю, что вы хотите этого, поскольку вы действительно хотите упорядочить в конце.
Наконец, на мой взгляд, самый элегантный способ выразить ваш код:
def convert(args: List[String]) = args.traverse(toIntSafe)
Поскольку map
, за которым следует sequence
, составляет traverse