Métodos

Todo código Pony que realmente faz algo, ao invés de definir tipos, etc., aparece em blocos nomeados que são referidos como métodos. Há três tipos de métodos: funções, construtores e comportamentos. Todos os métodos são anexados a definições de tipo (por exemplo, classes) - não há funções globais.

Comportamentos são usados para lidar com mensagens assíncronas enviadas aos atores, o que vimos no capítulo "Tipos" quando falamos sobre os atores.

Posso ter algum código fora de qualquer método, como eu faço em Python? Não. Todo código Pony deve estar dentro de um método.

Funções

As funções em Pony são muito parecidas com funções (ou métodos) em outras linguagens. Elas podem ter 0 ou mais parâmetros e valores 0 ou 1 de retorno. Se o tipo de retorno for omitido, então a função terá um valor de retorno de Nenhum.

class C
fun add(x: U32, y: U32): U32 =>
x + y
fun nop() =>
add(1, 2) // Pointless, we ignore the result

Os parâmetros da função (se houver) são especificados entre parênteses após o nome da função. Funções que não assumem nenhum parâmetro ainda precisam ter os parênteses.

A cada parâmetro é dado um nome e um tipo. Em nosso exemplo, a função adicionar tem 2 parâmetros, x e y, ambos do tipo U32. Os valores passados para uma chamada de função (os 1 e 2 em nosso exemplo) são chamados de argumentos e quando a chamada é feita, eles são avaliados e atribuídos aos parâmetros. Os parâmetros não podem ser atribuídos dentro da função - eles são efetivamente declarados alugados.

Após os parâmetros vem o tipo de retorno. Se nada for devolvido, isto é simplesmente omitido. Após o valor de retorno, há um => e então finalmente o corpo da função. O valor retornado é simplesmente o valor do corpo da função (lembre-se que tudo é uma expressão), que é simplesmente o valor do último comando na função.

Se você quiser sair cedo de uma função, então use o comando de retorno. Se a função tem um tipo de retorno, então você precisa fornecer um valor para retornar. Se a função não tem um tipo de retorno, então o retorno deve aparecer por si só, sem um valor. Posso sobrecarregar funções por tipo de argumento? Não, você não pode ter vários métodos com o mesmo nome no mesmo tipo.

Construtores

Os construtores de pônei são usados para rubricar objetos recém-criados, como em muitos idiomas. Entretanto, ao contrário de muitas línguas, os construtores de Pony são nomeados para que você possa ter quantos quiser, tomando os parâmetros que quiser. Por convenção, o construtor principal de cada tipo (se houver tal coisa para um determinado tipo) é chamado de create.

class Foo
var _x: U32
new create() =>
_x = 0
new from_int(x: U32) =>
_x = x

O objetivo de um construtor é estabelecer o estado interno do objeto que está sendo criado. Para garantir que isto seja feito, os construtores devem rubricar todos os campos do objeto que está sendo construído.

Posso sair cedo de um construtor? Sim. Só então use o comando de retorno sem um valor. O objeto já deve estar em um estado legal para fazer isso.

Chamadas

Como em muitas outras linguagens, os métodos em Pony são chamados fornecendo os argumentos dentro dos parênteses após o nome do método. Os parênteses são necessários mesmo que não haja argumentos sendo passados para o método.

class Foo
fun hello(name: String): String =>
"hello " + name
fun f() =>
let a = hello("Fred")

Os construtores são normalmente chamados em um tipo, especificando o tipo que deve ser criado. Para isso, basta especificar o tipo, seguido de um ponto, seguido do nome do construtor que se deseja chamar.

class Foo
var _x: U32
new create() =>
_x = 0
new from_int(x: U32) =>
_x = x
class Bar
fun f() =>
var a: Foo = Foo.create()
var b: Foo = Foo.from_int(3)

As funções são sempre chamadas sobre um objeto. Novamente basta especificar o objeto, seguido por um ponto, seguido pelo nome da função a ser chamada. Se o objeto a ser chamado for omitido, então o objeto atual é usado (ou seja, este).

class Foo
var _x: U32
new create() =>
_x = 0
new from_int(x: U32) =>
_x = x
fun get(): U32 =>
_x
class Bar
fun f() =>
var a: Foo = Foo.from_int(3)
var b: U32 = a.get()
var c: U32 = g(b)
fun g(x: U32): U32 =>
x + 1

Os construtores também podem ser chamados a uma expressão. Aqui um objeto é criado do mesmo tipo que a expressão especificada - isto equivale a especificar diretamente o tipo.

class Foo
var _x: U32
new create() =>
_x = 0
new from_int(x: U32) =>
_x = x
class Bar
fun f() =>
var a: Foo = Foo.create()
var b: Foo = a.from_int(3)

Podemos até mesmo reutilizar o nome da variável na expressão de atribuição para chamar o construtor.

class Bar
fun f() =>
var a: Foo = a.create()

Aqui especificamos que var a é do tipo Foo, então procedemos a usar a para chamar o construtor, create(), para objetos do tipo Foo.

Argumentos padrão

Ao definir um método, você pode fornecer valores padrão para qualquer um dos argumentos. O chamador tem então a opção de usar os valores que você forneceu ou fornecer os seus próprios valores. Os valores padrão dos argumentos são especificados com um = após o nome do parâmetro.

class Coord
var _x: U32
var _y: U32
new create(x: U32 = 0, y: U32 = 0) =>
_x = x
_y = y
class Bar
fun f() =>
var a: Coord = Coord.create() // Contains (0, 0)
var b: Coord = Coord.create(3) // Contains (3, 0)
var c: Coord = Coord.create(3, 4) // Contains (3, 4)

Eu tenho que fornecer valores padrão para todos os meus argumentos? Não, você pode fornecer valores padrão para tantos, ou poucos, quantos você quiser.

Argumentos nomeados

Até agora, quando chamamos os métodos, sempre demos todos os argumentos em ordem. Isto é conhecido como o uso de argumentos posicionais. Entretanto, também podemos especificar os argumentos em qualquer ordem, especificando seus nomes. Isto é conhecido como uso de argumentos nominativos.

Para chamar um método usando argumentos nomeados, a palavra-chave é usada, seguida pelos argumentos nomeados e seus valores.

class Coord
var _x: U32
var _y: U32
new create(x: U32 = 0, y: U32 = 0) =>
_x = x
_y = y
class Bar
fun f() =>
var a: Coord = Coord.create(3, 4) // Contains (3, 4)
var b: Coord = Coord.create(where y = 4, x = 3) // Contains (3, 4)

Observe como em b acima, os argumentos foram dados fora de ordem usando onde foram seguidos usando o nome dos argumentos. Devo especificar onde para cada argumento nomeado? Não. Deve haver apenas um onde em uma chamada de método.

Os argumentos nominais e posicionais podem ser usados em conjunto em uma única chamada. Basta começar com os argumentos posicionais que você deseja especificar, depois um onde e finalmente os argumentos nomeados. Mas tenha cuidado, cada argumento deve ser especificado apenas uma vez.

Argumentos predefinidos também podem ser usados em combinação com argumentos posicionais e argumentos nominativos - basta perder qualquer argumento para o qual você queira usar o padrão.

class Foo
fun f(a: U32 = 1, b: U32 = 2, c: U32 = 3, d: U32 = 4, e: U32 = 5): U32 =>
0
fun g() =>
f(6, 7 where d = 8)
// Equivalent to:
f(6, 7, 3, 8, 5)

Posso chamar usando argumentos posicionais, mas perder o primeiro? Não. Se você usar argumentos posicionais, eles devem ser os primeiros na chamada.

Encadeamento

O método de encadeamento permite que você acorrente chamadas em cadeia sobre um objeto sem exigir o método para devolver seu receptor. A sintaxe para chamar um método e encadear o receptor é object.>method(), que é mais ou menos equivalente a (object.method() ; objeto). Encadear um método descarta seu valor de retorno normal.

primitive Printer
fun print_two_strings(out: StdStream, s1: String, s2: String) =>
out.>print(s1).>print(s2)
// Equivalent to:
out.print(s1)
out.print(s2)
out

Observe que o último ".>" em uma cadeia pode ser um "."se o valor de retorno da última chamada import.

interface Factory
fun add_option(o: Option)
fun make_object(): Object
primitive Foo
fun object_wrong(f: Factory, o1: Option, o2: Option): Object =>
f.>add_option(o1).>add_option(o2).>make_object() // Error! The expression returns a Factory
fun object_right(f: Factory, o1: Option, o2: Option): Object =>
f.>add_option(o1).>add_option(o2).make_object() // Works. The expression returns an Object

Métodos anônimos

O Pony tem métodos anônimos (ou Lambdas). Eles são parecidos com isto:

use "collections"
actor Main
new create(env: Env) =>
let list_of_numbers = List[U32].from([1; 2; 3; 4])
let is_odd = {(n: U32): Bool => (n % 2) == 1}
for odd_number in list_of_numbers.filter(is_odd).values() do
env.out.print(odd_number.string())
end

Eles são apresentados mais detalhadamente na seção objetos literais.

Métodos Private

No Pony, os nomes dos métodos começam com uma letra minúscula ou com um sublinhado seguido por uma letra minúscula.

Os métodos com um sublinhado principal são privados. Isto significa que eles só podem ser chamados por código dentro do mesmo pacote.

Os métodos sem um sublinhado principal são públicos e podem ser chamados por qualquer pessoa.

Posso começar meu nome de método com 2 (ou mais) underlines? Não. Se o primeiro caractere for um underline, então o segundo DEVE ser uma letra minúscula.

Precedência

Já falamos antes sobre a precedência das operadoras, e no Pony, as chamadas de método e os acessos de campo têm precedência maior do que qualquer operadora.

Para resumir, em expressões complexas:

  • As chamadas de método e os acessos de campo têm maior precedência do que qualquer operador.
  • Operadores unários têm precedência maior do que os operadores infix.
  • Ao misturar os operadores infix em expressões complexas, devemos usar parênteses para especificar explicitamente as precedências.