Chamada pelo nome vs chamada pelo valor em Scala, esclarecimentos necessários

239

Pelo que entendi, em Scala, uma função pode ser chamada de

  • por valor ou
  • por nome

Por exemplo, dadas as seguintes declarações, sabemos como a função será chamada?

Declaração:

def  f (x:Int, y:Int) = x;

Ligar

f (1,2)
f (23+55,5)
f (12+3, 44*11)

Quais são as regras, por favor?

James Raitsev
fonte

Respostas:

540

O exemplo que você deu usa apenas chamada por valor, portanto, darei um novo exemplo mais simples que mostra a diferença.

Primeiro, vamos assumir que temos uma função com um efeito colateral. Esta função imprime algo e depois retorna um Int.

def something() = {
  println("calling something")
  1 // return value
}

Agora vamos definir duas funções que aceitam Intargumentos exatamente iguais, exceto que uma recebe o argumento em um estilo de chamada por valor ( x: Int) e a outra em um estilo de chamada por nome ( x: => Int).

def callByValue(x: Int) = {
  println("x1=" + x)
  println("x2=" + x)
}

def callByName(x: => Int) = {
  println("x1=" + x)
  println("x2=" + x)
}

Agora, o que acontece quando os chamamos com nossa função de efeito colateral?

scala> callByValue(something())
calling something
x1=1
x2=1

scala> callByName(something())
calling something
x1=1
calling something
x2=1

Portanto, você pode ver que na versão de chamada por valor, o efeito colateral da chamada de função passada ( something()) aconteceu apenas uma vez. No entanto, na versão chamada por nome, o efeito colateral aconteceu duas vezes.

Isso ocorre porque as funções de chamada por valor calculam o valor da expressão passada antes de chamar a função, portanto, o mesmo valor é acessado sempre. Em vez disso, as funções de chamada por nome recalculam o valor da expressão passada sempre que ela é acessada.

dhg
fonte
296
Sempre achei que essa terminologia é desnecessariamente confusa. Uma função pode ter vários parâmetros que variam entre status de chamada por nome e chamada por valor. Portanto, não é que uma função é chamada por nome ou chamada por valor, é que cada um dos seus parâmetros podem ser passar -de-nome ou passar por valor. Além disso, "chamada por nome" não tem nada a ver com nomes . => Inté um tipo diferente de Int; é "função de nenhum argumento que irá gerar um Int" vs just Int. Depois de obter funções de primeira classe, você não precisa inventar a terminologia de chamada por nome para descrever isso.
Ben
2
@ Ben, isso ajuda a responder algumas perguntas, obrigado. Eu gostaria que mais artigos explicassem claramente a semântica da passagem por nome.
Christopher Poile 18/03/2013
3
@SelimOber Se o texto f(2)for compilado como uma expressão do tipo Int, o código gerado chamará fcom argumento 2e o resultado será o valor da expressão. Se esse mesmo texto for compilado como uma expressão do tipo => Int, o código gerado usará uma referência a algum tipo de "bloco de código" como o valor da expressão. De qualquer forma, um valor desse tipo pode ser passado para uma função que espera um parâmetro desse tipo. Tenho certeza de que você pode fazer isso com atribuição de variáveis, sem nenhum parâmetro passando à vista. Então, o que nomes ou chamadas têm algo a ver com isso?
Ben
4
@ Ben Então, se => Inté "função de nenhum argumento que gera um Int", como isso é diferente () => Int? Scala parece tratar isso de maneira diferente, por exemplo, => Intaparentemente, não funciona como o tipo de a val, apenas como o tipo de um parâmetro.
Tim Goodman
5
@ TimGoodman Você está certo, é um pouco mais complicado do que eu percebi. => Inté uma conveniência e não é implementado exatamente como um objeto de função (presumivelmente por que você não pode ter variáveis ​​do tipo => Int, embora não haja uma razão fundamental para que isso não funcione). () => Inté explicitamente uma função de nenhum argumento que retornará um Int, que precisa ser chamado explicitamente e pode ser passado como uma função. => Inté uma espécie de "proxy Int", e a única coisa que você pode fazer com isso é chamá-lo (implicitamente) para obter o Int.
Ben
51

Aqui está um exemplo de Martin Odersky:

def test (x:Int, y: Int)= x*x

Queremos examinar a estratégia de avaliação e determinar qual é mais rápido (menos etapas) nestas condições:

test (2,3)

chamada por valor: teste (2,3) -> 2 * 2 -> 4
chamada por nome: teste (2,3) -> 2 * 2 -> 4
Aqui o resultado é alcançado com o mesmo número de etapas.

test (3+4,8)

chamada por valor: teste (7,8) -> 7 * 7 -> 49
chamada por nome: (3 + 4) (3 + 4) -> 7 (3 + 4) -> 7 * 7 -> 49
Aqui chamar pelo valor é mais rápido.

test (7,2*4)

chamada por valor: teste (7,8) -> 7 * 7 -> 49
chamada por nome: 7 * 7 -> 49
Aqui a chamada por nome é mais rápida

test (3+4, 2*4) 

chamada por valor: teste (7,2 * 4) -> teste (7, 8) -> 7 * 7 -> 49
chamada por nome: (3 + 4) (3 + 4) -> 7 (3 + 4) -> 7 * 7 -> 49
O resultado é alcançado nas mesmas etapas.

Behrooz Tabesh
fonte
1
No terceiro exemplo para CBV, eu acho que você quis teste (7,8) em vez de teste (7,14)
talonx
1
Exemplo é retirado do Coursera, princípio da programação scala. Aula 1.2. A chamada pelo nome deve ser lida, def test (x:Int, y: => Int) = x * xobserve que o parâmetro y nunca é usado.
dr jerry
1
Bom exemplo! Retirado do Coursera MOOC :)
alxsimo
Esta é uma boa explicação sobre a diferença, mas não endereço para ser pergunta, ou seja, qual dos dois é Scala chamando
db1234
16

No caso do seu exemplo, todos os parâmetros serão avaliados antes de serem chamados na função, pois você os define apenas por valor . Se você deseja definir seus parâmetros pelo nome, deve passar um bloco de código:

def f(x: => Int, y:Int) = x

Dessa forma, o parâmetro xnão será avaliado até que seja chamado na função.

Este pequeno post aqui explica isso muito bem.

resilva87
fonte
10

Para repetir o argumento de @ Ben nos comentários acima, acho melhor pensar em "chamada por nome" como apenas açúcar sintático. O analisador apenas agrupa as expressões em funções anônimas, para que possam ser chamadas posteriormente, quando usadas.

Com efeito, em vez de definir

def callByName(x: => Int) = {
  println("x1=" + x)
  println("x2=" + x)
}

e executando:

scala> callByName(something())
calling something
x1=1
calling something
x2=1

Você também pode escrever:

def callAlsoByName(x: () => Int) = {
  println("x1=" + x())
  println("x2=" + x())
}

E execute-o da seguinte maneira para o mesmo efeito:

callAlsoByName(() => {something()})

calling something
x1=1
calling something
x2=1
user1496984
fonte
Eu acho que você quis dizer: <! - language: lang-scala -> def callAlsoByName (x: () => Int) = {println ("x1 =" + x ()) println ("x2 =" + x ( ))} e então: <! - language: lang-js -> callAlsoByName (() => alguma coisa) ()) Eu não acho que você precise das chaves em volta de algo () nesta última chamada. Nota: tentei apenas editar sua resposta, mas minha edição foi rejeitada pelos revisores, dizendo que deveria ser um comentário ou uma resposta separada.
lambdista
Aparentemente, você não pode usar o destaque da sintaxe nos comentários, então ignore a parte "<! - language: lang-scala ->"! Eu teria editado meu próprio comentário, mas você poderá fazê-lo em apenas 5 minutos! :)
lambdista
1
Eu recentemente me deparei com isso também. Não há problema em conceitualmente pensar assim, mas scala diferencia entre => Te () => T. Uma função que usa o primeiro tipo como parâmetro, não aceita o segundo, o scala armazena informações suficientes na @ScalaSignatureanotação para gerar um erro de tempo de compilação para isso. O bytecode para ambos => Te () => Té o mesmo e é a Function0. Veja esta pergunta para mais detalhes.
Vsnyc 27/10/16
6

Vou tentar explicar por um caso de uso simples, em vez de apenas fornecer um exemplo

Imagine que você deseja criar um "aplicativo nagger" que o todas as vezes desde a última vez em que você foi incomodado.

Examine as seguintes implementações:

object main  {

    def main(args: Array[String]) {

        def onTime(time: Long) {
            while(time != time) println("Time to Nag!")
            println("no nags for you!")
        }

        def onRealtime(time: => Long) {
            while(time != time) println("Realtime Nagging executed!")
        }

        onTime(System.nanoTime())
        onRealtime(System.nanoTime())
    }
}

Na implementação acima, o nagger funcionará apenas ao passar pelo nome, o motivo é que, ao passar pelo valor, será reutilizado e, portanto, o valor não será reavaliado, enquanto ao passar pelo nome, o valor será reavaliado a cada vez que as variáveis ​​são acessadas

guykaplan
fonte
4

Normalmente, parâmetros para funções são parâmetros por valor; isto é, o valor do parâmetro é determinado antes de ser passado para a função. Mas e se precisarmos escrever uma função que aceite como parâmetro uma expressão que não queremos que seja avaliada até que seja chamada dentro de nossa função? Para essa circunstância, o Scala oferece parâmetros de chamada por nome.

Um mecanismo de chamada por nome passa um bloco de código para o receptor e cada vez que o receptor acessa o parâmetro, o bloco de código é executado e o valor é calculado.

object Test {
def main(args: Array[String]) {
    delayed(time());
}

def time() = {
  println("Getting time in nano seconds")
  System.nanoTime
}
def delayed( t: => Long ) = {
  println("In delayed method")
  println("Param: " + t)
  t
}
}
 1. C: /> scalac Test.scala 
 2. Teste de Scala
 3. No método atrasado
 4. Obtendo tempo em nano segundos
 5. Param: 81303808765843
 6. Obtendo tempo em nano segundos
sofiene zaghdoudi
fonte
2

Como presumo, a call-by-valuefunção conforme discutida acima passa apenas os valores para a função. De acordo com Martin OderskyÉ uma estratégia de avaliação seguida por um Scala que desempenha um papel importante na avaliação de funções. Mas, simplifique call-by-name. é como passar a função como argumento para o método também conhecido como Higher-Order-Functions. Quando o método acessa o valor do parâmetro passado, ele chama a implementação das funções passadas. como abaixo:

De acordo com o exemplo @dhg, crie o método primeiro como:

def something() = {
 println("calling something")
 1 // return value
}  

Esta função contém uma printlninstrução e retorna um valor inteiro. Crie a função, que possui argumentos como call-by-name:

def callByName(x: => Int) = {
 println("x1=" + x)
 println("x2=" + x)
}

Este parâmetro de função é definir uma função anônima que retorne um valor inteiro. Nele xcontêm uma definição de função que 0passou argumentos, mas retornam intvalor e nossa somethingfunção contém a mesma assinatura. Quando chamamos a função, passamos a função como argumento para callByName. Mas no caso de call-by-valueapenas passar o valor inteiro para a função. Chamamos a função como abaixo:

scala> callByName(something())
 calling something
 x1=1
 calling something
 x2=1 

Nesse somethingmétodo, nosso método é chamado duas vezes, porque quando acessamos o valor de xin callByNamemethod, sua chamada para a definição de somethingmethod.

Harmeet Singh Taara
fonte
2

Chamada por valor é um caso de uso geral, conforme explicado por muitas respostas aqui.

A chamada por nome passa um bloco de código para o chamador e cada vez que o chamador acessa o parâmetro, o bloco de código é executado e o valor é calculado.

Vou tentar demonstrar a chamada pelo nome de maneira mais simples com os casos de uso abaixo

Exemplo 1:

Exemplo simples / caso de uso de chamada pelo nome está abaixo da função, que assume a função como parâmetro e fornece o tempo decorrido.

 /**
   * Executes some code block and prints to stdout the 
time taken to execute   the block 
for interactive testing and debugging.
   */
  def time[T](f: => T): T = {
    val start = System.nanoTime()
    val ret = f
    val end = System.nanoTime()

    println(s"Time taken: ${(end - start) / 1000 / 1000} ms")

    ret
  }

Exemplo 2:

O apache spark (com scala) usa o log usando chamada por nome, como vê a Loggingcaracterística na qual seu usuário avalia preguiçosamente se deve log.isInfoEnabledou não a partir do método abaixo.

protected def logInfo(msg: => String) {
     if (log.isInfoEnabled) log.info(msg)
 }
Ram Ghadiyaram
fonte
2

Em uma chamada por valor , o valor da expressão é pré-calculado no momento da chamada da função e esse valor específico é passado como parâmetro para a função correspondente. O mesmo valor será usado em toda a função.

Enquanto em uma chamada por nome , a expressão em si é passada como parâmetro para a função e é computada apenas dentro da função, sempre que esse parâmetro específico é chamado.

A diferença entre Chamada por nome e Chamada por valor no Scala poderia ser melhor compreendida com o exemplo abaixo:

Fragmento de código

object CallbyExample extends App {

  // function definition of call by value
  def CallbyValue(x: Long): Unit = {
    println("The current system time via CBV: " + x);
    println("The current system time via CBV " + x);
  }

  // function definition of call by name
  def CallbyName(x: => Long): Unit = {
    println("The current system time via CBN: " + x);
    println("The current system time via CBN: " + x);
  }

  // function call
  CallbyValue(System.nanoTime());
  println("\n")
  CallbyName(System.nanoTime());
}

Resultado

The current system time via CBV: 1153969332591521
The current system time via CBV 1153969332591521


The current system time via CBN: 1153969336749571
The current system time via CBN: 1153969336856589

No trecho de código acima, para a chamada de função CallbyValue (System.nanoTime ()) , o nano tempo do sistema é pré-calculado e esse valor pré-calculado passou um parâmetro para a chamada de função.

Mas na chamada da função CallbyName (System.nanoTime ()) , a própria expressão "System.nanoTime ())" é passada como um parâmetro para a chamada de função e o valor dessa expressão é calculado quando esse parâmetro é usado dentro da função .

Observe a definição da função CallbyName, onde existe um símbolo => separando o parâmetro x e seu tipo de dados. Esse símbolo específico indica que a função é chamada pelo tipo de nome.

Em outras palavras, os argumentos da função de chamada por valor são avaliados uma vez antes de inserir a função, mas os argumentos da função de chamada por nome são avaliados dentro da função somente quando são necessários.

Espero que isto ajude!

Nijanthan Vijayakumar
fonte
2

Aqui está um exemplo rápido que eu codifiquei para ajudar um colega meu que está atualmente fazendo o curso Scala. O que eu achei interessante é que Martin não usou a resposta da pergunta && apresentada anteriormente na palestra como exemplo. De qualquer forma, espero que isso ajude.

val start = Instant.now().toEpochMilli

val calc = (x: Boolean) => {
    Thread.sleep(3000)
    x
}


def callByValue(x: Boolean, y: Boolean): Boolean = {
    if (!x) x else y
}

def callByName(x: Boolean, y: => Boolean): Boolean = {
    if (!x) x else y
}

new Thread(() => {
    println("========================")
    println("Call by Value " + callByValue(false, calc(true)))
    println("Time " + (Instant.now().toEpochMilli - start) + "ms")
    println("========================")
}).start()


new Thread(() => {
    println("========================")
    println("Call by Name " + callByName(false, calc(true)))
    println("Time " + (Instant.now().toEpochMilli - start) + "ms")
    println("========================")
}).start()


Thread.sleep(5000)

A saída do código será a seguinte:

========================
Call by Name false
Time 64ms
========================
Call by Value false
Time 3068ms
========================
Madpoptart
fonte
1

Os parâmetros geralmente são passados ​​por valor, o que significa que eles serão avaliados antes de serem substituídos no corpo da função.

Você pode forçar um parâmetro a ser chamado pelo nome usando a seta dupla ao definir a função.

// first parameter will be call by value, second call by name, using `=>`
def returnOne(x: Int, y: => Int): Int = 1

// to demonstrate the benefits of call by name, create an infinite recursion
def loop(x: Int): Int = loop(x)

// will return one, since `loop(2)` is passed by name so no evaluated
returnOne(2, loop(2))

// will not terminate, since loop(2) will evaluate. 
returnOne(loop(2), 2) // -> returnOne(loop(2), 2) -> returnOne(loop(2), 2) -> ... 
iblamefish
fonte
1

Já existem muitas respostas fantásticas para essa pergunta na Internet. Escreverei uma compilação de várias explicações e exemplos que reuni sobre o tópico, caso alguém ache útil

INTRODUÇÃO

chamada por valor (CBV)

Normalmente, parâmetros para funções são parâmetros de chamada por valor; ou seja, os parâmetros são avaliados da esquerda para a direita para determinar seu valor antes que a própria função seja avaliada

def first(a: Int, b: Int): Int = a
first(3 + 4, 5 + 6) // will be reduced to first(7, 5 + 6), then first(7, 11), and then 7

chamada por nome (CBN)

Mas e se precisarmos escrever uma função que aceite como parâmetro uma expressão que não avaliaremos até que seja chamada dentro de nossa função? Para essa circunstância, o Scala oferece parâmetros de chamada por nome. Significa que o parâmetro é passado para a função como está e sua avaliação ocorre após a substituição

def first1(a: Int, b: => Int): Int = a
first1(3 + 4, 5 + 6) // will be reduced to (3 + 4) and then to 7

Um mecanismo de chamada por nome passa um bloco de código para a chamada e cada vez que a chamada acessa o parâmetro, o bloco de código é executado e o valor é calculado. No exemplo a seguir, atrasado imprime uma mensagem demonstrando que o método foi inserido. Em seguida, atrasado imprime uma mensagem com seu valor. Finalmente, retornos atrasados ​​'t':

 object Demo {
       def main(args: Array[String]) {
            delayed(time());
       }
    def time() = {
          println("Getting time in nano seconds")
          System.nanoTime
       }
       def delayed( t: => Long ) = {
          println("In delayed method")
          println("Param: " + t)
       }
    }

No método atrasado
Obtendo tempo em nano segundos
Param: 2027245119786400

PRÓS E CONTRAS PARA CADA CASO

CBN: + Termina com mais frequência * verifique abaixo acima da terminação * + Tem a vantagem de um argumento de função não ser avaliado se o parâmetro correspondente não for usado na avaliação do corpo da função - É mais lento, cria mais classes (o que o programa leva mais tempo para carregar) e consome mais memória.

CBV: + Geralmente é exponencialmente mais eficiente que o CBN, porque evita essa recomputação repetida de argumentos que as expressões que chamam pelo nome envolvem. Ele avalia todos os argumentos de função apenas uma vez + É muito mais agradável com efeitos imperativos e efeitos colaterais, porque você tende a saber muito melhor quando expressões serão avaliadas. -Pode levar a um loop durante a avaliação dos parâmetros * verifique abaixo acima da terminação *

E se a rescisão não for garantida?

-Se a avaliação CBV de uma expressão e terminar, então a avaliação CBN de e também termina -A outra direção não é verdadeira

Exemplo de não rescisão

def first(x:Int, y:Int)=x

Considere a expressão primeiro (1, loop)

CBN: primeiro (1, loop) → 1 CBV: primeiro (1, loop) → reduz os argumentos dessa expressão. Como um é um loop, ele reduz argumentos infinitamente. Não termina

DIFERENÇAS EM CADA COMPORTAMENTO DE CASO

Vamos definir um teste de método que será

Def test(x:Int, y:Int) = x * x  //for call-by-value
Def test(x: => Int, y: => Int) = x * x  //for call-by-name

Teste Caso1 (2,3)

test(2,3)2*24

Como começamos com argumentos já avaliados, será a mesma quantidade de etapas para chamada por valor e chamada por nome

Teste Case2 (3 + 4,8)

call-by-value: test(3+4,8) → test(7,8)7 * 749
call-by-name: (3+4)*(3+4)7 * (3+4)7 * 749

Nesse caso, a chamada por valor executa menos etapas

Teste Case3 (7, 2 * 4)

call-by-value: test(7, 2*4) → test(7,8)7 * 749
call-by-name: (7)*(7)49

Evitamos o cálculo desnecessário do segundo argumento

Teste Case4 (3 + 4, 2 * 4)

call-by-value: test(7, 2*4) → test(7,8)7 * 749
call-by-name: (3+4)*(3+4)7*(3+4)7*749

Abordagem diferente

Primeiro, vamos assumir que temos uma função com um efeito colateral. Esta função imprime algo e retorna um Int.

def something() = {
  println("calling something")
  1 // return value
}

Agora, vamos definir duas funções que aceitam argumentos Int exatamente iguais, exceto que uma recebe o argumento em um estilo de chamada por valor (x: Int) e a outra em um estilo de chamada por nome (x: => Int).

def callByValue(x: Int) = {
  println("x1=" + x)
  println("x2=" + x)
}
def callByName(x: => Int) = {
  println("x1=" + x)
  println("x2=" + x)
}

Agora, o que acontece quando os chamamos com nossa função de efeito colateral?

scala> callByValue(something())
calling something
x1=1
x2=1
scala> callByName(something())
calling something
x1=1
calling something
x2=1

Portanto, você pode ver que na versão de chamada por valor, o efeito colateral da chamada de função passada (algo ()) aconteceu apenas uma vez. No entanto, na versão chamada por nome, o efeito colateral aconteceu duas vezes.

Isso ocorre porque as funções de chamada por valor calculam o valor da expressão passada antes de chamar a função, portanto, o mesmo valor é acessado sempre. No entanto, as funções de chamada por nome recalculam o valor da expressão passada sempre que ela é acessada.

Exemplos onde é melhor usar a chamada por nome

De: https://stackoverflow.com/a/19036068/1773841

Exemplo simples de desempenho: log.

Vamos imaginar uma interface como esta:

trait Logger {
  def info(msg: => String)
  def warn(msg: => String)
  def error(msg: => String)
}

E então usado assim:

logger.info("Time spent on X: " + computeTimeSpent)

Se o método info não fizer nada (porque, digamos, o nível de log foi configurado para mais alto que isso), o computeTimeSpent nunca será chamado, economizando tempo. Isso acontece muito com os criadores de logs, onde geralmente é possível manipular seqüências de caracteres que podem ser caras em relação às tarefas que estão sendo registradas.

Exemplo de correção: operadores lógicos.

Você provavelmente já viu código assim:

if (ref != null && ref.isSomething)

Imagine que você declararia o método && assim:

trait Boolean {
  def &&(other: Boolean): Boolean
}

então, sempre que ref for nulo, você receberá um erro porque isSomething será chamado em uma referência nula antes de ser passado para &&. Por esse motivo, a declaração real é:

trait Boolean {
  def &&(other: => Boolean): Boolean =
    if (this) this else other
}
Ignacio Alorre
fonte
1

Passar por um exemplo deve ajudá-lo a entender melhor a diferença.

Vamos definir uma função simples que retorne a hora atual:

def getTime = System.currentTimeMillis

Agora vamos definir uma função, por nome , que imprime duas vezes atrasada por um segundo:

def getTimeByName(f: => Long) = { println(f); Thread.sleep(1000); println(f)}

E um por valor :

def getTimeByValue(f: Long) = { println(f); Thread.sleep(1000); println(f)}

Agora vamos chamar cada um:

getTimeByName(getTime)
// prints:
// 1514451008323
// 1514451009325

getTimeByValue(getTime)
// prints:
// 1514451024846
// 1514451024846

O resultado deve explicar a diferença. O trecho está disponível aqui .

Maroun
fonte
0

CallByNameé invocado quando usado e callByValueinvocado sempre que a instrução é encontrada.

Por exemplo:-

Eu tenho um loop infinito, ou seja, se você executar esta função, nunca obteremos scalaprompt.

scala> def loop(x:Int) :Int = loop(x-1)
loop: (x: Int)Int

uma callByNamefunção assume o loopmétodo acima como argumento e nunca é usada dentro de seu corpo.

scala> def callByName(x:Int,y: => Int)=x
callByName: (x: Int, y: => Int)Int

Na execução do callByNamemétodo, não encontramos nenhum problema (recebemos o scalaprompt de volta), pois não estamos usando a função loop dentro da callByNamefunção.

scala> callByName(1,loop(10))
res1: Int = 1
scala> 

uma callByValuefunção assume o loopmétodo acima como parâmetro, como resultado da função ou expressão interna ser avaliada antes de executar a função externa por loopfunção executada recursivamente e nunca recebemos o scalaprompt de volta.

scala> def callByValue(x:Int,y:Int) = x
callByValue: (x: Int, y: Int)Int

scala> callByValue(1,loop(1))
Puneeth Reddy V
fonte
0

Veja isso:

    object NameVsVal extends App {

  def mul(x: Int, y: => Int) : Int = {
    println("mul")
    x * y
  }
  def add(x: Int, y: Int): Int = {
    println("add")
    x + y
  }
  println(mul(3, add(2, 1)))
}

y: => Int é chamada pelo nome. O que é passado como chamada pelo nome é add (2, 1). Isso será avaliado preguiçosamente. Portanto, a saída no console será "mul" seguida de "add", embora add pareça ser chamado primeiro. A chamada pelo nome funciona como um tipo de passagem de um ponteiro de função.
Agora mude de y: => Int para y: Int. O console mostrará "add" seguido de "mul"! Forma usual de avaliação.

Apurva Singh
fonte
-2

Eu não acho que todas as respostas aqui fazem a justificação correta:

Na chamada por valor, os argumentos são calculados apenas uma vez:

def f(x : Int, y :Int) = x

// following the substitution model

f(12 + 3, 4 * 11)
f(15, 4194304)
15

Como você pode ver acima, todos os argumentos são avaliados se não forem necessários, normalmente call-by-valuepodem ser rápidos, mas nem sempre são como neste caso.

Se a estratégia de avaliação fosse call-by-name, a decomposição teria sido:

f(12 + 3, 4 * 11)
12 + 3
15

Como você pode ver acima, nunca precisamos avaliar 4 * 11e, portanto, economizamos um pouco de computação que às vezes pode ser benéfico.

vivek
fonte