Почему Scala иногда применяет thunks автоматически? - PullRequest
26 голосов
/ 29 мая 2010

Сразу после 2:40 в ShadowofCatron * Scala Tutorial 3 video указано, что скобки следуют за именем thunk являются необязательными . «Буг?» сказал мой мозг по функциональному программированию, так как значение функции и значение, которое она оценивает при применении, совершенно разные вещи.

Поэтому я написал следующее, чтобы попробовать это. Мой мыслительный процесс описан в комментариях.

object Main {

    var counter: Int = 10
    def f(): Int = { counter = counter + 1; counter }

    def runThunk(t: () => Int): Int = { t() }

    def main(args: Array[String]): Unit = {
        val a = f()     // I expect this to mean "apply f to no args"
        println(a)      // and apparently it does

        val b = f       // I expect this to mean "the value f", a function value
        println(b)      // but it's the value it evaluates to when applied to no args
        println(b)      // and the application happens immediately, not in the call

        runThunk(b)     // This is an error: it's not println doing something funny
        runThunk(f)     // Not an error: seems to be val doing something funny
    }

}

Чтобы понять проблему, эта программа Scheme (и следующий дамп консоли) показывает, что я ожидала от программы Scala.

(define counter (list 10))
(define f (lambda ()
            (set-car! counter (+ (car counter) 1))
            (car counter)))

(define runThunk (lambda (t) (t)))

(define main (lambda args
               (let ((a (f))
                     (b f))
                 (display a) (newline)
                 (display b) (newline)
                 (display b) (newline)
                 (runThunk b)
                 (runThunk f))))

> (main)
11
#<procedure:f>
#<procedure:f>
13

Придя на этот сайт и спросив об этом, я наткнулся на этот ответ , в котором рассказывалось, как исправить вышеуказанную программу Scala:

    val b = f _     // Hey Scala, I mean f, not f()

Но подчеркивание «подсказка» нужно только иногда . Когда я звоню runThunk(f), подсказка не требуется. Но когда я 'псевдоним' f к b с val, а затем применить его, это не работает: приложение происходит в val; и даже lazy val работает таким образом, поэтому это не точка оценки, вызывающая такое поведение.

Это все оставляет меня с вопросом:

Почему Scala иногда автоматически применяет thunks при их оценке?

Это, как я подозреваю, вывод типа? И если да, то не должна ли система типов оставаться вне семантики языка?

Это хорошая идея? Программисты Scala применяют thunks вместо того, чтобы ссылаться на их значения гораздо чаще , что делает необязательные парены лучше в целом?


Примеры написаны с использованием Scala 2.8.0RC3, DrScheme 4.0.1 в R5RS.

Ответы [ 4 ]

14 голосов
/ 13 июня 2010

Проблема здесь:

Бух? "Сказал мое функциональное программирование мозг, так как значение функции и значение, которое он оценивает, когда применяются совершенно разные вещи.

Да, но вы не объявили никакой функции.

def f(): Int = { counter = counter + 1; counter }

Вы объявили метод с именем f, который имеет один пустой список параметров и возвращает Int. Метод не является функцией - он не имеет значения. Никогда, никогда. Лучшее, что вы можете сделать, - это получить экземпляр Method посредством отражения, что на самом деле совсем не одно и то же.

val b = f _     // Hey Scala, I mean f, not f()

Итак, что означает f _? Если бы f была функцией, это означало бы, что сама функция предоставлена, но здесь это не так. Что это действительно означает, это:

val b = () => f()

Другими словами, f _ - это замыкание над вызовом метода. И замыкания реализуются через функции.

Наконец, почему пустые списки параметров являются необязательными в Scala? Поскольку Scala допускает такие объявления, как def f = 5, Java этого не делает. Все методы в Java требуют как минимум пустой список параметров. И есть много таких методов, которые в стиле Scala не будут иметь никаких параметров (например, length и size). Таким образом, чтобы код выглядел более единообразно в отношении пустого списка параметров, Scala делает их необязательными.

13 голосов
/ 29 мая 2010

Причина по умолчанию, когда вы пишете:

val b = f

- оценить функцию и присвоить результат b, как вы заметили. Вы можете использовать _ или явно указать тип b:

// These all have the same effect
val b = f _
val b: () => Int = f
val b: Function0[Int] = f
12 голосов
/ 29 мая 2010

В вашем примере

def f(): Int = { counter = counter + 1; counter }

определяет метод, а не функцию. Методы AFAIK автоматически переводятся в функции в Scala в зависимости от контекста Чтобы определить функцию, вы можете написать

val f = () => { counter = counter + 1; counter }

и я думаю, вы получите то, что хотите.

9 голосов
/ 29 мая 2010

Ваше предположение верно - у Scala есть зависящая от типа семантика, касающаяся оценки выражений.

Как и Ruby, он всегда оценивает thunks даже без скобок. (Это может иметь преимущества для целей взаимодействия, поскольку вы можете переключать чистые и, возможно, нечистые операции без необходимости изменения синтаксиса.)

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

Обратите внимание, что оценка, зависящая от типа, может даже моделировать вызов по имени


Теперь поведение оценки в зависимости от типа - это хорошо или плохо? ... Ну, это, конечно, может привести к запутанным случаям, подобным вашему, и больше не будет чувствовать себя чистым . Но в большинстве случаев это просто работает , как задумано программистом (делая код более кратким) - так скажем, это хорошо .

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...