Scala: параметры без имен в функции со списком и опцией - PullRequest
4 голосов
/ 22 мая 2019

2 разных примера, первый работает:

  import cats.syntax.either._
  val e = 10.asRight[String]
  def i2s(i:Int):String = i.toString
  e.map(i => List(i2s(i))) //using explicit parameter
  e.map(List(i2s(_)))   //using no-name _ parameter

Теперь тот же пример с Option не скомпилирован:

e.map(Option(i2s(_))) 

Ошибка:

Error:(27, 15) type mismatch;
 found   : Option[Int => String]
 required: Int => ?
  e.map(Option(i2s(_)))

С явным параметром он работает нормально:

e.map(i => Option(i2s(i)))

В обоих случаях метод apply вызывается с помощью List и Option. List.apply подпись:

 def apply[A](xs: A*): List[A] = ???

Option.apply подпись:

 def apply[A](x: A): Option[A]

Пожалуйста, объясните разницу.

Ответы [ 2 ]

4 голосов
/ 22 мая 2019

Оба ваших List примера компилируются, но они не означают одно и то же и не дают одинаковых результатов.

e.map(i => List(i2s(i))) //res0: scala.util.Either[String,List[String]] = Right(List(10))
e.map(List(i2s(_)))      //java.lang.IndexOutOfBoundsException: 10

Первый легко понять, так что же происходит со вторым?

Что происходит, так это то, что вы используете eta extension для создания Int => String функции из метода i2s(). Затем вы заполняете List этой единственной функцией в качестве единственного элемента в списке, а затем пытаетесь получить значение по индексу 10, которое не существует, и, следовательно, исключение.

Если вы измените 1-ю строку на val e = 0.asRight[String], то исключение исчезнет, ​​потому что что-то действительно существует с индексом 0, функцией, которую только что поместили туда.

Компилируется, потому что экземпляр List будет принимать Int в качестве параметра (через скрытый метод apply()), но экземпляр Option не имеет метода apply() это занимает Int (*), поэтому не может быть скомпилировано.

(*) У объекта Option есть метод apply(), но это другое животное.

2 голосов
/ 22 мая 2019

Здесь несколько моментов, объясняющих, почему работает ваш первый пример с List[A]. Сначала давайте рассмотрим расширение, которое происходит в выражении:

val res: Either[String, Int => String] = 
  e.map[Int => String](List.apply[Int => String](((x$1: Int) => FlinkTest.this.i2s(x$1))));

Обратите внимание на две вещи:

  • Расширение лямбда-выражения происходит внутри List.apply, и, возможно, не так, как вы ожидали, так как оно выходит за пределы List.apply, например:

    e.map(i => List(i2s(i))
    
  • Тип возврата из .map как-то не Either[String, List[Int => String]], а Either[String, Int => String]. Это связано с тем, что в иерархической цепочке List[A] расширяет PartialFunction[Int, A], что позволяет ему преобразовывать результат в тип функции.

Это не работает для Option[A], поскольку оно не распространяется на PartialFunction нигде в иерархии типов.

Ключевым выводом здесь является то, что расширение лямбда-выражения работает не так, как вы ожидаете, так как List(i2s(_)) расширяется до List(i2s(x => i2s(x)), а не List(i => i2s(i)). Для получения дополнительной информации о расширении подчеркивания см. Каковы все варианты использования подчеркивания в Scala?

...