DEV Community

Cover image for GoLang — Simplificando a complexidade “O Inicio”
jefferson otoni lima
jefferson otoni lima

Posted on

GoLang — Simplificando a complexidade “O Inicio”

Existem inúmeras linguagens de programação e cada uma nasceu com um propósito: “resolver problemas”. As linguagens são ferramentas onde teremos que saber utilizá-las no momento certo. Falando “como desenvolvedor” quanto mais poliglota conseguir ser melhor será para sua carreira profissional e para um melhor entendimento e compreensão da diversidade deste ecossistema.

O objetivo deste post é apresentar o que é a linguagem Go e, porque ela é tão poderosa. Apresentando alguns conceitos e pontos importantes sobre a linguagem Go que muita das vezes é expressado de forma equivocada em diversos artigos encontrados na internet e grupos de discussões de linguagens de programação. Acredito que os pontos fracos e problemas gerados por diversas outras linguagens de programação ao longo destas décadas fez com que Go torna-se o que é hoje. E para explicarmos como isto é possível iremos voltar ao início de tudo, onde tudo começou.

package main

func main() {
    println("Hello all folks!")
}
Enter fullscreen mode Exit fullscreen mode

Menos é exponencialmente Mais

A equipe de desenvolvedores do Go dizem que sua criação é uma tentativa de tornar os programadores mais produtivos. Melhorando o processo de desenvolvimento de software no Google. O primeiro objetivo foi criar uma linguagem melhor para enfrentar os desafios da simultaneidade escalável, ou seja software que lida com muitas preocupações simultaneamente, um exemplo seria a coordenação de mil servidores back-end enviando tráfego de rede todo o tempo.

Manter a linguagem Go pequena permite objetivos mais importantes. Ser pequeno torna o Go mais fácil de aprender, mais fácil de entender, mais fácil de implementar, mais fácil de reimplementar, mais fácil de depurar, mais fácil de ajustar e mais fácil de evoluir. Fazer menos permite mais. É uma expressão utilizada pela equipe de desenvolvimento do Go: “Do Less. Enable More” seria o equilíbrio entre os universo de problemas existentes e o que Go poderá ajudar a resolver bem tais problemas. Go explicitamente não foi projetado para resolver todos os problemas em vez disso fizeram o suficiente para possamos criar as próprias soluções personalizadas com facilidade, mas deixando bem claro que Go não pode fazer tudo.

Go foi projetado pelo Google em 2007 para melhorar a produtividade de programação em uma era de multicore, rede de máquinas e grandes bases de código. Os designers queriam abordar críticas de outras línguas em uso no Google, mas manter suas características úteis.

Os criadores Rob Pike, Ken Thompson e Robert Griesemer mantiveram a sintaxe de Go semelhante ao C. No final de 2008 Russ Cox juntou-se a equipe e ajudou a mudar a linguagem e as bibliotecas de protótipo para realidade.

A linguagem Go foi lançada em 2009 com propósito de facilitar a resolução de problemas quando o assunto é desenvolvimento em camadas de rede, escalabilidade, desempenho, produtividade e o mais importante concorrência. O próprio Rob Pike declarou que “Go foi projetado para tratar de um conjunto de problemas de engenharia de software a que estávamos expostos na construção de grandes softwares de servidor”.

Go teve influências de diversas linguagens de programação e paradigmas diferentes dentre elas*:* Alef, APL, BCPL, C, CSP, Limbo, Modula, Newsqueak, Oberon, occam, Pascal, Smalltalk e Cristal, percebe-se que utilizaram do que tinham de melhor e criou algo novo e enxuto, com o mínimo necessário para resolver os problemas propostos, sem perder sua simplicidade. Acredito que isto podemos chamar de inovação. Go inovou ao quebrar os paradigmas de linguagens e implementar algo novo de forma simples e muito poderosa.

Polêmicas

Foram deixadas de fora diversas características que as linguagens modernas contrariando e indo em direção oposta das outras linguagens atuais algumas funcionalidades como o uso de: declarações de imutabilidade, tipos de dados de correspondência de padrões, generics, tratamento de exceção, Herança, programação genérica e sobrecarga de métodos e diversos outros pontos. Todos os recursos acima foram criticados e apontados para os criadores de Go, que responderam: “Essas omissões foram simplificações que contribuem para a força de Go”. Há muitas coisas na linguagem e bibliotecas Go que diferem das práticas modernas, foi uma decisão da equipe de desenvolvimento do Go: *“*às vezes vale a pena tentar uma abordagem diferente”.

Todos estão convidados a ajudar e contribuir se assim desejar, podem abrir propostas para novos recursos e tudo relacionado ao ecossistema da linguagem. O código fonte de Go está disponível no GitHub . A documentação sobre como você pode contribuir com o idioma é fornecida aqui.

Goroutines em vez de threads

Um dos principais objetivos da linguagem de programação Go é tornar a simultaneidade mais simples, rápida e eficiente.

Goroutines são leves e tiram proveito de todo o poder de processamento disponível. Goroutines existe apenas no espaço virtual do tempo de execução Go e não no sistema operacional.

Goroutine é um método/função que pode ser executado independentemente junto com outras goroutines. Cada atividade simultânea na linguagem Go é geralmente denominada Goroutine.

Um dos pontos mais relevantes e importantes é o trabalho com concorrência, ele inovou ao quebrar o modelo tradicional de threads e sua forma de utilização ao criar um novo modelo, as goroutines. As goroutines são responsáveis por realizar execuções em Go de forma assíncrona. São muito poderosas e uma simples máquina de 1G de Ram poderá subir milhares delas.

Goroutines fazem parte de tornar a simultaneidade fácil de usar. As goroutines, pode ser muito barata: eles têm pouca sobrecarga além da memória para a pilha, que é apenas alguns kilobytes. Para tornar as pilhas pequenas, o tempo de execução de Go usa pilhas redimensionáveis ​​e delimitadas. Uma goroutine recém lançada recebe alguns kilobytes, o que é quase sempre suficiente. Quando não é, o tempo de execução aumenta (e encolhe) a memória para armazenar a pilha automaticamente, permitindo que muitos goroutines vivam em uma quantidade modesta de memória. A sobrecarga da CPU tem em média três instruções baratas por chamada de função. É prático criar centenas de milhares de goroutines no mesmo espaço de endereço. Se goroutines fossem apenas threads, os recursos do sistema acabariam em um número muito menor.

O design de Go foi fortemente influenciado pelo artigo de C.A.R. Hoare 1978Communicating sequential processes” (Comunicação de processos sequenciais).

Simultaneidade nas ideias do CSP

Um dos modelos mais bem-sucedidos para fornecer suporte linguístico de alto nível para concorrência é o Hoare’s Communicating Sequential Processes, ou CSP. Occam e Erlang são duas línguas bem conhecidas que derivam de CSP. As primitivas de concorrência de Go derivam de uma parte diferente da árvore genealógica cuja principal contribuição é a poderosa noção de canais como objetos de primeira classe. A experiência com várias linguagens anteriores mostraram que o modelo CSP se encaixa bem em uma estrutura de linguagem procedural.

A maior diferença entre Go e o modelo CSP, além da sintaxe, é que o Go modela os canais de comunicação simultânea explicitamente como canais, enquanto os processos da linguagem de Hoare enviam mensagens diretamente uns aos outros, semelhante ao Erlang.

Bem para chegar neste resultado Go novamente sofreu críticas do modelo adotado. Go incorpora uma variante do CSP (Comunicação de processos sequenciais) é uma linguagem formal para descrever padrões de interação em sistemas concorrentes com canais de primeira classe. Não foi adotada uma abordagem de gravação única para valorizar a semântica no contexto da computação concorrente como é feita no Erlang, ao invés disto adotaram algo prático e que resultou em algo poderoso, permitindo programação simultânea simples e segura, mas não proíbe programação incorreta. E o lema criado foi: “Não se comunique compartilhando memória, compartilhe memória comunicando-se”.

A simplicidade e o suporte para concorrências oferecidas por Go gera robustez.

Concorrência é diferente de Paralelismo

Uma das famosas frases é: “Concorrência é sobre lidar com muitas coisas ao mesmo tempo. Paralelismo é fazer muitas coisas ao mesmo tempo”. Concorrência é a composição de cálculos de execução independente. Concorrência é uma maneira de estruturar software, e definitivamente não é paralelismo embora permita o paralelismo.

Se você tiver apenas um processador, seu programa ainda pode ser concorrente, mas não pode ser paralelo. Por outro lado, um programa concorrente bem escrito pode ser executado de forma eficiente em paralelo em um multiprocessador. Sugiro dar uma conferida neste vídeo de uma palestra do Rob PikeConcorrência não é Paralelismo”.

Concorrência em Go é muito poderosa e também simples de codar está foi a intensão dos engenheiros que desenvolveram Go. A resolução de muitos problemas é bem mais eficiente utilizando concorrência e este é o poder de Go, por isto, se tornou um Deus quando o assunto é concorrência. Devido a isto, problemas englobados neste universo serão resolvidos com muita eficiência e o mais importante, com muito pouco recurso computacional.

Quando falamos de concorrência temos que ressaltar que existem alguns patterns e descrevo eles logo abaixo, e existe alguns tipos de cargas de trabalhos eles serão nossa bússola para determinar nosso ponto de equilíbrio quando tivermos que resolver problemas evolvendo concorrência.

Workloads: CPU-Bound & IO-Bound

Uma thread pode fazer dois tipos de cargas de trabalhos (Workloads): CPU-Bound e o IO-Bound.

CPU-Bound: Esta carga de trabalho nunca irá cria uma situação em que a thread pode ser colocado em estados de espera. Ele estará constantemente fazendo cálculos. Uma thread calculando pi para décima oitava potência seria limitado pela CPU. (curiosidade: Emma Haruka Iwao é uma cientista da computação japonesa e Engenheira de Desenvolvimento Cloud do Google. Em 2019, Haruka Iwao calculou o valor de pi mais preciso do mundo, que incluiu 31,4 trilhões de dígitos). Este tipo de carga de trabalho, trabalhos ligadas à CPU, você precisa do paralelismo para aproveitar a simultaneidade, mais goroutines não irá ajuda-lo e não serão eficientes, podendo atrasar ainda mais as cargas de trabalho a serem executadas, isto ocorre devido ao custo de latência (o tempo gasto) de mover Goroutines dentro e fora da thread do sistema operacional.

IO-Bound: Neste tipo de carga de trabalho as threads entram em estados de espera. Um bom exemplo seria uma solicitação de acesso a um recurso pela rede ou fazer chamadas para o sistema operacional. Uma thread que precisa acessar um banco de dados, eventos de sincronização (mutex, atomic*), todos estes exemplos fazem com que a thread aguarde então poderíamos dizer que são do tipo de trabalho **IO-Bound*. Neste tipo de carga de trabalho você não precisa de paralelismo para usar a simultaneidade um único core físico já seria suficiente para uma boa execução de várias Goroutines. As Goroutines estão entrando e saindo dos estados de espera como parte de sua carga de trabalho. Neste tipo de carga de trabalho ter mais Goroutines do que cores físicos pode acelerar a execução porque o custo de latência de mover Goroutines dentro e fora do thread do sistema operacional não está criando um evento. Sua carga de trabalho é naturalmente interrompida e isso permite que um Goroutine diferente aproveite o mesmo core físico ao invés de permitir que ele fique ocioso.

Goroutine e Pattern

Existem alguns patterns para concorrência, dentre eles: Fan-In e Fan-Out.

Fan-Out:

*Múltiplas funções podem ler do mesmo canal até que esse canal seja fechado, isso é chamado de **fan-out
* . Isso fornece uma maneira de distribuir o trabalho entre um grupo de trabalhadores para fazer uso da CPU e E/S de forma concorrente.

Fan-In:

É a forma de ler a partir de múltiplas entradas e prosseguir até que todas estejam fechadas, multiplexando os canais de entrada em um único canal que está fechado quando todas as entradas estão fechadas. Isso é chamado fan-in.

*Pipeline:

**Não há definição formal de um pipeline em Go, é apenas um dos muitos tipos de programas concorrentes. Informalmente, um pipeline é uma série de *estágios
conectados por canais, onde cada estágio é um grupo de goroutines executando a mesma função. Em cada etapa, os goroutines:

  • Recebem valores do upstream via canais de entrada;

  • Executam alguma função nesses dados, geralmente produzindo novos valores;

  • Enviam valores a jusante através de canais de saída.

Cada estágio possui vários canais de entrada e saída, exceto o primeiro e o último estágio, que possuem apenas canais de entrada ou saída, respectivamente.

Para se ter uma pequena ideia, em uma máquina de 1CPU e 1G Ram, conseguimos com uma API simples feita em Go, com poucas linhas de código, aguentar de 5k a 6k de requisições por segundo em um protocolo TCP, utilizando RPC ou TCP nativo e gravando em um banco de dados relacional como o Postgresql, sem derrubá-lo e consumindo em média somente 60Mb a 70Mb de memória e de 8% a 15% de CPU para que isto seja possível utilizamos um “Worker Poll” que é um Fan-out.

Que aprofundar ainda mais sobre concorrência? Só entrar neste link. Quer testar e visualizar alguns exemplos? Click Aqui.

Vamos criar um post só para falarmos sobre Goroutine é um tópico extenso muito cheio de detalhes e muitas situações interessantes. Sabemos que o suporte a concorrência em Go o torna um Deus perante aos problemas que são melhores resolvidos usando concorrência.

Go é uma linguagem compilada, um like de C, muito focada na produtividade. Para projetos simples ou complexos Go sempre será uma excelente alternativa, pois torna tudo mais simples e enxuto, fácil de codar e dar manutenção.

package main

import "fmt"

func main() {
    fmt.Printf("Olá, eu amo Go!\n")
}
Enter fullscreen mode Exit fullscreen mode

Não existe ainda bala de prata

Uma solução ou ferramenta para resolver todos os problemas não existe ainda é claro. O que sabemos é que existe uma infinidade de soluções desenvolvidas em diversas linguagens todas com um propósito e cada uma delas tem suas características. Por este motivo existem paradigmas de programação é uma forma de classificar as linguagens baseado em suas funcionalidades facilitando o entendimento e o contexto que ela poderá está melhor inserida e é claro com isto teremos uma visão sobre sua estruturação e execução das linguagens.

Esta cada vez mais comum linguagens suportarem múltiplos paradigmas. Sabemos que existem classes diferentes na computação quando o assunto é resolver problemas, quanto mais próximos destes assuntos estivermos, mais confortáveis estaremos para escolher uma determinada linguagem para resolver problemas, e assim sabermos qual ferramenta usar no momento certo. Na prática esta tarefa não é tão fácil mas o que sabemos é que ainda não criaram a bala de prata para resolver todos os problemas de uma única vez com apenas uma linguagem, se existisse não existiria mais P versus NP e todos os problemas poderiam ser resolvidos em tempo polinomial.

As coisas Boas de Go

  • Não possui Generics;

  • Não possui sobrecarga do operador;

  • Não possui aritmética de ponteiros;

  • Não é explícito o gerenciamento de Threads;

  • Possui inferência de tipos;

  • As Dependências são claras;

  • A sintaxe é limpa;

  • A semântica é clara;

  • A composição e não herança;

  • GC Nativo (Garbage collector);

  • Possui Goroutines;

  • Suporte nativo a programação concorrente;

  • Funções de Primeira Classe (podemos passar funções como parâmetros);

  • Multiplataforma;

  • Open Source;

  • Curva de Aprendizado muito baixa ou seja fácil de aprender;

  • Altamente produtivo;

  • Resolve problemas de escalabilidade;

  • Pode diminui seus custos de servidores drasticamente;

  • Diversos problemas pensados na perspectiva Go torna-se muito mais fáceis de tratar e resolvê-los;

  • Possui somente 25 Palavras reservadas;

  • Multi-paradigma: concorrente, funcional, imperativa, orientada a objeto;

  • Memory Safe.

Sabemos que diversos pontos acima serão criticados mas na prática e com o tempo percebemos que todas as simplificações omitidas tornaram Go uma linguagem ainda mais poderosa.

Não há Generics ainda

Existem motivos fortes de Go não ter implementado Generics no inicio do projeto, há uma discussão nas comunidades em geral sobre este tema. Go deixou de fora diversas funcionalidades, dentre elas: “Generics” que em sua concepção iria comprometer diretamente o desempenho e propósito que a linguagem Go propusera a resolver. Os que reclamam são em sua grande maioria desenvolvedores que vieram de linguagens que usam esse recurso.

No dia 29 de novembro de 2018 os responsáveis pela manutenção do código fonte de Go abriram a possibilidade de enviarem proposta de novas funcionalidades com todos os detalhes para que possam

analisá-las e talvez implementá-las na versão Go 2.0, para terem uma boa noção leiam este arquivo clicando aqui.

Generics é uma forma de fazer cast de tipos diferentes em métodos e classes. É um tipo de Polimorfismo de inclusão e paramétrico, são funções que operam da mesma forma sobre objetos de tipos diferentes ou seja funções genéricas.

Exemplo:

1. Imagina um método que recebe um vetor do tipo int, e imprima na tela.

2. Imagina que precise fazer isso agora com tipo String e do tipo float, teria que fazer novos métodos ou funções várias vezes, repetindo o código.

*Solução:

*Para não precisar criar vários métodos é criado somente um método como generics para poder receber qualquer tipo.

Percebe-se que isso só é problemas em linguagens que precisam declarar tipos de variáveis ou funções etc, em linguagens dinâmicas como PHP por exemplo não tem essa necessidade, não vou dizer linguagens fortemente tipadas ou fracamente tipadas pois é um pouco mais complexo que isso e isto levaria a um outra discussão.

Em Go conseguimos resolver todos os problemas dessa natureza usando reflect e outras usando interface. Go adota uma abordagem incomum para programação orientada a objeto então não faz muito sentido no momento ter uma funcionalidade que não agrega tanto para contribuir com o verdadeiro propósito que a linguagem foi construída, além do recurso deixar todo processo de execução e compilação bem mais lento e complexo.

Porém no ano de 2020 a proposta para Generics ficou ainda mais próximo de tornar-se realidade não temos dúvidas que veem algo por aí relacionado ao Generics então vamos ver na prática como isto seria possível usando Go.😎

Abaixo três exemplos com três propostas para resolver o problema desta forma irá conseguir perceber quando falamos em Generics😊.

Copiando e Colando funções com tipos específicos

package main

// Pega um slice de qualquer tipo e a inverte.
func Reverses(list []string) []string {
    i := 0
    j := len(list) - 1
    for i < j {
        list[i], list[j] = list[j], list[i]
        i++
        j--
    }
    return list
}

// Pega um slice de qualquer tipo e a inverte.
func Reversei(list []int) []int {
    i := 0
    j := len(list) - 1
    for i < j {
        list[i], list[j] = list[j], list[i]
        i++
        j--
    }
    return list
}

func main() {
    s := []string{"otoni","jeff","Go"}
    Reverses(s)
    println(s[0], s[1], s[2])

    i := []int{2020,2012,2009}  
    Reversei(i) 
    println(i[0], i[1], i[2])
}
Enter fullscreen mode Exit fullscreen mode

Usando Interface

package main

// Pega um slice de qualquer tipo e a inverte.
func Reverse(in interface{}) interface{} {
    switch in.(type) {
        case []string:
        list := []string(in.([]string))
        i := 0
        j := len(list) - 1
        for i < j {
               list[i], list[j] = list[j], list[i]
           i++
           j--
            }
            return list

         case []int:
        list := []int(in.([]int))
        i := 0
        j := len(list) - 1
        for i < j {
           list[i], list[j] = list[j], list[i]
           i++
           j--
            }
            return list
    }
    return nil
}

func main() {
    s := []string{"otoni","jeff","Go"}
    Reverse(s)
    println(s[0], s[1], s[2])

    i := []int{2020,2012,2009}  
    Reverse(i)  
    println(i[0], i[1], i[2])
}
Enter fullscreen mode Exit fullscreen mode

Utilizando Generics

// https://go2goplay.golang.org/
package main

import (
    "fmt"
)

// The playground now supports parentheses or square brackets (only one at
// a time) for generic type and function declarations and instantiations.
// By default, parentheses are expected. To switch to square brackets,
// the first generic declaration in the source must use square brackets.

func Print[type T] (s []T){
    for _, v := range s {
        fmt.Print(v)
    }
    fmt.Println("")
}

func Reverse[type T](list []T) {
    i := 0
    j := len(list) - 1
    for i < j {
        list[i], list[j] = list[j], list[i]
        i++
        j--
    }
}

func main() {
        var i []interface{}
        i = append(i, "jeffotoni")
        i = append(i, " ano de 2020 ")
        i = append(i, []string{"otoni", "jeff", "Go"})
    i = append(i, []int{2020, 2012, 2009})
        Print(i)

    s := []string{"otoni", "jeff", "Go"}
    Reverse(s)
    Print(s)

    intt := []int{2020, 2012, 2009}
    Reverse(intt)
    Print(intt)
}
Enter fullscreen mode Exit fullscreen mode

Composição não herança

A abordagem adotada por Go foge da regra e é incomum para programação orientada a objetos, permitindo métodos em qualquer tipo, não apenas classes, mas sem qualquer forma de herança baseada em tipos, como subclasses. Isso significa que não há hierarquia de tipos. Isto não é um bug, não é uma desvantagem ou erro, é uma escolha intencional de design dos desenvolvedores de Go. Embora as hierarquias de tipos tenham sido usadas para construir softwares bem-sucedidos, é uma postura e opinião da equipe de desenvolvedores do Go, onde preferiram dar um passo para trás ao invés de fazerem igual a todos, o mesmo modelo repetitivo em diversas linguagens.

Interfaces são implícitas

Em vez disso, o Go possui interfaces, que são apenas um conjunto de métodos e nada mais.

Exemplo:

type Gemetric interface {
  area() float64
  perim() float64
}
Enter fullscreen mode Exit fullscreen mode

Todos os tipos de dados que implementam esses métodos satisfazem essa interface implicitamente, não há implements na declaração. A satisfação da interface é verificada estaticamente em tempo de compilação, portanto, apesar das interfaces de desacoplamento, elas são seguras quanto ao tipo.

Um tipo geralmente satisfaz muitas interfaces, cada uma correspondendo a um subconjunto de seus métodos. Essa fluidez de satisfação de interface incentiva uma abordagem diferente para a construção de software uma quebra no paradigma existente.

Por que as interfaces não são explícitas ?

A programação orientada a objetos fornece uma visão poderosa: que o comportamento dos dados pode ser generalizado independentemente da representação desses dados. O modelo funciona melhor quando o comportamento (conjunto de métodos) é fixo, mas depois que você insere uma subclasse em um tipo e adiciona um método, os comportamentos não são mais idênticos . Se, em vez disso, o conjunto de comportamentos for fixo, como nas interfaces definidas estaticamente em Go, a uniformidade de comportamento permite que dados e programas sejam compostos uniformemente, ortogonalmente e com segurança.

As hierarquias de tipo resultam em código frágil. Go estimula a composição e não herança, usando interfaces simples, geralmente de um método, para definir comportamentos triviais que servem como limites claros e compreensíveis entre os componentes e a eliminação da hierarquia de tipos também elimina uma forma de hierarquia de dependência.

Os designs não são como métodos hierárquicos herdados de subtipo. Eles são mais soltos, orgânicos, desacoplados, independentes e portanto escaláveis.

package main

import "fmt"
import "math"

type Gemetric interface {
    area() float64
    perim() float64
}

type rect struct {
    width, height float64
}

type circle struct {
    radius float64
}

func (r rect) area() float64 {
    return r.width * r.height
}

func (r rect) perim() float64 {
    return 2*r.width + 2*r.height
}

func (c circle) area() float64 {
    return math.Pi * c.radius * c.radius
}

func (c circle) perim() float64 {
    return 2 * math.Pi * c.radius
}

func measure(g geometry) {
    fmt.Println(g)
    fmt.Println(g.area())
    fmt.Println(g.perim())
}

func main() {
    r := rect{width: 3, height: 4}
    c := circle{radius: 5}

    measure(r)
    measure(c)
}
Enter fullscreen mode Exit fullscreen mode

Duck typing

O Go suporta “Duck typing”, é um estilo de tipagem em que os métodos e propriedades de um objeto determinam a semântica válida, em vez de sua herança de uma classe particular ou implementação de uma interface explicita. O nome do conceito refere-se ao teste do pato, atribuído à James Whitcomb Riley. Isso faz com que o Go se pareça com uma linguagem dinâmica.

Go usa “Tipagem Estrutural“ em métodos para determinar a compatibilidade de um tipo com uma interface. Não há hierarquias de tipos e a “Tipagem Estrutural” é uma alternativa interessante à herança clássica em linguagens com tipagem estática. Ele permite que você escreva algoritmos genéricos sem obscurecer a definição de um tipo em um mar de interfaces. Talvez mais importante, ajuda as linguagens com tipagem estática a capturar a sensação e a produtividade das linguagens dinamicamente tipificadas.

“Duck typing” ocorre em tempo de execução e “Tipagem Estrutural” que ocorre em tempo de compilação. Go não da suporte a orientação a objetos como é implementado nas linguagens como C#, Java ou mesmo C ++, não possui herança e honestamente fico muito feliz com isto, mas oferece alguns recursos como composição e interfaces.

package main

import (
    "fmt"
)

type familia interface {
    dados() string
}

type pai struct {
    Nome  string
    Idade int
    Cpf   string `json:"campo-x"`
}

func (p pai) dados() string {
    return fmt.Sprintf("Nome: %s, Idade: %d", p.Nome, p.Idade)
}

type filho struct {
    pai
    email string
}

func (f filho) dados() string {
    return fmt.Sprintf("Nome: %s, Idade: %d, Email: %s", f.Nome, f.Idade, f.email)
}

func mostraDados(membro familia) {
    fmt.Println(membro.dados())
}
func main() {
    pai := new(pai)
    pai.Nome = "Jeff"
    pai.Idade = 50
    pai.Cpf = "00.xxx.xxx-xx"

    filho := new(filho)
    filho.Nome = "Arthur"
    filho.Idade = 20
    filho.email = "arthur@gmail.com"

    mostraDados(pai)
    mostraDados(filho)
 }
Enter fullscreen mode Exit fullscreen mode

Muito suporte a bibliotecas de terceiros

No cenário atual Go disparou como linguagem de programação e todos os grandes, médios e pequenos players existentes estão desenvolvendo suas libs, sdks, clients etc. na linguagem Go. Existe milhares de iniciativas e muitas bibliotecas de terceiros. Tudo colabora para que isto ocorra, a comunidade Go é muito forte, cresce a cada dia, tem uma curva de aceitação muito alta e com isto facilita sua entrada nas empresas e sua utilização pelos desenvolvedores.

O resultado disto é a disseminação em massa na internet com bibliotecas escritas em Go. Existe também bibliotecas que estão abandonadas, como em qualquer outra linguagem mas funcionais. Mas o número de bibliotecas escritas em Go é muito mas muito grande. Diversas empresas estão reescrevendo suas plataformas em Go isto é uma vertente que não para de crescer. E dia após dia o número de ferramentas para DevOps cresce, e com isto a aceitação e o aumento de novos adeptos em Go cresce na cultura e comunidade DevOps.

Gerenciamento de dependências nativo

Go teve um momento de trevas e durante um bom tempo não teve um gerenciador de dependências oficial. Após algumas tentativas e diversos posts e discussões sobre o assunto. O protótipo inicial VGO foi anunciado em fevereiro de 2018. Em julho de 2018, o suporte para módulos com versão foi instalado no repositório principal. O Go 1.11 foi lançado em agosto de 2018.

O Go 1.11 inclui suporte preliminar para módulos com versão, conforme proposto aqui . Mesmo que alguns detalhes possam mudar, versões futuras suportarão módulos definidos usando Go 1.11 ou 1.12, os testes e utilização em massa que estamos atualmente fazendo usando “go mod” acreditamos que não tem volta, ficou simplesmente fantástico, mesmo sendo anunciado como um módulo experimental está muito simples e poderoso.

Go é uma linguagem muito nova e o que surpreende é que em muito pouco tempo já exista uma infinidade de bibliotecas disponíveis para diversas necessidades e inúmeros tipos especificados de problemas e não para de crescer.

$ go mod init yourapp
$ go build
Enter fullscreen mode Exit fullscreen mode

Não é mais obrigatório o $GOPATH

Após a implementação do controle de versão o VGO em Go, temos a possibilidade agora de não precisar que nossos projetos estejam abaixo do $GOPATH, agora podemos criar nossos projetos em qualquer diretório fora do $GOPATH. Esta funcionalidade só é possível devido o controle de versão, quando usamos o “go mod” os nossos workspaces de projetos passam a ser controlados de uma nova forma, basta executar o comando “go mod init yourapp” é criado um arquivo “go.mod” nele irá conter a localização do seu path e a versão que ele encontra-se. Muito simples e lindo.

Toda vez que formos executar nosso projeto Go podemos utilizar a variável ambiente “GO111MODUL=yes|no” para determinar se iremos utilizar o “go mod” ou não.

Garbage collector (GC)

Os GCs são peças complexas de software, são décadas de pesquisas e implementações, diversas tentativas de cientistas da computação em fazer um GC melhorado e confiável. Não é uma tarefa simples, devido a isto existe uma corrente muito grande na internet descrevendo os prós e contras de um GC. O que temos que levar em consideração é que independente do resultado, se não tivermos um GC teríamos que construir um para resolver o problema de alocação.

Geralmente existem duas maneiras de alocar memória em Go: na pilha e o heap. Em geral os desenvolvedores estão familiarizados com a pilha basta escrever um programa recursivo e irá perceber um estouro da pilha caso não faça corretamente. O heap, por outro lado, é um pool de memória que pode ser usado para alocação dinâmica.

As alocações de pilha são ótimas porque elas só vivem durante a vida útil da função da qual fazem parte. As alocações de heap, no entanto, não serão desalocadas automaticamente quando saem do escopo. Para evitar que o heap fique desassociado, devemos desalocar explicitamente ou, no caso de linguagens de programação com gerenciamento de memória (como Go), confiar no coletor de lixo para localizar e excluir objetos que não são mais referenciados.

De um modo geral, em linguagens com um GC, quanto mais você puder armazenar na pilha, melhor, uma vez que essas alocações nunca são vistas pelo GC. Compiladores usam uma técnica chamada análise de escape para determinar se algo pode ser alocado na pilha ou deve ser colocado no heap.

Na prática, escrever programas que forcem o compilador a somente alocar na pilha pode ser muito limitante, e assim, no Go, nós aproveitamos seu maravilhoso GC para fazer o trabalho de manter nosso heap limpo.

Quer da uma conferida e aprofundar ainda mais no assunto entre neste link escrito por R*ichard L. Hudson* https://blog.golang.org/ismmkeynote.

Alguns tipos de aplicações implementadas em Go

Go é uma linguagem de uso geral. Neste link você conseguirá visualizar diversas aplicações implementadas em Go e suas respectivas categorias.

Alguns tipos:

. Web backend (com diversos frameworks disponíveis)

. Web Assembly (um dos frameworks vugu)

. Microservices (alguns frameworks: Go Micro, Go Kit, Gizmo, Kite)

. Fragments services (Termo citado pelo @jeffotoni em um grupo de discussão de microservices)

. Lambdas (FaaS example)

. Client Server

. Aplicações em terminal (utilizando a lib tview)

. IoT (alguns frameworks)

. Boots (alguns aqui)

. Aplicações Client que usam tecnologia Web

. Desktop Usando Qt+QML, Lib Nativa Win (example Qt, widgets Qt, Qml)

. Aplicações de Rede

. Aplicações para protocolos

. Aplicações rEST,

. Aplicações SOAP

. Aplicações GraphQL

. Aplicações RCP

. Aplicações TCP

. Aplicações gRPC

. Aplicações Websocket

. GopherJS (compiles Go to JavaScript)

Top comments (0)