/ nim-programmin

The zen of Nim #2

Pois é, vamos continuar na nossa jornada Nim da coisa, vamos começar de onde paramos! Agora é importante matar essa sua curiosidade do idioma, fazer aquele famoso Hello, World para não ser amaldiçoado.

Então vamos lá, porém, diferente de você que já me acompanha, não vou fazer aquela explicação deep do código, apenas... Vamos lá:

echo "Hello, World!"

E é isso, o que você quer que eu explique ai? Tem o que explicar? Bom, deixo claro que esse é o nível de verbrosidade que eu quero ver por aqui, que coisa linda! Sabe outra curiosidade? Comparado a Go, Rust, Haskell e D, temos o menor binário do clássico Hello, World! que existe!

Por ai você já tira um pouco do poder dessa linguagem, que meus amigos, está me surpreendendo cada vez mais nas minhas brincadeiras de desenvolvimento com JavaScript. Se você não sabe como compilar e gerar um executável desse cara, vem comigo:

Parece que temos um Hello, World aqui! Simplicidade até nos argumentos de linha de comando do compilador, isso foi uma das coisas que mais gostei quando comecei a estudar a linguagem, mas não se engane, as possibilidades do sistema de compilação da linguagem é exorbitantemente grande, você tem o controle de tudo, assim como temos no clang!

1.1 Entendendo melhor a linguagem

De muitas maneiras, Nim é muito inovador. Muitos dos recursos de Nim não podem ser encontrados em nenhuma outra linguagem de programação. Se você gosta de aprender novas linguagens de programação, especialmente aqueles com características interessantes e únicas, então Nim é definitivamente o idioma para você.

Vamos ver agora, alguns dos recursos que tornam Nim tão exclusivas:

  • Metaprogramação - Você molda a linguagem da maneira como quer, e isso é somente uma das inúmeras características que esse tópico contém!
  • Flexibilidade na definição de nomes para estruturas, aqui tanto camelCase quanto snake_case são bem vindos!
  • Um sistema de tipos lindo! Muito bem projetado e executado, aqui os genéricos tomam conta do código, tornando a semântica muito bem vista!
  • Você pode compilar para C ou C++ (além de JavaScript), isso torna seus códigos muito mais portáveis!
  • Aqui o coletor de lixo é definido por você, não existe somente uma diretriz de funcionamento!

Agora que temos algumas das principais caracteristicas do núcleo da linguagem, vamos avançar em cada um dos tópicos apresentados.

1.2 Metaprogramação

Algo que sinceramente me deixa extremamente decepcionado lá no C#, é algo que foi perfeitamente pensado e implementado dentro do Nim, a metaprogramção em diversos cenários, é de fato um recurso extremamente poderoso, basicamente, você pode ler, gerar, analisar e transformar o código-fonte.

Esse poderoso recurso não é uma invenção da linguagem, porém, dentre todas as demais, sem sombra de dúvidas Nim é a que melhor implementa essa ideia, sabe aquela extensibilidade toda que o Lisp proporciona, aqui você vai ter a mesma coisa, porém de forma mais amigável!

Dentro da metaprogramação, você pode tratar o código como dados com uma sintaxe abstraída desse fonte, você pode manipular o código existente, gerando novos recursos para sua aplicação através de templates definidos, e executados em tempo de compilação.

Mas vamos com calma, esse tópico é bem avançado! Por esse motivo vamos começar bem de vagar, não colocando o burro na frente dos bois, o nosso intuito aqui, é conhecer melhor a linguagem e seus recursos, e não de fato dominar o design da arquitetura de linguagens de programação.

Vamos partir agora, para algo mais prático, com DSLs, as famosas linguagens de domínio especifico, fazer algo assim em linguagens como Java e C# geram uma quantidade ridícula de lixo em seu código, a capacidade de implementação de DSLs permite que você "crie uma linguagem de programação" para resolver um problema extremamente específico.

1.3 Flexibilidade e coesão no design de soluções

Pois é, Nim é famosa por manter sua estrutura de códigos concisas e bonitas, raramente você vai se deparar com algum código que foge dos padrões de formatação propostos pela linguagem, algo parecido com as PEPs que temos no Python, vamos ver isso mais profundamente mais no futuro, mas vale a pena já deixa-los com aquela dúvida sobre o assunto! (hahahaha)

Outro recurso importantíssimo, é a insensibilidade da linguagem para o padrões de escrita de nomes de variáveis, métodos, tipos e qualquer tipo de coisa. Lembra da sua linguagem? Que se de fato você consome algo definido em snake_case, aquilo permanentemente será snake_case, em Nim você tem a flexibilidade de usar o que quiser, mesmo em casos que seu alvo é escrito em camelCase, e você deseja consumi-lo com snake_case.

Ficou confuso com essa última né? Bom, vamos ver mais detalhadamente no próximo capitulo da série, onde vamos colocar a mão na massa, mas só para você entender melhor sobre oque se trata, basicamente, se temos um nome de função como toUper(), você pode consumi-lo sem nenhum tipo de problema com diferentes name cases, como to_upper() ou o próprio toUper().

Para você entender melhor, os identificadores são iguais para o Nim, então use o que for mais fácil para você!

Mas calma ai! Não é pela existência dessa flexibilidade, que você vai se tornar um vacilão na linguagem, vamos entender um pouco mais a fundo isso, quando o Nim vai entender seu código, e se depara com essas funções, ele considera o caso do primeiro caractere, mas não se preocupa com o caso do resto dos caracteres do identificador, ignorando os sublinhados também.

Como resultado, os identificadores do exemplo (toUpper() e ToUpper()) não são iguais, isso pois o caso do primeiro caractere difere. Isso permite que os nomes dos tipos sejam distinguidos dos nomes das variáveis, porque, por convenção, os nomes dos tipos devem começar com uma letra maiúscula e os nomes das variáveis devem começar com uma letra minúscula.

Ainda sim temos flexibilidade, mas com boas práticas! Para você entender melhor, vamos ver esse exemplo aqui:

type
    Pessoa = object
    nome: string

let uliana = Pessoa(nome: "Uli")

echo "Seu nome é ", uliana.nome, "!"

Aqui, temos todas as boas práticas respeitadas, o tipo Pessoa é definido com o primeiro caractere maiúsculo, em quanto suas propriedades com o primeiro minúsculo! E o nome da nossa variável uliana, também é definida da mesma forma. Volto a dizer, esse é o tipo de verbrosidade que quero ver aqui no blog!

1.4 Um poderoso sistema de tipos

De nada basta ter falado bem da linguagem até agora, sendo que sua definição de sistema de tipos seja algo porco igual temos em C ou no próprio C#, acredito que o principal dilema das linguagens mais aceitas é justamente a diferença enter seus sistema de tipos, frisei nos exemplos dos C's da vida por implementar uma filosofia similar, porém de maneira porca.

Claro, isso de acordo com o peso das features de onde se implementa, não podemos cobrar tanto o C por ser a pior linguagem para realizar tarefas X e Y, vamos respeitar o vovô, sem ele não teríamos Nim, e provavelmente, a maquina de registradores funcionaria de uma maneira totalmente diferente do que temos hoje (em nível alto, claro.)

O objetivo principal de um sistema de tipo é reduzir as oportunidades de erros em seus programas. Outros benefícios que um sistema de tipo bem implementado fornece, são certas otimizações de compilação e melhor documentação de código.

Sobre a documentação, fica tranquilo que vamos tratar melhor sobre como funciona ela dentro do Nim, mas antes disso, existe uma série de coisas que ainda quero falar com vocês. Vamos partir para um pouco de teoria, para melhor entender de fato o que esse tópico abrange dentro da computação moderna.

As principais categorias utilizadas para classificar os sistemas de tipo são: estáticas e dinâmicas. A maioria das linguagens de programação cai entre os dois extremos e incorpora ideias de ambos. Isso ocorre porque os sistemas de tipo estático e dinâmico requerem certas compensações. A digitação estática encontra mais erros no tempo de compilação, mas também diminui a velocidade na qual os programas podem ser gravados. A tipagem dinâmica é o contrário.

Sobre o idioma, ele é estático, mas saindo um pouco do que é conhecido por tipagem estática, especialmente do implementado em diversas outras linguagens, Nim absorve um pouco dos dois mundos, e fazem eles conversar de maneira harmoniosa, tudo isso para tornar o desenvolvimento mais rápido e seguro.

A inferência de tipos é um bom exemplo disso, podemos classifica-la básicamente em um sistema onde os tipos podem ser resolvidos pelo compilador sem a necessidade de você escrever sua sintaxe explicita(embora você possa escolher).

Por causa disso, seu programa pode ser livre de erros e, no entanto, sua velocidade de desenvolvimento não é prejudicada. Nim também incorpora alguns recursos dinâmicos de verificação de tipo, como informações de tipo de tempo de execução, que permitem o envio dinâmico de funções.

Um dos fatores que definem Nim como segura, é justamente o fato da garantia do sistema de tipos, dessa forma seu programa é verificado, e consequentemente, temos mais segurança na memória que tal aplicação pode consumir.

Algumas linguagens de programação, como C, não são seguras para a memória, isso porque permitem que os programas acessem a memória que não foi atribuída para seu uso. Outras linguagens de programação são seguras para a memória à custa de não permitir que os programas acessem detalhes de memória de baixo nível, o que, em alguns casos, é necessário (como o próprio C#).

Nim combina os dois: é seguro para a memória, desde que você não use nenhum tipo inseguro no seu programa. O suporte a esses recursos inseguros torna a Nim uma poderosa linguagem de programação de sistemas.

Vale lembrar, que quando você consumir algo como prt para interoperabilidade entre o C e o idioma, se código se torna inseguro, mas isso de fato não é algo ruim dependendo do estilo de programação do programa que você está consumindo na outra linguagem.

Voltando a falar sobre aquela forçada de barra que o Nim faz para o desenvolvedor da linguagem, que é justamente o fato de frisar a segurança, alguns dos recursos fornecidos pela biblioteca padrão da linguagem são:

  • As matrizes são verificadas por limites em tempo de compilação ou em tempo de execução quando as verificações de tempo de compilação não são possíveis, impedindo tanto os leaks de memória de buffer (like buffer overflow) quanto o buffer overreads.
  • Ponteiros não são compátiveis com tipos de referência isso pois eles são totalmente gerenciados pelo coletor de lixo do idioma. Isso evita problemas como ponteiros pendurados (dangling) e outros problemas de memória relacionados ao gerenciamento de memória manual.- As variáveis são sempre inicializadas pelo Nim para seus valores padrão, o que evita variáveis contendo dados inesperados e corrompidos.

Agora, imagina você não ter que escrever sobrecarga para suas funções que podem aceitar mais de um tipo de sistema para realizar determinada ação, pois é, com Nim isso é possível, uma função, para vários tipos. Isso se classifica no poder da programação genérica, e o melhor que essa feature no Nim, não sacrificam a segurança do código!

1.5 Conclusão

Bom, aqui vimos mais alguns recursos interessantes da linguagem, fica tranquilo, já vamos começar a escrever alguns códigos com Nim, por enquanto é bom você entender o que de fato muda tanto dela em relação as demais, especialmente sobre os recursos da sua arquitetura e design.

Ainda vamos ter um pouco de bagagem teórica, mas garanto que os senhores iram gostar do que está por vir, e para manter a consistência das postagens, vou tentar sempre deixar o conteúdo entre 1500 ~ 2000 palavras, para não ser uma leitura desgastante (e nem uma revisão).

Então é isso que temos para hoje! Espero você no próximo capitulo dessa série!