Diferença entre método e function em Scala

Eu li Scala Functions (parte de Another tour of Scala ). Nesse post ele afirmou:

Métodos e funções não são a mesma coisa

Mas ele não explicou nada sobre isso. O que ele estava tentando dizer?

Jim tem isso bastante coberto em seu post no blog , mas estou postando um briefing aqui para referência.

Primeiro, vamos ver o que a Especificação Scala nos diz. O capítulo 3 (tipos) nos informa sobre os tipos de funções (3.2.9) e os tipos de methods (3.3.1). O Capítulo 4 (declarações básicas) fala de Declaração e Definições de Valor (4.1), Declaração e Definições de Variáveis (4.2) e Declarações e Definições de Funções (4.6). O Capítulo 6 (expressões) fala de Funções Anônimas (6.23) e Valores de Métodos (6.7). Curiosamente, os valores das funções são falados de uma vez em 3.2.9 e em nenhum outro lugar.

Um tipo de function é (aproximadamente) um tipo de formulário (T1, …, Tn) => U , que é uma abreviação para o traço FunctionN na biblioteca padrão. Funções anônimas e valores de método têm tipos de function, e os tipos de function podem ser usados ​​como parte das declarações e definições de valor, variável e function. De fato, pode fazer parte de um tipo de método.

Um Tipo de Método é um tipo sem valor . Isso significa que não valor – nenhum object, nenhuma instância – com um tipo de método. Como mencionado acima, um valor de método na verdade tem um tipo de function . Um tipo de método é uma declaração def – tudo sobre um def exceto seu corpo.

Declarações e Definições de Valor e Declarações e Definições de Variável são declarações val e var , incluindo tanto o tipo quanto o valor – que podem ser, respectivamente, Tipo de Função e Funções Anônimas ou Valores de Método . Observe que, na JVM, esses (valores do método) são implementados com o que o Java chama de “methods”.

Uma declaração de function é uma declaração de def , incluindo tipo e corpo . A parte do tipo é o Tipo de Método e o corpo é uma expressão ou um bloco . Isso também é implementado na JVM com o que o Java chama de “methods”.

Finalmente, uma function anônima é uma instância de um tipo de function (ou seja, uma instância do traço FunctionN ), e um valor de método é a mesma coisa! A distinção é que um Method Value é criado a partir de methods, seja postfixando um sublinhado ( m _ é um valor de método correspondente à “declaração de function” ( def ) m ), ou por um processo chamado eta-expansion , que é como um conversão automática de método para function.

Isso é o que dizem as especificações, então deixe-me colocar isso de cara: nós não usamos essa terminologia! Isso leva a muita confusão entre a chamada “declaração de function” , que é uma parte do programa (capítulo 4 – declarações básicas) e “function anônima” , que é uma expressão, e “tipo de function” , que é, bem um tipo – uma característica.

A terminologia abaixo, e usada por programadores Scala experientes, faz uma mudança da terminologia da especificação: em vez de dizer declaração de function , dizemos método . Ou até mesmo declaração de método. Além disso, notamos que as declarações de valor e as declarações de variables também são methods para fins práticos.

Então, dada a mudança acima na terminologia, aqui está uma explicação prática da distinção.

Uma function é um object que inclui uma das características da FunctionX , como Function0 , Function1 , Function2 , etc. Ela também pode include PartialFunction , que na verdade estende Function1 .

Vamos ver a assinatura de tipo para um desses traços:

 trait Function2[-T1, -T2, +R] extends AnyRef 

Esta característica tem um método abstrato (também tem alguns methods concretos):

 def apply(v1: T1, v2: T2): R 

E isso nos diz tudo o que há para saber sobre isso. Uma function tem um método apply que recebe N parâmetros dos tipos T1 , T2 , …, TN e retorna algo do tipo R É contra-variante nos parâmetros que recebe e co-variante no resultado.

Essa variação significa que um Function1[Seq[T], String] é um subtipo de Function1[List[T], AnyRef] . Ser um subtipo significa que pode ser usado no lugar dele. Pode-se ver facilmente que, se vou chamar f(List(1, 2, 3)) e esperar um AnyRef volta, qualquer um dos dois tipos acima funcionaria.

Agora, qual é a similaridade de um método e uma function? Bem, se f é uma function e m é um método local para o escopo, ambos podem ser chamados assim:

 val o1 = f(List(1, 2, 3)) val o2 = m(List(1, 2, 3)) 

Essas chamadas são realmente diferentes, porque a primeira é apenas um açúcar sintático. Scala expande para:

 val o1 = f.apply(List(1, 2, 3)) 

O que, claro, é uma chamada de método no object f . As funções também têm outros açúcares sintáticos em sua vantagem: literais de function (dois deles, na verdade) e assinaturas de tipo (T1, T2) => R Por exemplo:

 val f = (l: List[Int]) => l mkString "" val g: (AnyVal) => String = { case i: Int => "Int" case d: Double => "Double" case o => "Other" } 

Outra semelhança entre um método e uma function é que o primeiro pode ser facilmente convertido no último:

 val f = m _ 

Scala expandirá isso , assumindo que m type é (List[Int])AnyRef em (Scala 2.7):

 val f = new AnyRef with Function1[List[Int], AnyRef] { def apply(x$1: List[Int]) = this.m(x$1) } 

No Scala 2.8, ele realmente usa uma class AbstractFunction1 para reduzir o tamanho das classs.

Observe que não é possível converter o inverso – de uma function para um método.

Os methods, no entanto, têm uma grande vantagem (bem, dois – eles podem ser um pouco mais rápidos): eles podem receber parâmetros de tipo . Por exemplo, enquanto f acima pode necessariamente especificar o tipo de List que recebe ( List[Int] no exemplo), m pode parametrizá-lo:

 def m[T](l: List[T]): String = l mkString "" 

Acho que isso praticamente cobre tudo, mas ficarei feliz em complementá-lo com respostas para quaisquer perguntas que possam permanecer.

Uma grande diferença prática entre um método e uma function é o que significa return . return only retorna de um método. Por exemplo:

 scala> val f = () => { return "test" } :4: error: return outside method definition val f = () => { return "test" } ^ 

Retornar de uma function definida em um método faz um retorno não local:

 scala> def f: String = { | val g = () => { return "test" } | g() | "not this" | } f: String scala> f res4: String = test 

Considerando que retornando de um método local só retorna desse método.

 scala> def f2: String = { | def g(): String = { return "test" } | g() | "is this" | } f2: String scala> f2 res5: String = is this 

function Uma function pode ser invocada com uma lista de argumentos para produzir um resultado. Uma function possui uma lista de parâmetros, um corpo e um tipo de resultado. Funções que são membros de um object de class, característica ou singleton são chamadas de methods . Funções definidas dentro de outras funções são chamadas de funções locais. Funções com o tipo de resultado da unidade são chamadas de procedimentos. Funções anônimas no código-fonte são chamadas de literais de function. No tempo de execução, literais de function são instanciados em objects chamados valores de function.

Programação no Scala Second Edition. Martin Odersky – Lex Colher – Bill Venners

Vamos dizer que você tem uma lista

 scala> val x =List.range(10,20) x: List[Int] = List(10, 11, 12, 13, 14, 15, 16, 17, 18, 19) 

Definir um método

 scala> def m1(i:Int)=i+2 m1: (i: Int)Int 

Definir uma function

 scala> (i:Int)=>i+2 res0: Int => Int =  scala> x.map((x)=>x+2) res2: List[Int] = List(12, 13, 14, 15, 16, 17, 18, 19, 20, 21) 

Método aceitando o argumento

 scala> m1(2) res3: Int = 4 

Definindo Função com val

 scala> val p =(i:Int)=>i+2 p: Int => Int =  

O argumento para funcionar é opcional

  scala> p(2) res4: Int = 4 scala> p res5: Int => Int =  

O argumento para o método é obrigatório

 scala> m1 :9: error: missing arguments for method m1; follow this method with `_' if you want to treat it as a partially applied function 

Verifique o seguinte tutorial que explica a passagem de outras diferenças com exemplos como outro exemplo de diff com a function do método Vs, usando a function como variables, criando a function que retornou a function

Funções não suportam padrões de parâmetro. Métodos fazem. A conversão de um método para uma function perde os padrões dos parâmetros. (Scala 2.8.1)

Há um bom artigo aqui do qual a maioria das minhas descrições são tiradas. Apenas uma breve comparação de funções e methods sobre o meu entendimento. Espero que ajude:

Funções : Eles são basicamente um object. Mais precisamente, funções são objects com um método apply; Portanto, eles são um pouco mais lentos que os methods devido à sobrecarga. É semelhante aos methods estáticos no sentido de serem independentes de um object a ser invocado. Um exemplo simples de uma function é como abaixo:

 val f1 = (x: Int) => x + x f1(2) // 4 

A linha acima não é nada, exceto atribuir um object a outro como object1 = object2. Na verdade, o object2 em nosso exemplo é uma function anônima e o lado esquerdo recebe o tipo de object por causa disso. Portanto, agora f1 é um object (Function). A function anônima é na verdade uma instância de Function1 [Int, Int] que significa uma function com 1 parâmetro do tipo Int e valor de retorno do tipo Int. Chamar f1 sem os argumentos nos dará a assinatura da function anônima (Int => Int =)

Métodos : Eles não são objects, mas atribuídos a uma instância de uma class, ou seja, um object. Exatamente o mesmo que o método em java ou funções de membro em c ++ (como Raffi Khatchadourian apontou em um comentário para esta questão ) e etc. Um exemplo simples de um método é como abaixo:

 def m1(x: Int) = x + x m1(2) // 4 

A linha acima não é uma atribuição de valor simples, mas uma definição de um método. Quando você invoca este método com o valor 2 como a segunda linha, o x é substituído por 2 e o resultado será calculado e você obtém 4 como uma saída. Aqui você receberá um erro se simplesmente escrever m1 porque é o método e precisa do valor de input. Usando _ você pode atribuir um método a uma function como abaixo:

 val f2 = m1 _ // Int => Int =