Programação Funcional

Conceitos de linguagens funcionais: uma breve introdução aos monads

O paradigma funcional vem ganhando cada vez mais espaço entre os desenvolvedores, quer seja pela sua naturalidade para lidar com problemas cada vez mais comuns em softwares de larga escala (como os aspectos de concorrência e paralelismo) ou por causa da sintaxe mais declarativa que as linguagens funcionais geralmente possuem. Mas, mesmo com essa popularização, o paradigma funcional ainda é visto com certo receio por uma série de razões, como a proximidade com a matemática (odiada por muitas pessoas) ou a grande quantidade de termos encarados como obscuros para quem não conhece o paradigma. Por isso, iniciei essa série de artigos abordando conceitos e termos típicos do paradigma funcional, tentando mostrar como as coisas podem ser muito mais simples do que parecem. O primeiro artigo dessa série discute sobre os princípios mais essenciais do paradigma funcional. Já o segundo artigo, onde o currying é discutido, pode ser acessado a partir daqui.
Neste artigo, vamos discutir um pouco sobre um conceito essencial ao paradigma funcional: os monads.

Relembrando alguns conceitos…

Antes de prosseguirmos aos monads de fato, vamos relembrar alguns pilares do paradigma funcional:

  • O paradigma funcional tem forte inspiração nos conceitos relacionados às funções matemáticas;
  • Um código escrito de maneira funcional é composto por funções que sempre vão receber ao menos um parâmetro e produzir uma saída;
  • No paradigma funcional, as funções devem ser determinísticas, ou seja: dado um valor para um parâmetro, a saída produzida deve ser sempre a mesma em qualquer momento e situação. Por exemplo: se temos uma função somar() e esta função recebe os parâmetros 2e 2, esta função deve sempre retornar 4, independente se ela for invocada hoje, amanhã, ano que vem ou no próximo milênio;
  • Linguagens funcionais evitam o que é chamado side effect ou transições de estado: quando você cria uma “variável” (geralmente chamada de binding em programação funcional), esta “variável” não pode ter seu valor alterado em nenhuma circunstância. A grosso modo, é como se você estivesse trabalhando o tempo inteiro com “constantes de verdade”.

Sendo assim, vamos imaginar que necessitamos de alguma estrutura de código que execute uma operação de elevação de um número ao cubo… Se adotarmos o JavaScript como linguagem, poderíamos criar um “método” que executasse essa operação:

const cubo = (n) => Math.pow(n, 3);

// Testes...
console.log(cubo(2)); // Resposta: 8

Por hora, tudo certo… Temos um trecho de código que segue todos os pilares do paradigma funcional.
Também poderíamos ter uma função que calculasse a raiz quadrada de um número… O código seria bem similar:

const raizQuadrada = (n) => Math.sqrt(n);

// Testes...
console.log(raizQuadrada(4)); // Resposta: 2

Por alguma razão, poderíamos precisar calcular a raiz quadrada de um número que foi elevado ao cubo… No caso acima, como ambas as funções recebem um parâmetro numérico e retornam um número, nós poderíamos encadear as chamadas destas funções, criando uma nova função como resultado da combinação das duas funções anteriores:

const raizQuadrada = (n) => Math.sqrt(n);
const cubo = (n) => Math.pow(n, 3);

const raizQuadradaDoCubo = (n) => raizQuadrada(cubo(n));

// Testes...
console.log(raizQuadradaDoCubo(4)); // Resposta: 8 (4 elevado ao cubo = 64; raiz de 64 = 8)

Esse código ficaria mais interessante ainda se criássemos uma função que fosse responsável exclusivamente por realizar essa composição de funções… Poderíamos ter algo similar ao trecho de código abaixo:

const juntar = (f, g) => (x) => f(g(x));

const cubo = (x) => Math.pow(x, 3);
const raizQuadrada = (x) => Math.sqrt(x);
const cuboDaRaizQuadrada = juntar(cubo, raizQuadrada);

// ...
console.log(cuboDaRaizQuadrada(4)); // Resultado: 8
F# (F Sharp) - Fundamentos
Curso de F# (F Sharp) - Fundamentos
CONHEÇA O CURSO

O trecho de código acima implementa quase tudo que vimos sobre programação funcional: tudo está baseado em valores imutáveis, temos várias funções que recebem pelo menos um parâmetro e retornam um determinado valor relacionado ao processamento do parâmetro, as funções são todas determinísticas e até temos o currying em ação aqui, já que a função juntar() retorna uma outra função, que é o resultado da composição das funções f() e g()! E veja a estrutura declarativa do código: o código é fluído e fácil de ser lido…
Mas esse código tem um problema crítico: o que aconteceria se passarmos undefined como argumento da função cuboDaRaizQuadrada()? Nesse caso, vamos acabar tendo o famoso NaN, o que é completamente errado.
Podemos corrigir isso colocando uma verificação de segurança nas funções cubo() e raizQuadrada():

const juntar = (f, g) => (x) => f(g(x)); 

const cubo = (x) => {
  if (x) {
    return Math.pow(x, 3)
  } else {
    return 0;
  }
};

const raizQuadrada = (x) => {
  if (x) {
    Math.sqrt(x);
  } else {
    return 0;
  }
}

const cuboDaRaizQuadrada = juntar(cubo, raizQuadrada);

// ...
console.log(cuboDaRaizQuadrada(undefined)); // Resultado: 0

Isso resolve o nosso problema. Mas já temos código idêntico espalhado entre vários lugares… E cada nova função que implementássemos, precisaria replicar essa verificação. Essa situação é terrível, quer seja pela legibilidade (que já começa a ficar prejudicada), quer seja pela manutenibilidade desse trecho simples de código… Nós precisamos de uma estrutura que encapsule esse processo de verificação que necessitamos… E é aqui que começam a entrar os monads.

Implementando o primeiro monad

Poderíamos escrever uma estrutura que encapsulasse esse valor e nos disesse se temos um valor válido em questão. Essa estrutura poderia ser definida da seguinte maneira:

const _talvez = (valor) => ({
    valor,
    nada() {
      return valor == null || valor == undefined;
    }
});

const x = _talvez(4);
console.log(x.valor); // 4
console.log(x.nada()); // false
const y = _talvez(undefined);
console.log(y.valor); // undefined
console.log(y.nada()); // true

Agora, temos a estrutura _talvez que adiciona comportamentos ao objeto valor, fazendo com que seja possível:

  • Encapsular o valor internamente, o expondo adequadamente através da propriedade valor;
  • Tratar o valor que é encapsulado, o identificando como um valor válido ou não através da função nada().

Essa é a definição mais essencial de um monad: uma estrutura que age como um wrapper em cima de um dado, expondo seu valor de maneira correta e controlando possíveis side-effects que sua manipulação pode causar (como o NaNque não esperávamos).
Nossa estrutura _talvez ainda precisa implementar pelo menos mais uma funcionalidade para chegar mais próxima ainda da definição clássica de um monad: o encadeamento de operações no valor monádico. Ou seja: um monad precisa prover uma maneira de estabelecer uma sequência de operações que deve ser aplicada ao valor que ele guarda (em nosso exemplo, o valor contido em valor dentro da estrutura _talvez).
Isso poderia ser implementado da seguinte maneira:

const _talvez = (valor) => ({
    valor,
    nada() {
      return valor == null || valor == undefined;
    },
    ligar(fn) {
      if (this.nada()){
        return _talvez(null);
      } else {
        return _talvez(fn(this.valor));
      }
    }
});
Desenvolvedor Front-end Pleno
Formação: Desenvolvedor Front-end Pleno
HTML, CSS e JavaScript são a base de toda a web. Tudo o que você está vendo aqui agora depende deste tripé. Nesta formação vamos conhecer assuntos mais avançados do HTML5, CSS3 e JavaScript, conhecer o NPM, trabalhar com o RxjS (base do framework Angular), testes com Jasmine e uma pequena introdução ao TypeScript.
CONHEÇA A FORMAÇÃO

Veja que agora criamos mais uma função em nossa estrutura _talvez. A função ligar() recebe uma função que pode ser aplicada ao valor armazenado pela estrutura _talvez, desde que este seja considerado um valor válido (ou seja, que a função nada() retorne false). Se a função puder ser aplicada, um nova estrutura _talvez (ou seja, um novo monad) é gerado, permitindo a ligação com outras funções. Veja que, caso tenhamos um valor monádico inválido (ou seja, nada() retornar true), um novo monad também é gerado, mas com o valor null, já que temos um valor inválido. Isso é chamado de automatic propagation.
Agora, podemos reescrever o nosso código que calcula a raiz de um número elevado ao cubo da seguinte forma, utilizando a nossa estrutura _talvez:

const juntar = (f, g) => (x) => f(g(x)); 

const _talvez = (valor) => ({
    valor,
    nada() {
      return valor == null || valor == undefined;
    },
    ligar(fn) {
      if (this.nada()){
        return _talvez(null);
      } else {
        return _talvez(fn(this.valor));
      }
    }
});

const cubo = (monad) => monad.ligar(n => Math.pow(n, 3));
const raizQuadrada = (monad) => monad.ligar(n => Math.sqrt(n));
const cuboDaRaizQuadrada = juntar(cubo, raizQuadrada);

const x = _talvez(4);
console.log(cuboDaRaizQuadrada(x).valor); // 8
const y = _talvez(null);
console.log(cuboDaRaizQuadrada(y).valor); // null
const z = _talvez(undefined);
console.log(cuboDaRaizQuadrada(z).valor); // null

Veja que nosso monad evitou um possível side-effect (que poderia acontecer com o NaN que podia ser retornado), além de ainda seguir todos os princípios funcionais vistos anteriormente. Nossa estrutura _talvez é de fato bem próxima a um monad.
Ah! Reparou a semelhança entre o método ligar() da estrutura _talvez com métodos comuns do JavaScript como o map()? Essa semelhança não acontece por acaso… Uma outra observação pertinente: se você já teve contato com alguma linguagem funcional (como F#, Haskell, Elixir, Scala, entre outras), vai também reparar que a nossa função juntar(), utilizada para compor funções, faz exatamente a mesma coisa que o pipe operator (|>) das linguagens funcionais. 😉

Eu realmente preciso conhecer monads?

A resposta é… Depende.
Se você não utiliza linguagens funcionais, não tem muito sentido estudar monads, tendo em vista que outros paradigmas (como o paradigma orientado a objetos) já quebram o conceito de monads só por se basearem em transições de estados. Eles podem até ajudar a reduzir a complexidade do código, mas outros paradigmas podem oferecer outras soluções mais interessantes através de outros design patterns.
Se você utiliza alguma linguagem funcional, a resposta ainda é depende. Existem linguagens que implementam o conceito de maneira mais “suave” por considerar o conceito e o termo muito “agressivos” ou obscuros demais, escondendo algumas complicações do desenvolvedor (é o caso do F# por exemplo, que dá o nome de workflow e computed expression a estruturas similares aos monads). Outras linguagens já exibem os conceitos de monads de maneira bem explícita, como o Haskell. Com certeza será interessante para qualquer desenvolvedor que trabalhe com o paradigma funcional conhecer pelo menos os aspectos essenciais dos monads, mas a profundidade deste estudo fatalmente estará atrelada à linguagem utilizada.
Monads constituem um conceito de fato mais complexo dentro do paradigma funcional. Tanto que ainda existem mais regras matemáticas que devem ser aplicadas para que determinada estrutura seja de fato considerada um monad. Mas, neste momento, não iremos entrar em uma discussão mais aprofundada sobre a definição dos monads. Porém, mesmo assim, não se trata de algo impossível ou mirabolante como o nome do termo pode sugerir: trata-se na verdade de uma estrutura poderosíssima que permite ao paradigma funcional continuar aplicando seus conceitos básicos em situações como no caso de imprevisibilidade de valores de entrada. De quebra, os monads ainda constituem um tipo de estrutura que não precisa necessariamente ficar restrita ao universo funcional, podendo ser aplicada até mesmo na programação orientada a objetos para encapsulamento de dados e simplificação de código.

Conceitos de linguagens funcionais: o que é currying?

O paradigma funcional e suas vertentes vêm ganhando cada vez mais destaque no mercado de maneira geral. Os benefícios das linguagens funcionais que vimos neste artigo justificam essa adoção mais intensa de soluções e linguagens que suportem este paradigma por parte do mercado.
Porém, junto com esta popularização, começam a surgir uma variedade de termos que antes eram incomuns e soam como algo extremamente complexo, como currying, pattern matching e monads, por exemplo. E, por muitas vezes, estes termos acabam por assustar quem está tendo os primeiros contatos com o mundo do paradigma funcional.
Nesta série de artigos, vamos começar a ilustrar alguns destes termos esquisitos. Iremos iniciar falando um pouco sobre currying.

JavaScript Básico
Curso de JavaScript Básico
CONHEÇA O CURSO

Antes de tudo: quem foi Haskell Curry?

Toda essa história começa com um matemático norte-americano: Haskell Curry. Haskell inicialmente iniciou seus estudos superiores em medicina na Universidade de Harvard em 1916, mas decidiu estudar matemática no meio do caminho. Ainda trabalhou no campo de engenharia elétrica no MIT e logo após retornou à Harvard para obter uma pós-graduação em Física. Logo após, ainda obteve o título de Ph.D. na área de lógica combinatória, também pela Universidade de Harvard. Curry ainda trabalhou no projeto ENIAC (o primeiro computador digital do mundo) após a Segunda Guerra Mundial.

Haskell Curry (Gleb.svechnikov [CC BY-SA 4.0 (https://creativecommons.org/licenses/by-sa/4.0)])

A foto acima pertence a Gleb.svechnikov (CC BY-SA 4.0 (https://creativecommons.org/licenses/by-sa/4.0))

Curry fez um trabalho fantástico na área de lógica combinatória, área esta que era sua grande paixão. Seu trabalho matemático envolvendo lógica e análise combinatória inspirou e norteou uma série de princípios na área da computação, principalmente no que diz respeito justamente ao paradigma funcional e no cálculo-lambda (uma das bases do paradigma funcional). Além disso, Curry fundamentou um conceito importantíssimo para o paradigma funcional, conceito este que, em sua homenagem, recebeu seu sobrenome: currying. Ainda temos uma outra homenagem a Curry: a famosa linguagem funcional Haskell, que recebeu esse nome em homenagem ao matemático precursor dos conceitos de programação funcional.

O que é o currying em linguagens funcionais?

A definição clássica de currying diz o seguinte:

Dada uma função f do tipo f: (X x Y) → Z, a técnica de currying pode transformar a expressão em curry(f): → (Y → Z)

Calma! Não se desespere com a notação matemática. Ela é mais simples do que parece. 🙂
Vamos relembrar um conceito importante sobre funções matemáticas: elas não podem receber mais do que um parâmetro. Por definição, funções matemáticas só recebem um único parâmetro por vez.
Se levarmos esta situação para o ambiente de programação, criar métodos que recebem um único parâmetro por vez pode ser um complicador. Mas, se quisermos seguir o paradigma funcional, precisamos tentar seguir estas regras… Como podemos criar funções que recebam um único parâmetro por vez? Como poderíamos, por exemplo, criar uma função para multiplicar dois números recebendo um único parâmetro?
Vamos adotar o JavaScript para ilustrar esta situação. Se fôssemos escrever essa função de maneira “normal”, poderíamos ter o seguinte código:

function multiplicar(n1, n2) {
    return n1 * n2;
}
// ...
console.log(multiplicar(2, 2)); // A saída deverá ser "4"

Nossa função está recebendo dois parâmetros, e precisamos que ela receba um único parâmetro. Nós podemos obter esse resultado se montarmos uma composição de funções: podemos ter a primeira função recebendo n1, sendo que esta função retorna uma outra função que recebe n2! Nosso código poderia ficar da seguinte maneira:

function multiplicar(n1) {
    return function(n2) {
        return n1 * n2;
    }
}
// ...
const funcaoCurrying = multiplicar(2);
console.log(funcaoCurrying(2)); // A saída deverá ser "4"
// OU...
console.log(multiplicar(2)(2)); // A saída deverá ser "4"

Veja que temos funções que recebem um único parâmetro por vez, e estas funções podem retornar outras funções, formando uma cadeia de composição de funções. É exatamente o que a notação curry(f): → (Y → Z) na notação matemática que vimos no começo deste bloco diz: nós podemos ter uma função chamada curry gerada pelo encadeamento das funções Y e Z. No final, todas essas funções são funções que chamamos de “unárias”, pois elas lidam somente com um único parâmetro por vez.
O código acima ficaria muito mais elegante se utilizássemos arrow functions (ou funções-lambda):

const multiplicar = (n1) => (n2) => n1 * n2;
// ...
console.log(multiplicar(2)(2)); // A saída deverá ser "4"

Com o trecho acima, temos um código simples, elegante, funcional e utilizando a técnica de currying! 😉

Python - Fundamentos
Curso de Python - Fundamentos
CONHEÇA O CURSO

Quais as vantagens obtidas através da utilização do currying?

Para mim, particularmente, nós temos uma série de vantagens interessantes com o currying:

  • Reaproveitamento de código. Utilizando currying, podemos criar uma série de funções genéricas que, quando combinadas, podem chegar à funcionalidade que desejamos, aplicando-se o conceito clássico de composição com maestria;
  • Criação de métodos que podem receber múltiplos parâmetros de maneira “emulada” em linguagens puramente funcionais: algumas linguagens funcionais deixam o conceito de currying implícito: basta você criar uma função que receba mais de um parâmetro que, internamente, ela será decomposta para múltiplas funções aplicando-se a técnica de currying. Porém, outras linguagens, como o Haskell, não aceitam essa abordagem de nenhuma maneira. Estas linguagens são conhecidas como linguagens puramente funcionais ou linguagens funcionais puras. Nestes casos, se quisermos criar métodos que recebam mais do que um parâmetro (o que é bem comum), nós precisaremos recorrer ao currying inevitavelmente;
  • A utilização do currying produz código elegante e conciso, mesmo em linguagens não funcionais. Currying não é um conceito que é aplicável somente em linguagens funcionais. Você pode aplicar a técnica de currying em linguagens que não são majoritariamente funcionais, como Java e C#, produzindo código legível e de fácil manutenção em praticamente qualquer linguagem;
  • No final, temos funções unárias. Funções unárias – as funções que recebem um único parâmetro – são mais legíveis e mais fáceis de serem manuteídas, pois geralmente são menores e possuem menos pontos de entrada (já que possuem um único parâmetro), o que reduz os possíveis pontos de efeito colateral durante uma alteração no código;
  • Você provavelmente já utiliza a técnica de currying. Se você analisar as APIs de map/reduce/filter do JavaScript, o LINQ do C# ou a stream API do Java, você verá que as APIs já trabalham com o conceito de funções que recebem um único parâmetro, ou mesmo funções que recebem funções como argumentos. Boa parte dos métodos destas APIs já utiliza conceitos de linguagens funcionais ,como o currying. Estas APIs só conseguem ser tão genéricas justamente por causa desse conceito de composição de funções que é natural ao conceito de currying.

Ainda temos muitos outros termos para vermos!

A programação funcional é de fato um mundo cheio de termos e jargões que podem soar esquisitos no início, mas que geralmente são mais simples do que parecem (como o conceito de currying). Ainda temos vários outros termos para serem explorados, como monads e partial function applications. Nos próximos posts, iremos continuar a desbravar estes termos esquisitos e que colocam medo por várias vezes em nós; mas que, no final, são mais simples do que parecem.
Até o próximo post! 😉

O que são as linguagens funcionais?

Ultimamente, as linguagens ditas funcionais estão ganhando cada vez mais a atenção dos desenvolvedores em geral. Já não é tão difícil encontrarmos projetos que utilizem uma linguagem funcional para resolver problemas que envolvem principalmente paralelismo e/ou processamento de quantidades grandes de dados. As ideias fundamentadas pelo paradigma funcional dão essa “vocação” para o paralelismo e processamento de quantidades extensas de dados para estas linguagens ditas funcionais.
Mas, no final das contas, o que torna as linguagens funcionais tão adequadas para a resolução destes tipos de problemas? E quais são os contrapontos entre o paradigma funcional e o paradigma orientado a objetos? Será que o paradigma funcional pode substituir completamente o paradigma orientado a objetos?

O que é o paradigma funcional?

Antes de qualquer coisa: não, o paradigma funcional não tem esse nome porque ele funciona.

O paradigma funcional possui esse nome porque ele está fortemente fundamentado nos conceitos das funções matemáticas.
Mas calma! Muita gente já “torce o nariz” ao ouvir falar de matemática, mas os conceitos envolvidos na descrição do paradigma funcional podem ser mais tranquilos de se entender do que você imagina.

Relembrando alguns conceitos de funções matemáticas

Vamos recapitular um pouco sobre uma função matemática. Uma função matemática tem essa aparência:

Se verificarmos, uma função matemática possui uma única entrada. Em uma função matemática, essa única entrada sofre um tipo de processamento, processamento este que retorna uma saída de qualquer maneira. No caso da função acima:

  • A entrada é definida por x;
  • O processamento é a elevação de x ao quadrado mais 3;
  • A saída é o resultado do processamento descrito acima.

Mais um exemplo: se entrarmos com o número 2 nessa função matemática, teremos a saída 7, pois 2^2 (4) + 3 é igual a 7.

Existem alguns detalhes importantes. A função acima, além de sempre retornar um resultado compulsoriamente, ela sempre retornará a mesma saída para uma determinada entrada. Por exemplo: podemos “chamar” esta função passando para ela a entrada 2 hoje, amanhã ou daqui 10 anos… Não importa: o resultado dela sempre será 7, independente da situação. Nós chamamos esta característica de determinismo. Por isso, dizemos que funções matemáticas em sua essência são determinísticas.
Outro fato interessante: funções matemáticas não possuem o conceito de escopo. Isso quer dizer que a entrada é processada e a saída correspondente é devolvida imediatamente. Não existem “variáveis” intermediárias que só podem ser acessadas dentro da função matemática: só existe a entrada e a saída.

Java - Fundamentos
Curso de Java - Fundamentos
CONHEÇA O CURSO

Trazendo conceitos de funções matemáticas para as linguagens de programação

O paradigma funcional se baseia fortemente nos conceitos de funções matemáticas que discutimos no tópico anterior. E, sendo um paradigma, não necessariamente precisa ser implementado com uma linguagem funcional…
Por exemplo, poderíamos reescrever a função matemática anterior com o seguinte código Java:

public int doFunction(int x){
    return (x * x) + 3;
}

System.out.println(doFunction(2)); // A saída será "7"!

Veja que, na função acima:

  • Temos uma única entrada;
  • Não existe o conceito de escopo, pelo menos não explicitamente (já que não existem variáveis criadas dentro do método doFunction());
  • O método sempre irá produzir uma saída;
  • O método é determinístico, pois ele irá sempre produzir a mesma saída para uma mesma entrada.

Sendo assim, conseguimos criar um código funcional, mesmo utilizando o Java, que não é uma linguagem essencialmente funcional.
Acima, ainda temos uma outra característica intrínseca ao paradigma funcional que é fantástica: a imutabilidade! Veja que, em nossa versão Java da função matemática, a entrada nunca é modificada: ela simplesmente sofre um processamento, produzindo uma saída. A saída produzida também nunca é modificada… Isso é fantástico, pois ao final, estamos quase que trabalhando com constantes o tempo inteiro.
Obviamente, o Java e outras linguagens majoritariamente orientadas a objeto não dão um suporte 100% para estes conceitos que descrevemos… Porém, se pegarmos linguagens majoritariamente funcionais, nós teremos estes princípios básicos levados mais à risca. Por exemplo: vamos considerar a reescrita desta função em F#, uma linguagen funcional:

let doFunction x = (x * x) + 3

let result = doFunction 2
printfn "Resultado: %i" result

Acima, temos a mesma função reescrita em uma linguagen funcional. Veja que os conceitos de ausência de escopo, massa de entrada e saída e determinismo são respeitados aqui. Aliás, se você tentar modificar a “variável” result (que aqui é chamada de binding), o compilador irá produzir um erro.

C# (C Sharp) Básico
Curso de C# (C Sharp) Básico
CONHEÇA O CURSO

Por que linguagens funcionais podem ser interessantes para paralelismo, assincronia e processamento de grandes quantidades de informações?

Nós estamos, em maioria, acostumados a escrever código orientado a objetos, e uma das premissas da orientação a objetos é a transição de estado: nós criamos variáveis e/ou objetos que mudam de conteúdo o tempo inteiro, fazendo com que o nosso código tome decisões em cima destas flutuações de estado que acontecem. Isso não existe dentro do paradigma funcional, tendo em vista o determinismo e a imutabilidade.
Quando estamos falando sobre o processamento de quantidades muito grandes de dados, tornar esse processamento assíncrono e paralelo é essencial. E isso pode se tornar um problema muito complicado quando falamos de transição de estado neste cenário. Um exemplo simples: quando criamos threads, uma thread não pode acessar o conteúdo que está sendo processado por outra thread. Quando precisamos que threads por alguma razão compartilhem alguma varíavel, nós acabamos precisando recorrer a patterns (como o famigerado singleton ). E no final, nós ainda precisamos nos preocupar em garantir que a modificação destes recursos compartilhados não irá ocorrer de maneira concorrente, com uma thread sobrescrevendo ou passando por cima do resultado de uma outra thread ao mesmo tempo. Para isso, precisamos recorrer a recursos como semáforos e monitores. Veja que, somente por termos um código baseado em transições de estados, nós precisamos recorrer a uma certa complexidade de código para garantir que tudo ocorra bem. Logicamente isso é factível, mas é extremamente trabalhoso.
Linguagens funcionais não possuem estes problemas, pois elas já trabalham com imutabilidade e determinismo nativamente. Imutabilidade e determinismo são pilares do paradigma funcional. Se não existem transições de estado, nós não teríamos este tipo de problema se utilizássemos uma linguagem funcional.

F# (F Sharp) - Fundamentos
Curso de F# (F Sharp) - Fundamentos
CONHEÇA O CURSO

Então devo chutar a orientação a objetos e aprender somente o paradigma funcional?

Não! Não faça isso!
O paradigma funcional também possui seus problemas… A curva de aprendizado tende a ser ligeiramente maior do que a curva do paradigma orientado a objetos, principalmente porque muitas pessoas já criam um “bloqueio” pela associação à matemática. Existem também alguns conceitos dentro do paradigma funcional que não são tão “naturais” de serem entendidos, como currying e monads. Sendo assim, a curva de aprendizado tende a ser um pouquinho mais íngreme.
Também temos o fato de que pouca coisa no mundo real é determinística: estamos falando das típicas situações de I/O. O acesso a uma tabela de um banco de dados, por exemplo, não pode ser determinístico: nós podemos tanto conseguir acessar a tabela como podemos em algum momento ter uma queda de conexão… Logicamente, as linguagens funcionais modernas são preparadas para também lidar com situações não-determinísticas, mas cada uma delas pode tratar de maneira diferente, o que contribui para aumentar um pouco mais o processo de aprendizagem e aplicação correta em situações do dia-a-dia.
Existe também a questão de legibilidade do código. Eu particularmente observo que é mais fácil produzir código ilegível em linguagens funcionais do que em linguagens orientadas a objeto. Tudo bem, um desenvolvedor iniciante (ou um desenvolvedor infelizmente ruim) irá produzir código ruim de qualquer maneira… Mas observo que as linguagens funcionais dão uma “forcinha” a mais nesse sentido por diferentes fatores: sintaxe propriamente dita das linguagens que implementam este paradigma, a maior ocorrência da não-observância dos pilares funcionais, entre outros.
Por isso, pelo menos para mim, o ideal é: aprenda e domine os dois paradigmas. Ambos serão muito úteis, dependendo-se da situação. Precisa criar uma página de cadastro de algo? Provavelmente a orientação a objetos pode ser mais aplicável… Precisa gerar um CSV a partir de um conjunto grande de dados? Provavelmente uma linguagem ou até mesmo uma abordagem funcional será mais aplicável. Aqui vale o velho mantra: não existe bala de prata no mundo de desenvolvimento de software. Aprenda os dois paradigmas e seja feliz!

Imutabilidade de objetos no PHP

Imutabilidade é uma característica forte nas linguagens funcionais, onde a alteração de estado não é frequente e, quando ela acontece, é controlada. Há de se observar que, o PHP, uma linguagem multi-paradigma e fracamente tipada, não implementa (ainda) nenhum mecanismo padrão para lidar com imutabilidade.

Linguagens de programação são naturalmente opinativas e é difícil fazer com que elas se comportem de uma determinada forma se não foram cultura e tecnicamente desenvolvidas para servir aquele propósito específico. Por exemplo, não adianta eu querer acessar diretamente um endereço da memória usando o PHP, a linguagem não nasceu com essa premissa, não é uma linguagem para se desenvolver sistemas embarcados, por exemplo. Consegue perceber? As linguagens possuem “culturas” e características e, quando escolhemos uma delas, temos que nos “encaixar” nesses aspectos.

Disso, infere-se que, por mais que tentemos, programar em PHP nunca será 100% thread safe, será sempre mutável. No entanto, podemos usar características da linguagem a fim de emular diferentes comportamentos e características, dentre elas, a imutabilidade (mesmo que parcial, se estritamente avaliado). Há de se destacar que, sim, é possível programar usando o paradigma funcional em PHP desde a versão 5.3 com a introdução de Lambda’s e Closure’s.

PHP Básico
Curso de PHP Básico
CONHEÇA O CURSO

Imutabilidade

Um objeto imutável é, por definição, aquele que não pode ter o seu estado alterado depois da sua criação.

Abrindo um parênteses aqui, uma constante é a única estrutura realmente imutável no PHP. Tanto as constantes do escopo global quanto as constantes de classe garantem que o valor declarado permanecerá estático durante todo o ciclo de vida de execução do script.

class FooClass
{
    const PI = 3.14159265359;

    public function __construct()
    {
        static::PI = 3.14; // FATAL ERROR syntax error, unexpected '=' on line number 7
    }
}

$foo = new FooClass(); // Error

Mas, constantes só trabalham com valores escalares (int, string, float, boolean), nulos e arrays (um tipo composto).

Objetos mutáveis

A alternância de estados é uma das características mais fortes do paradigma imperativo, logo, essa também é uma característica evidente no paradigma orientado a objetos (que é imperativo). Há muitos benefícios, claro. Os princípios SOLID são conceitualmente incríveis, aumentam a legibilidade e a manutenibilidade dos softwares. Mas, às vezes, lidar com centenas de objetos pode nos levar à perigosas armadilhas.

Um problema que a mutabilidade pode nos trazer é o efeito colateral. Um código mutável fica a mercê de alterações não previstas do seu estado, então, num determinado ciclo da execução ele pode ter o estado A e em outro ciclo o estado B, ele fica suscetível à violações.

Vamos a um exemplo didático? Considere uma classe para trabalhar com simples cálculos monetários:

class Money
{
    /**
     * @var mixed
     */
    private $amount;

    /**
     * Money constructor.
     * @param $amount
     */
    public function __construct($amount)
    {
        $this->amount = $amount;
    }

    /**
     * @param $amount
     * @return $this
     */
    public function plus($amount)
    {
        $this->amount += $amount;

        return $this;
    }

    /**
     * @param $amount
     * @return $this
     */
    public function sub($amount)
    {
        $this->amount -= $amount;

        return $this;
    }

    /**
     * @return string
     */
    public function amount()
    {
        return $this->amount;
    }
}

Suponhamos, então, que temos uma classe Payment que utiliza a Money:

class Payment
{
    /**
     * @param $valor
     * @return array
     */
    public function process($valor)
    {
        $valorBruto = new Money($valor);

        // TODO

        $valorLiquido = $valorBruto->sub(20);

        // TODO

        return [
            'valor_bruto' => $valorBruto->amount(),
            'valor_liquido' => $valorLiquido->amount(),
        ];
    }
}

Integrando e utilizando o exemplo:

$payment = new Payment();
$result = $payment->process(100);

Avaliando a classe Payment subtende-se como senso comum que teremos armazenado na variável $result o seguinte array:

[
    'valor_bruto' => 100,
    'valor_liquido' => 80,
]

Uma vez que a entrada foi 100 e o valor líquido é o valor bruto menos 20. Certo?

Mas não é o que acontece. O resultado é:

[
    'valor_bruto' => 80,
    'valor_liquido' => 80,
]

Se você programa em PHP regularmente sabe que, quando atribuímos um objeto a uma variável, uma referência desse objeto é nos retornada. Isso quer dizer, no nosso exemplo, tanto a variável $valorBruto quanto a $valorLiquido trabalham com exatamente o mesmo endereço de objeto na memória, por isso tivemos esse efeito colateral do valor bruto ser 80 e não 100, como esperado.

Não teríamos tido tal problema se no exemplo tivéssemos explicitamente clonado o objeto $valorBruto usando o operador clone, assim:

$valorLiquido = clone $valorBruto;
$valorLiquido->sub(20);

Uma clonagem de um objeto significa a cópia de toda a estrutura interna dele, mas em outro endereço de memória. É um objeto igual em atributos / características, mas diferente.

Se alterarmos o objeto $valorBruto isso não será refletido no $valorLiquido e vice versa. São objetos distintos, moram em outro “endereço”, mesmo que iguais (no sentido de ser, ambos são do tipo Money).

Mas, que fique claro, isso não tira o fato de que esses objetos continuarão sendo suscetíveis à consecutivas mudanças de estado no ciclo de execução. É agora que entra a parte que “toca” o objetivo desse artigo.

PHP Intermediário
Curso de PHP Intermediário
CONHEÇA O CURSO

Objetos imutáveis

Não tem como definirmos um objeto essencialmente 100% imutável no PHP, ele pode ser violado por reflexão, métodos mágicos, bindings de funções, truques com referências etc. Por exemplo, um objeto pode ser alterado pelos métodos mágicos __set(), __unset(), no uso das funções serialize() e unserialize().

No entanto, podemos chegar bem próximos disso, seguindo algumas regras:

  • Declare a classe como sendo final (a impede de ser estendida);
  • Declare as propriedades como sendo privadas;
  • Evite métodos setters, no lugar, utilize o construtor da classe para receber o valor.
  • Quando for preciso modificar o valor do objeto, retorne uma cópia (um clone) dele, nunca ele próprio;
  • Evite que esse objeto receba outro objeto e, caso seja preciso, ele também precisa ser imutável;

Vamos transformar a nossa classe Money em uma “classe imutável” usando os recursos que temos disponíveis no PHP?

final class Money
{
    /**
     * @var mixed
     */
    private $amount;

    /**
     * Money constructor.
     * @param $amount
     */
    public function __construct($amount)
    {
        if( ! is_numeric($amount)) {
            throw new \InvalidArgumentException('The amount must be numeric.');
        }

        $this->amount = $amount;
    }

    /**
     * @param $amount
     * @return Money
     */
    public function plus($amount)
    {
        return new self($this->amount + $amount);
    }

    /**
     * @param $amount
     * @return $this
     */
    public function sub($amount)
    {
        return new self($this->amount - $amount);
    }

    /**
     * @return string
     */
    public function amount()
    {
        return $this->amount;
    }
}

O que fizemos:

  • A classe agora é final;
  • Garantimos no construtor receber só o tipo de valor que queremos;
  • Nos métodos plus() e sub(), ao invés de alterarmos o objeto atual, retornamos sempre um novo objeto com o valor da operação em questão. Essa é a parte mais importante.
  • O estado de $amount agora estará protegido, ademais, é um atributo privado.

Observe os métodos plus() e sub():

/**
 * @param $amount
 * @return Money
 */
public function plus($amount)
{
    return new self($this->amount + $amount);
}

/**
 * @param $amount
 * @return $this
 */
public function sub($amount)
{
    return new self($this->amount - $amount);
}

Eles sempre vão retornar uma nova instância de Money e isso faz com os nossos objetos não tenham seus estados alterados durante o ciclo de execução.

Observe que, se queremos um objeto imutável, as alterações realizadas nele precisam criar uma nova estrutura que compartilhe características da original. Em termos gerais, tudo o que for invocado no objeto não pode alterar o estado dele, ao contrário disso, deve-se retornar o resultado dessa transformação em uma nova estrutura.

Vamos testar isso na prática?

$valor1 = new Money(20);
$valor1->sub(10);

echo $valor1->amount();

Qual será o valor da impressão?

Será 20. Estamos subtraindo 10, mas o método sub() nos retorna um novo objeto. Não estamos “tocando” no objeto $valor1. Ele permaneceu intacto.

Isso pode ser constatado se compararmos os dois objetos, veremos que são diferentes:

$valor1 = new Money(20);
$valor2 = $valor1->sub(10);

if($valor1 === $valor2) {
    echo 'São iguais';
} else {
    echo 'São diferentes.';
}

O resultado será:

São diferentes.

Alguns dos benefícios da imutabilidade:

  • A aplicação se torna um pouco mais previsível, já que o estado dos objetos não alteram durante a execução;
  • Fica mais fácil identificar onde determinado problema aconteceu, já que não há variáveis compartilhando a referência para o mesmo objeto;

Com essa alteração, você pode voltar a testar a primeira versão da classe Payment:

class Payment
{
    /**
     * @param $valor
     * @return array
     */
    public function process($valor)
    {
        $valorBruto = new Money($valor);

        // TODO

        $valorLiquido = $valorBruto->sub(20);

        // TODO

        return [
            'valor_bruto' => $valorBruto->amount(),
            'valor_liquido' => $valorLiquido->amount(),
        ];
    }
}

O resultado será:

[
    'valor_bruto' => 100,
    'valor_liquido' => 80,
]

Sem surpresas.

RFC: Immutable classes and properties

Há um RFC aberto para o PHP que discute a inclusão de classes e propriedades imutáveis no PHP. Se aprovado para alguma futura versão, teremos uma sintaxe assim:

immutable class Email
{
  public $email;

  public function __construct ($email)
  {
    // validation

    $this->email = $email;
  }
}

$email = new Email("foo@php.net");
$emailRef = &$email->email;
$emailRef = "bar@php.net" // Call will result in Fatal Error

Observe que, mesmo esse Value Object tendo o atributo $email púbico não é possível, nem fazendo um truque com referência, alterá-lo. Isso torna menos verboso a construção objetos imutáveis e sem a necessidade de terem seus membros protegidos, além de garantir uma maior segurança (tira do desenvolvedor ter que cuidar de tais detalhes de implementação).

Aproveitando a deixa, temos na standard library do PHP uma classe chamada DateTimeImmutable para trabalhar com data e hora. Um objeto dessa classe nunca têm o estado modificado, ao contrário disso, uma nova instância é sempre retornada (como fizemos com a classe Money).

Concluindo

Há situações onde o uso de objetos imutáveis são essenciais para se garantir integridade ou até mesmo um comportamento previsível. Em DDD o uso de Value Objects (que são imutáveis por essência) é bastante encorajado. Naturalmente você se deparará com esse conceito quando lidar com programação reativa com concorrências ou com qualquer outra característica da programação funcional.

Um abraço!

PHP Avançado
Curso de PHP Avançado
CONHEÇA O CURSO

Precisamos falar sobre o TypeScript

Ultimamente muita gente tem torcido o nariz para um novo superset do JavaScript. Sim, estamos falando do TypeScript.

O TypeScript é um caso interessante. Percebo ultimamente que, ou você o adora (o que é meu caso) ou você simplesmente o odeia. Parece não haver um consenso intermediário de maneira geral na comunidade. Por que será que isso acontece? Será que existem conceitos errôneos difundidos na comunidade sobre o TypeScript? Será que ainda há pontos cruciais a serem melhorados nele? Vamos discorrer sobre isso! 🙂

TypeScript - Primeiros Passos
Curso de TypeScript - Primeiros Passos
CONHEÇA O CURSO

É da Microsoft…

Acho que esse ponto talvez seja o mais complicado de se discutir. Muita gente simplesmente tem ódio do TypeScript simplesmente por ele ser da Microsoft. Em minha opinião, acho que é simplesmente perda de tempo deixar de utilizar alguma tecnologia por ela ser de alguma empresa específica. Acho que a qualidade técnica e a maneira como a companhia conduz a plataforma, além dos fatores externos (infraestrutura, contratos comerciais, etc.), deveriam ser os fatores cruciais para se decidir se determinado framework/linguagem/plataforma será adotada ou não. Porém, sabemos que por muitas vezes a paixão toma lugar da razão nesse tipo de tomada de decisão (eu comentei um pouco sobre esse tipo de cenário no artigo Não seja um profissional “intolerante”.

Eu concordo que a conduta que a Microsoft tinha há algum tempo não era a mais adequada com relação aos desenvolvedores. Mas também precisamos reconhecer que essa postura nos últimos anos mudou da água para o vinho! Em pouco mais de cinco anos, a Microsoft se tornou a maior contribuidora em software open source no GitHub. Duvida? Você pode comprovar através do próprio GitHub.

A Microsoft também se tornou uma das principais contribuidoras do kernel Linux. Ela ainda tem disponibilizado uma série de ferramentas que antes eram licenciadas (caso do Visual Studio e do Xamarin, por exemplo) de maneira completamente gratuita. As principais plataformas da Microsoft (o que inclui toda a plataforma do .NET Core e o próprio TypeScript) estão abertos no GitHub. Não há mais motivos para achar que a Microsoft “tem um plano maquiavélico/diabólico/capitalista de dominar a indústria de software” (embora esse acabe sendo um objetivo indireto de qualquer empresa no fim das contas).

A Microsoft mostra atualmente que já entendeu que ela precisa se tornar parte do mundo open source, além de já ter provado que sim, ela tem muita coisa interessante para a comunidade. Para mim, isso tudo reforça que essa mania de falar que o que é da Microsoft é ruim deveria já ter ficado no passado.

O TypeScript não quer substituir o JavaScript

Algumas pessoas que eu conheço têm pavor do TypeScript porque acham que ele vem para substituir o JavaScript, o que é um equívoco. O TypeScript nunca teve a intenção de substituir completamente o JavaScript. A própria documentação diz que ele é na verdade um superset do JavaScript. E o que isso quer dizer? Isso quer dizer que ele visa fornecer alguns recursos que o JavaScript não oferece por padrão. Entre esses recursos, temos por exemplo:

  • A oportunidade de termos tipos de dados mais definidos (tornando o JavaScript um pouco mais tipado);
  • Uma sintaxe e estrutura fortemente orientados à objetos (o TypeScript é fortemente influenciado pelo C# e F#), aproximando a experiência de desenvolvimento de linguagens tradicionalmente orientada a objetos como o C# e o Java;

Essas vantagens, por decorrência, trazem outras vantagens:

Por causa da existência de tipagem e também de uma sintaxe mais clara, as IDEs ganham a possibilidade de aprimorarem seus mecanismos de detecção de erros e de sintaxe em tempo de desenvolvimento. Isso quer dizer que, usando TypeScript, sua IDE poderá lhe avisar de alguma passagem de tipo incorreta ou mesmo sobre alguma sintaxe incorreta enquanto você ainda escreve o código. Ferramentas como o IntelliSense (conhecido por muitos como AutoComplete) também podem ser bem mais efetivas nesse cenário. Não quero dizer que, quando utilizamos JavaScript “convencional”, as IDEs não ofereçam esse tipo de suporte, mas o suporte das IDEs para o TypeScript é ou pelo menos tem a grande tendência de ser bem mais aprimorado;

Ainda temos, como decorrência do ponto anterior, a possibilidade de que o código escrito com TypeScript seja bem mais robusto, seguro e fácil para ser testado. Isso tudo, mais uma vez, graças aos conceitos de linguagens estáticas e tipadas que o TypeScript trouxe para o mundo das linguagens funcionais, na minha opinião, no nível certo. TypeScript não é JavaScript simplesmente tipado e estático, ele torna possível utilizar o melhor do mundo de linguagens de tipagem fraca e funcionais e do mundo de linguagens estáticas e de tipagem forte;

Aqui já é uma opinião bem mais pessoal, mas eu prefiro a sintaxe do TypeScript que a sintaxe do JavaScript, principalmente no que tange à orientação a objetos. Eu gosto das “regras de sintaxe” e a pseudo-tipagem que o TypeScript traz para o JavaScript. Ou você realmente acha que isso:

var __extends = (this && this.__extends) || function (d, b) {
    for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
    function __() { this.constructor = d; }
    d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
var Animal = (function () {
    function Animal(name) {
        this.name = name;
    }
    Animal.prototype.move = function (distanceInMeters) {
        if (distanceInMeters === void 0) { distanceInMeters = 0; }
        console.log(this.name + " moved " + distanceInMeters + "m.");
    };
    return Animal;
}());
var Snake = (function (_super) {
    __extends(Snake, _super);
    function Snake(name) {
        return _super.call(this, name) || this;
    }
    Snake.prototype.move = function (distanceInMeters) {
        if (distanceInMeters === void 0) { distanceInMeters = 5; }
        console.log("Slithering...");
        _super.prototype.move.call(this, distanceInMeters);
    };
    return Snake;
}(Animal));

… é mais claro que isso?

class Animal {
    constructor(public name: string) { }
    move(distanceInMeters: number = 0) {
        console.log(`${this.name} moved ${distanceInMeters}m.`);
    }
}

class Snake extends Animal {
    constructor(name: string) { super(name); }
    move(distanceInMeters = 5) {
        console.log("Slithering...");
        super.move(distanceInMeters);
    }
}

Agora, nada impede de você utilizar JavaScript “tradicional” em conjunto com o TypeScript, mesmo porque no final o TypeScript é transpilado para JavaScript! No final, tudo vira JavaScript… Veja que no exemplo acima mesmo isso ocorre quando utilizamos o tradicional console no meio do código TypeScript. Isso reforça que a idéia da Microsoft não é substituir o JavaScript, nem nunca vai ser! A idéia é podermos ter a possibilidade de utilizar um ferramental mais poderoso para produzir um código mais robusto e legível.

Agora terei um código muito verboso

Não há como negar isso. As características técnicas (tipagem, vocação para orientação à objetos, etc.) fazem naturalmente que o código TypeScript torne-se um pouco mais verboso e com menos vocação para o lado funcional, principalmente para quem vem de linguagens fracamente tipadas e/ou funcionais. Na verdade, no final, o TypeScript faz uma mistura entre os conceitos dos dois paradigmas (funcional e orientado a objeto) e dos dois sistemas de tipagem (fracamente tipado e fortemente tipado).

E aí, já viu: isso pode provocar tanto admiração como também raiva. Algumas pessoas podem gostar bastante dessa mistura (meu caso) ou outras podem simplesmente odiar (já que o TypeScript corre o sério risco de desagradar tanto os adeptos do paradigma funcional como também do paradigma orientado à objetos). Temos verbosidade no TypeScript sim, mas ainda sim temos aspectos funcionais também presentes.

O TypeScript ainda não consegue lidar bem com todas as bibliotecas JavaScript

Para que o TypeScript consiga “conversar” com as APIs de outras bibliotecas (por exemplo, o jQuery), ele precisa de um arquivo com a extensão **.d.ts*. Este arquivo, conhecido como TypeScript Type Definition, traz os mapeamentos necessários para que através do TypeScript você possa acessar e utilizar os métodos expostos por uma biblioteca externa, mesmo que esta não tenha sido escrita com o TypeScript. Esse arquivo acaba funcionando como uma ponte entre uma biblioteca externa e o TypeScript.

Aí reside o problema: nem todas as bibliotecas possuem esse arquivo que as prepara para serem consumidas por código TypeScript. As mais famosas bibliotecas já possuem este arquivo preparado, mas se você utiliza uma biblioteca um pouco menos conhecida ou possui uma biblioteca proprietária, ela pode não ser de cara compatível com um código TypeScript de maneira completa. Nesse caso, não há outro cenário: será necessário que você mesmo escreva este arquivo de definições ou abra mão do TypeScript. E isso, de fato, pode vir a ser um empecilho dependendo do projeto…

O TypeScript não é o salvador da pátria

alt

O TypeScript pode trazer algumas confusões com relação a sua utilização… Já que o código TypeScript no final é sempre transpilado para um código JavaScript “normal”, podemos dizer que todo código JavaScript também é código TypeScript! Isso é legal porque justamente conseguimos ainda ter as características legais do JavaScript com o poder do TypeScript.

Por isso mesmo que o TypeScript não visa substituir o JavaScript, e sim o complementar. Algumas pessoas têm comentado ultimamente, chegando ao (absurdo, na minha opinião) de dizer que hoje TypeScript é a linguagem de alto nível enquanto o JavaScript é o assembly.

É cedo demais para dizer isso; assim como, em minha opinião, nem sempre o TypeScript pode ser a melhor saída. Para se escolher se você deve usar TypeScript ou não, não se esqueça de levar em consideração a quebra de paradigmas que ele pode causar na equipe de desenvolvimento, bem como o suporte às bibliotecas que você pretende utilizar. E esses são somente dois pontos que devemos considerar… TypeScript não é uma bala de prata..

Afinal, devo utilizar TypeScript ou não?

Mais uma vez, essa é uma questão muito subjetiva, onde não temos o certo e o errado. Tudo vai depender da situação como um todo. Eu gosto da idéia do TypeScript e o utilizo sem problema nenhum: o sistema de tipagem, a sintaxe mais clara e a aliança com o JavaScript (afinal, ainda temos JavaScript como produto final) me agradam bastante. Mas você pode ter uma opinião diferente… No final, nenhum de nós vai estar 100% certo ou 100% errado, hehe.

Agora, eu não acho perda de tempo estudar TypeScript. Para mim, está claro que ele é algo que veio para ficar. Além de tudo, ele ainda está muito alinhado com o ECMA 6, tendo muitos recursos que o ECMA 6 propõe. E tem mais um tempo: alguns frameworks, como Angular e Ionic, estão adotando o TypeScript, assim como outros estão pelo menos oferecendo suporte a ele. Por essas e outras, o TypeScript veio para ficar.

Melhor para nós! Mais uma opção de ferramenta legal que podemos utilizar em nossos projetos. 🙂 Que a guerra dos frameworks/bibliotecas/supersets do JavaScript continue! o/

Você quer aprender TypeScript? Veja esse nosso incrível curso:

TypeScript - Primeiros Passos
Curso de TypeScript - Primeiros Passos
CONHEÇA O CURSO