Variáveis


Como a maioria das outras linguagens de programação, o Pony permite que você armazene dados em variáveis. Há alguns tipos diferentes de variáveis que têm vidas diferentes e são usadas para finalidades ligeiramente diferentes.

Variáveis locais

As variáveis locais no Pony funcionam muito como funcionam em outras linguagens, permitindo armazenar valores temporários enquanto se realizam os cálculos. As variáveis locais vivem dentro de um trecho de código (elas são locais para esse trecho) e são criadas toda vez que esse trecho de código é executado e descartado quando ele é completado.

Para definir uma variável local é usada a palavra-chave var (também pode ser usada, mas chegaremos a ela mais tarde). Logo após a var vem o nome da variável, e então você pode (opcionalmente) colocar um : seguido do tipo da variável. Por exemplo:

var x: String = "Hello"

Aqui, estamos atribuindo a String literal "Hello" a x. Você não precisa dar um valor para a variável ao defini-la: você pode atribuir uma mais tarde, se preferir. Se você tentar ler o valor de uma variável antes de atribuí-la, o compilador reclamará ao invés de permitir o temido bug não-inicializado da variável.

Cada variável tem um tipo, mas você não precisa especificá-lo na declaração se você fornecer um valor inicial. O compilador usará automaticamente o tipo do valor inicial da variável. As seguintes definições de x, y e z são todas efetivamente idênticas.

var x: String = "Hello"
var y = "Hello"
var z: String
z = "Hello"

Posso perder tanto o tipo quanto o valor inicial de uma variável? Não. O compilador vai reclamar que não pode descobrir um tipo para essa variável.

Todos os nomes de variáveis locais começam com uma letra minúscula. Se você quiser, pode terminá-las com um prime ' (ou mais de um) que é útil quando você precisa de uma segunda variável com quase o mesmo significado que a primeira. Por exemplo, você pode ter uma variável chamada "tempo" e outra chamada "tempo".

O pedaço de código em que uma variável vive é conhecido como seu escopo. Exatamente qual é seu escopo depende de onde é definido. Por exemplo, o escopo de uma variável definida dentro da expressão "if" de uma declaração é essa expressão. Ainda não analisamos se as declarações, mas elas são muito parecidas com todas as outras linguagens.

if a > b then
var x = "a is bigger"
env.out.print(x) // OK
end
env.out.print(x) // Illegal

As variáveis só existem desde quando são definidas até o final do escopo atual. Para nossa variável x este é o final no final da expressão de então: depois disso, ela não pode ser usada.

Var vs. let

As variáveis locais são declaradas com um var ou com um let. Usar var significa que a variável pode ser atribuída e reatribuída quantas vezes você desejar. Usar let significa que a variável só pode ser atribuída uma vez.

var x: U32 = 3
let y: U32 = 4
x = 5 // OK
y = 6 // Error, y is let

Usar let em vez de var também significa que a variável tem que ser atribuída imediatamente.

let x: U32 = 3 // Ok
let y: U32 // Error, can't declare a let local without assigning to it
y = 6 // Error, can't reassign to a let local

Note que uma variável declarada com let apenas restringe a reatribuição, e não influencia a mutabilidade do objeto ao qual ela se refere. Este é o trabalho das capacidades de referência, explicado mais adiante neste tutorial.

Você nunca precisa declarar variáveis como let, mas se você sabe que nunca vai mudar o que uma variável referencia, então usar let é uma boa maneira de detectar erros. Ele também pode servir como um comentário útil, indicando que o que é referenciado não deve ser alterado.

Campos

No Pony, os campos são variáveis que vivem dentro de objetos. Eles funcionam como campos em outras linguagens orientadas a objetos. Os campos têm a mesma vida útil que o objeto em que estão, em vez de serem escopados. Eles são criados pelo construtor do objeto e descartados junto com o objeto.

Se o nome de um campo começa com "_", ele é privado. Isso significa que somente o tipo em que o campo está pode ter código que leia ou escreva esse campo. Caso contrário, o campo é público e pode ser lido ou escrito de qualquer lugar.

Assim como as variáveis locais, os campos podem ser var ou let. Entretanto, as regras para a atribuição de campos diferem um pouco da atribuição de variáveis. Não importa o tipo do campo (var ou let), também: 1. um valor inicial tem que ser atribuído em sua definição ou 2. um valor inicial tem que ser atribuído no método do construtor.

No exemplo abaixo, o valor inicial dos dois campos da classe Wombat é atribuído no nível de definição:

class Wombat
let name: String = "Fantastibat"
var _hunger_level: U32 = 0

Alternativamente, estes campos poderiam ser atribuídos no método de construção:

class Wombat
let name: String
var _hunger_level: U32
new create(hunger: U32) =>
name = "Fantastibat"
_hunger_level = hunger

Se a tarefa não for feita no nível de definição ou no construtor, um erro é levantado pelo compilador. Isto é verdade tanto para os campos var como para os campos let.

Favor observar que a atribuição de um valor a um campo tem que ser explícita. O exemplo abaixo levanta um erro quando compilado, mesmo quando o campo é do tipo var:

class Wombat
let name: String
var _hunger_level: U64
new ref create(name': String, level: U64) =>
name = name'
set_hunger_level(level)
// Error: field _hunger_level left undefined in constructor
fun ref set_hunger_level(hunger_level: U64) =>
_hunger_level = hunger_level

Veremos mais adiante na seção Métodos que uma classe pode ter vários construtores. Por enquanto, basta lembrar que se a atribuição de um campo não for feita no nível de definição, ela tem que ser feita em cada construtor da classe à qual o campo pertence.

Quanto às variáveis, usar var significa que um campo pode ser atribuído e reatribuído quantas vezes se desejar na classe. Usar let significa que o campo só pode ser atribuído uma vez.

class Wombat
let name: String
var _hunger_level: U64
new ref create(name': String, level: U64) =>
name = name'
_hunger_level = level
fun ref set_hunger_level(hunger_level: U64) =>
_hunger_level = hunger_level // Ok, _hunger_level is of var type
fun ref set_name(name' : String) =>
name = name' // Error, can't assign to a let definition more than once

As declarações de campo podem aparecer após os métodos? Não. Se var ou deixar as palavras-chave aparecerem após uma diversão ou serem declaradas, elas serão tratadas como variáveis dentro do corpo do método e não como campos dentro do tipo de declaração. Como resultado, os campos devem aparecer antes dos métodos no tipo de declaração

Campos incorporados

Ao contrário das variáveis locais, alguns tipos de campos podem ser declarados usando embed. Especificamente, apenas classes ou estruturas podem ser incorporadas - interfaces, traços, primitivas e tipos numéricos não podem. Um campo declarado usando embed é semelhante a um declarado usando let, mas no nível de implementação, a memória para a classe embedded é disposta diretamente dentro da classe externa. Contraste isto com let ou var, onde a implementação usa ponteiros para referenciar a classe do campo. Os campos embutidos podem ser passados para outras funções exatamente da mesma forma que os campos let ou var. Os campos incorporados devem ser inicializados a partir de uma expressão construtora.

Por que eu usaria embed? embed evita uma indireção de ponteiro ao acessar um campo e uma alocação de memória separada ao criar esse campo. Por padrão, é aconselhável usar o embed, se possível. Entretanto, como um campo embutido é alocado ao lado de seu objeto pai, referências externas ao campo proíbem a coleta de lixo do pai, o que pode resultar em maior uso de memória se um campo sobreviver a seu pai. Use let se esta for uma preocupação para você.

Variaveis Globais

Algumas linguagens de programação têm variáveis globais que podem ser acessadas de qualquer lugar no código. Que má idéia! Pony não tem variáveis globais em absoluto.

Shadowing

Algumas linguagens de programação permitem declarar uma variável com o mesmo nome de uma variável existente, e depois há regras sobre qual delas você recebe. Isto é chamado de sombra, e é uma fonte de bugs. Se você acidentalmente sombreia uma variável no Pony, o compilador irá reclamar.

Se você precisar de uma variável com quase o mesmo nome, você pode usar um " Prime ".