Почему Scala нужен без параметров в дополнение к методам нулевого параметра?
Я понимаю разницу между методами с нулевым параметром и без параметров, но то, что я действительно не понимаю, - это выбор дизайна языка, который делал необязательные методы.
Недостатки, о которых я могу думать:
- Это сбивает с толку. Каждую неделю или две есть вопросы здесь или в списке рассылки Scala.
- Это сложно; мы также должны различать
() => X
и => X
.
- Это двусмысленно:
x.toFoo(y)
означает, что он говорит, или x.toFoo.apply(y)
? (Ответ: это зависит от того, какие перегрузки имеют метод x
toFoo
и перегрузки по методу Foo
apply
, но если есть столкновение, вы не видите ошибку, пока не попытаетесь ее вызвать.)
- Это испортит синтаксис метода метода operator style: вместо аргументов, методов цепочки или в конце, чтобы избежать помех с точкой с запятой, нет символа. С помощью методов zero-arg вы можете использовать пустой список параметров
()
.
В настоящее время вы не можете иметь оба определения в классе: вы получаете сообщение об ошибке, когда метод уже определен. Они также преобразуются в Function0
.
Почему бы просто не сделать методы def foo
и def foo()
точно такими же и разрешить их вызывать с круглыми скобками или без них? Каковы его особенности?
Ответы
Ответ 1
Currying, That Why
Даниэль отлично поработал, объясняя, почему нужны беспараметрические методы. Я объясню, почему они отчетливо рассматриваются методами нулевого параметра.
Многие люди рассматривают различие между параметрами без параметров и функциями с нулевым параметром в виде нечеткой формы синтаксического сахара. По правде говоря, это просто артефакт о том, как Scala поддерживает каррирование (для полноты, см. Ниже более подробное объяснение того, что такое каррирование, и почему нам все это так нравится).
Формально функция может иметь ноль или более списков параметров с нулевым или большим количеством параметров.
Это означает, что допустимы следующие значения: def a
, def b()
, а также изобретенные def c()()
и def d(x: Int)()()(y: Int)
и т.д.
Функция def foo = ???
имеет нулевые списки параметров. Функция def bar() = ???
имеет только один список параметров с нулевыми параметрами. Введение дополнительных правил, которые объединяют две формы, подорвало бы каррирование как согласованное языковое средство: def a
было бы эквивалентно по форме def b()
и def c()()
для обоих; def d(x: Int)()()(y: Int)
будет эквивалентен def e()(x: Int)(y: Int)()()
.
Один случай, когда currying не имеет значения, - это когда вы работаете с Java interop. Java не поддерживает currying, поэтому нет проблем с внедрением синтаксического сахара для методов с нулевым параметром, таких как "test".length()
(который непосредственно вызывает java.lang.String#length()
), также вызывается как "test".length
.
Краткое объяснение каррирования
Scala поддерживает функцию языка, называемую "currying", названную в честь математика Хаскелла Карри.
Currying позволяет вам определять функции с несколькими списками параметров, например:
def add(a: Int)(b: Int): Int = a + b
add(2)(3) // 5
Это полезно, потому что теперь вы можете определить inc
в части частичного применения add
:
def inc: Int => Int = add(1)
inc(2) // 3
Каррирование чаще всего рассматривается как способ введения структур управления через библиотеки, например:
def repeat(n: Int)(thunk: => Any): Unit = (1 to n) foreach { _ => thunk }
repeat(2) {
println("Hello, world")
}
// Hello, world
// Hello, world
В качестве примера, см., как repeat
открывает еще одну возможность использования currying:
def twice: (=> Any) => Unit = repeat(2)
twice {
println("Hello, world")
}
// ... you get the picture :-)
Ответ 2
Одна хорошая вещь о проблеме, периодически возникающей в ML, заключается в том, что существуют периодические ответы.
Кто может сопротивляться потоку, называемому "Что с нами не так?"
https://groups.google.com/forum/#!topic/scala-debate/h2Rej7LlB2A
От: martin odersky Дата: Пт, 2 мар 2012 в 12:13 PM Тема: Re: [ scala -debate] что с нами не так...
То, что некоторые люди считают "неправильным с нами", состоит в том, что мы пытаемся сгибать назад, чтобы заставить идиомы Java работать плавно в Scala. принципиальная вещь заключалась бы в том, чтобы сказать def length() и def length разные, и, извините, String - это класс Java, поэтому вам нужно написать s.length(), а не s.length. Мы очень усердно работаем над допускающие автоматические преобразования от s.length до s.length(). Это проблематичным, как есть. Обобщая, что так, чтобы эти два были идентифицированы в системе типов будет верным способом обречения. Как тогда вы неоднозначность:
type Action =() = > () def foo: Действие
Является ли foo типа Action или()? Как насчет foo()?
Martin
Мой любимый фрагмент фиаско из этой темы:
On Fri, Mar 2, 2012 at 10:15 AM, Rex Kerr <[email protected]> wrote:
>This would leave you unable to distinguish between the two with
>structural types, but how often is the case when you desperately
>want to distinguish the two compared to the case where distinguishing
>between the two is a hassle?
/** Note to maintenance programmer: It is important that this method be
* callable by classes which have a 'def foo(): Int' but not by classes which
* merely have a 'def foo: Int'. The correctness of this application depends
* on maintaining this distinction.
*
* Additional note to maintenance programmer: I have moved to zambia.
* There is no forwarding address. You will never find me.
*/
def actOnFoo(...)
Таким образом, основной мотивацией для этой функции является генерация такого рода потока ML.
Еще один бит googlology:
В Чт, 1 апреля 2010 в 8:04, Rex Kerr < [скрытое письмо] > написал: On Thu, Apr 1, 2010 at 1:00 PM, richard emberson < [скрытое письмо] > написал:
Я предполагаю, что "def getName: String" совпадает с "def getName(): String"
Нет, на самом деле, это не так. Хотя оба они называют метод без параметров, один - это "метод с нулевыми списками параметров", тогда как другой - это "метод с одним пустым списком параметров". Если ты хочешь быть еще более озадаченным, попробуйте def getName()(): String (и создайте класс с этой сигнатурой)!
Scala представляет параметры как список списков, а не только список и
Список()!= Список (Список())
Это своего рода причудливое раздражение, тем более, что так мало различия между ними в противном случае, и поскольку оба могут быть автоматически превращается в подпись функции() = > String.
True. В самом деле, любая слияния между безпараметрическими методами и методы с пустыми списками параметров полностью связаны с Java interop. Они должны быть разными, но тогда обработка Java-методов будет просто слишком болезненно. Можете ли вы представить, что нужно писать str.length() каждый вы берете длину строки?
Приветствия
Ответ 3
Во-первых, () => X
и => X
не имеют абсолютно никакого отношения к беспараметрированным методам.
Теперь выглядит довольно глупо писать что-то вроде этого:
var x() = 5
val y() = 2
x() = x() + y()
Теперь, если вы не следите за тем, что приведенное выше имеет отношение к беспараметричным методам, вы должны искать принцип равномерного доступа. Все вышеперечисленное относится к объявлениям методов, и все они могут быть заменены на def
. То есть, предполагая, что вы удалите их круглые скобки.
Ответ 4
Я бы сказал, что оба варианта возможны, потому что вы можете обращаться к изменяемому состоянию с помощью метода без параметров:
class X(private var x: Int) {
def inc() { x += 1 }
def value = x
}
Метод value
не имеет побочных эффектов (он обращается только к изменяемому состоянию). Это поведение явно упоминается в Программирование в Scala:
Такие безпараметрические методы довольно распространены в Scala. Напротив, методы, определяемые пустым скобками, например def height(): Int, называются пустыми методами. Рекомендуемое соглашение заключается в использовании безпараметрического метода, когда нет параметров, и метод обращается к изменяемому состоянию только путем чтения полей содержащего объекта (в частности, он не изменяет изменяемое состояние).
Это соглашение поддерживает принцип единого доступа [...]
Подводя итог, рекомендуется, чтобы стиль в Scala определял методы, которые не принимают никаких параметров и не имеют побочных эффектов в качестве безпараметрических методов, т.е. оставляют пустые круглые скобки. С другой стороны, вы никогда не должны определять метод, который имеет побочные эффекты без круглых скобок, потому что тогда вызовы этого метода будут выглядеть как выбор полей.
Ответ 5
Помимо упомянутого условного факта (побочный эффект и побочный эффект), он помогает в нескольких случаях:
Полезность наличия пустого парнера
// short apply syntax
object A {
def apply() = 33
}
object B {
def apply = 33
}
A() // works
B() // does not work
// using in place of a curried function
object C {
def m()() = ()
}
val f: () => () => Unit = C.m
Полезность наличия no-paren
// val <=> def, var <=> two related defs
trait T { def a: Int; def a_=(v: Int): Unit }
trait U { def a(): Int; def a_=(v: Int): Unit }
def tt(t: T): Unit = t.a += 1 // works
def tu(u: U): Unit = u.a += 1 // does not work
// avoiding clutter with apply the other way round
object D {
def a = Vector(1, 2, 3)
def b() = Vector(1, 2, 3)
}
D.a(0) // works
D.b(0) // does not work
// object can stand for no-paren method
trait E
trait F { def f: E }
trait G { def f(): E }
object H extends F {
object f extends E // works
}
object I extends G {
object f extends E // does not work
}
Таким образом, в терминах регулярности языка имеет смысл иметь различие (особенно для последнего показанного случая).