2013-09-11 2 views
4

фона

Недавно я присутствовал начинающую Scala Meetup, и мы говорили о различии между методами и функциями (также обсуждается в углубленном here).Почему Scala не позволяет List.map _ и типа подписи в РЕПЛ

Например:

scala> val one = 1 
one: Int = 1 

scala> val addOne = (x: Int) => x + 1 
addOne: Int => Int = <function1> 

Это свидетельствует о том, что Vals может не только иметь целочисленный тип, но может иметь тип функции. Мы можем увидеть тип в лестницу РЕПЛ:

scala> :type addOne 
Int => Int 

Мы также можем определить методы объектов и классов:

scala> object Foo { 
| def timesTwo(op: Int) = op * 2 
| } 
defined module Foo 

И в то время как метод не имеет тип (а скорее имеет тип подписи), мы можем поднять его в функцию, чтобы увидеть, что это такое:

scala> :type Foo.timesTwo 
<console>:9: error: missing arguments for method timesTwo in object Foo; 
follow this method with `_' if you want to treat it as a partially applied function 
      Foo.timesTwo 
      ^


scala> :type Foo.timesTwo _ 
Int => Int 

Пока что так хорошо. Мы даже говорили о том, как функции фактически являются объектами с помощью метода применяются и как мы можем де-синтаксических sugarify выражения, чтобы показать это:

scala> Foo.timesTwo _ apply(4) 
res0: Int = 8 

scala> addOne.apply(3) 
res1: Int = 4 

Для меня это очень полезно в изучении языка, потому что я могу усвоить то, что синтаксис на самом деле подразумевает.

Проблемные Пример

Мы делали, однако, столкнуться с ситуацией, что мы не смогли определить. Возьмем, например, список строк. Мы можем сопоставить функции над значениями, демонстрирующих основные коллекции Scala и функционального программирования вещи:

scala> List(1,2,3).map(_*4) 
res2: List[Int] = List(4, 8, 12) 

Итак, что такое тип List (1,2,3) .map()? Я ожидаю, что мы сделали бы то же самое: тип трик в РЕПЛ:

scala> :type List(1,2,3).map _ 
<console>:8: error: Cannot construct a collection of type Nothing with elements of type Nothing based on a collection of type List[Int]. 
      List(1,2,3).map _ 
        ^

Из определения API, я знаю, что подпись:

def map[B](f: (A) ⇒ B): List[B] 

Но есть также полная подпись:

def map[B, That](f: (A) ⇒ B)(implicit bf: CanBuildFrom[List[A], B, That]): That 

Вопрос

Итак, есть две вещи, которые я не совсем понимаю:

  • Почему не работает обычная функция трюка с функцией List.map? Есть ли способ де-синтаксического сахара ошибочное утверждение, чтобы продемонстрировать, что происходит?
  • Если причина, по которой метод не может быть снят, вызвана полной подписью «неявной», что именно происходит там?

И, наконец, существует ли надежный способ проверки обоих типов и подписи от REPL?

+0

Вопрос о неявной сигнатуре типа объясняется здесь: http://stackoverflow.com/a/18533437/125901 –

ответ

7

Проблема, с которой вы столкнулись, связана с тем, что в Scala, functions are monomorphic, while methods can be polymorphic.В результате параметры типа B и That должны быть известны, чтобы создать значение функции для List.map.

Компилятор пытается вывести параметры, но не может придумать ничего разумного. Если поставить параметры, вы получите правильный тип функции:

scala> List(1,2,3).map[Char, List[Char]] _ 
res0: (Int => Char) => List[Char] = <function1> 

scala> :type res0 
(Int => Char) => List[Char] 
1

без фактического аргумента функции, выведенный тип функции Int => Nothing, но цель коллекции также Nothing. В области видимости нет подходящего CanBuildFrom[List[Int], Nothing, Nothing], что можно увидеть, введя implicitly[CanBuildFrom[List[Int], Nothing, Nothing]] в REPL (появляется такая же ошибка). Если поставить параметры типа, то вы можете получить функцию:

scala> :type List(1,2,3).map[Int, List[Int]] _ 
(Int => Int) => List[Int] 

Я не думаю, что вы можете проверить сигнатуры методов в REPL. Это для Scaladoc.

+0

Я все еще думаю, что было бы полезно увидеть сигнатуры типов в REPL. Это не только упростит проверку сервера, но и позволит вам проверять код, определенный или составленный во время выполнения. Есть ли способ сделать это, не перейдя в java.lang.reflect? –

+1

Возможно, вам больше повезло с 'scala.reflect', но даже тогда он не собирается рассказывать вам, что для него используются различные параметры или что делает этот метод. REPL можно было бы улучшить, чтобы отображать комментарии scaladoc, но это все равно было бы плохим кузеном, глядя на scaladoc в вашем браузере. Документы являются частью дистрибутива Scala, поэтому просто найдите папку на жестком диске и постоянно открывайте ее на вкладке браузера. –

+2

@ 16bytes На самом деле я обнаружил, что вы можете видеть сигнатуры методов в REPL, используя клавишу Tab после ввода имени метода. Кажется, что это работает только тогда, когда у вас есть один идентификатор, за которым следует точка и имя метода, поэтому, если вы определяете 'val list = List (1,2,3)', то 'list.map', за которым следует Tab, отображается его подпись, Список (1,2,3) .map', за которым следует Tab, выводится сообщение об ошибке. Тем не менее, может быть полезно. –

Смежные вопросы