O documento discute o uso de funções de alta ordem e interfaces funcionais em Go. Apresenta exemplos de como criar servidores web de forma funcional, encapsulando configurações e comportamentos em funções para melhor modularidade e imutabilidade.
4. Foco em simplicidade de soluções
Sintaxe simples
Uma única (ou poucas) formas de se
fazer algo
Leitura e escrita homogêneas por todo
ecossistema
Por que os exemplos em Go?
4
5. Idiomático
public class SimpleHashSetExample {
public static void main(String[] args) {
HashSet hSet = new HashSet();
hSet.add(new Integer("1"));
hSet.add(new Integer("2"));
hSet.add(new Integer("3"));
System.out.println("HashSet contains.." + hSet);
}
}
5
6. Idiomático
class Animal(val name: String)
class Zoo(val animals: List<Animal>) {
operator fun iterator(): Iterator<Animal> {
return animals.iterator()
}
}
fun main() {
val zoo = Zoo(listOf(Animal("zebra"), Animal("lion")))
for (animal in zoo) {
println("Watch out, it's a ${animal.name}")
}
}
6
8. Domínio
Estrutura das entidades
Composição ou herança
Estrutura de persistência
type Entity struct {
name string
...
}
Regra de negócio
Fluxo e manipulação de entidades
Regra de negócio
Adição ou remoção de etapas de
processamento
func (e Entity) transformedName() string {
return e.name + " with transformation"
}
...
The right tool for the right job
8
9. Server simples em Go
package main
import (
"log"
"net/http"
)
func main() {
log.Println(NewServer(":8080"))
}
func NewServer(address string) error {
r := http.NewServeMux()
r.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("My Web Servern"))
})
r.HandleFunc("/api", func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("{"success":true}"))
})
srv := &http.Server{
Addr: address,
Handler: r,
}
return srv.ListenAndServe()
}
10. Server simples em Go + Timeout
func main() {
log.Println(NewServer(":8080", 2*time.Second, 5*time.Second))
}
func NewServer(address string, rt, wt time.Duration) error {
r := http.NewServeMux()
r.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("My Web Servern"))
})
r.HandleFunc("/api", func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("{"success":true}"))
})
srv := &http.Server{
Addr: address,
Handler: r,
ReadTimeout: rt,
WriteTimeout: wt,
}
return srv.ListenAndServe()
}
10
11. Server simples em Go + Timeout + TLS
func main() {
log.Println(NewServer(":8080", "cert.pem", "key.pem", 2*time.Second, 5*time.Second))
}
func NewServer(address, cert, key string, rt, wt time.Duration) error {
r := http.NewServeMux()
r.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("My Web Servern"))
})
r.HandleFunc("/api", func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("{"success":true}"))
})
srv := &http.Server{
Addr: address,
Handler: r,
ReadTimeout: rt,
WriteTimeout: wt,
}
return srv.ListenAndServeTLS(cert, key)
}
11
13. Server simples em Go + Con g
type Config struct {
Address string
CertPath string
KeyPath string
ReadTimeout time.Duration
WriteTimeout time.Duration
}
func main() {
config := Config{":8080", "cert.pem", "key.pem", 2 * time.Second, 5 * time.Second}
log.Println(NewServer(config))
}
13
14. Server simples em Go + Con g
func NewServer(c Config) error {
r := http.NewServeMux()
r.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("My Web Servern"))
})
r.HandleFunc("/api", func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("{"success":true}"))
})
srv := &http.Server{
Addr: c.Address,
Handler: r,
ReadTimeout: c.ReadTimeout,
WriteTimeout: c.WriteTimeout,
}
return srv.ListenAndServeTLS(c.CertPath, c.KeyPath)
}
14
15. Con guration structs
Vantagens
Melhora a documentação e entendimento do código.
Permite utilizar "zero values" como regra
Crescimento das con gurações sem quebrar assinatura
15
16. Con guration structs
Desvantagens
Gera diversos possíveis problemas com "zero values", exemplo: porta 0
c := Config{
"cert.pem", "key.pem"
}
O cenário ideal para "default" value seria aleatoriamente escolher uma porta e não ir
direto para a zero. Mas e se quisermos tentar a porta zero?
Quais campos são obrigatórios e quais são opcionais?
16
17. Con guration structs
Problema da mutabilidade
config := Config{
":8080", "cert.pem", "key.pem", 2 * time.Second, 5 * time.Second
}
go func() {
config.Address = ":9090"
}()
log.Println(NewServer(config))
Problema da obrigatoridade
func main() {
//Quero o server mais simples possível!!
log.Println(NewServer(????))
}
17
18. High-Order Function
Função que faz pelo menos uma das duas:
1. Recebe uma (ou mais) funções como parâmetros
2. Retorna uma função
Exemplos:
Cálculo: dy/dx (operador de diferencial) que recebe uma função e retorna a sua
derivada
callbacks em Javascript
http.HandlerFunc(pattern, handlerFunc)
18
19. High-Order Function + rst class citizen
package main
import "fmt"
//High-Order: retorna uma função
func myFunction() func(int, int) int {
return func(a, b int) int {
return a + b
}
}
func main() {
//first class citizen: atribuir a uma variável
f := myFunction()
//first class citizen: chamar através da variável
soma := f(2, 3)
fmt.Println(soma)
}
19
20. High-Order Function + Type Alias
package main
import "fmt"
//type alias
type SomaFunc func(int, int) int
//High-Order: retorna uma função
func myFunction() SomaFunc {
return func(a, b int) int {
return a + b
}
}
func main() {
//first class citizen: atribuir a uma variável
f := myFunction()
//first class citizen: chamar através da variável
soma := f(2, 3)
fmt.Println(soma)
}
20
21. High-Order na Prática
package main
import "net/http"
func main() {
//first class citizen: atribuir a uma variável
f := handlerFunction()
http.HandleFunc("/", f)
http.ListenAndServe(":8080", nil)
}
//High-Order: retorna uma função
func handlerFunction() func(http.ResponseWriter, *http.Request) {
return func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("Contentn"))
}
}
21
24. Functional Server
Ajuste na estrutura do server: criando a struct
type Server struct {
Ltn net.Listener
ReadTimeout time.Duration
WriteTimeout time.Duration
Handler http.Handler
}
func NewServer(addr string) (*Server, error) {
dftLtn, err := net.Listen("tcp", addr)
return &Server{
Ltn: dftLtn,
ReadTimeout: 3 * time.Second,
WriteTimeout: 5 * time.Second,
}, err
}
24
25. Functional Server
Ajuste na estrutura do server: usando a struct
func (s *Server) Serve() error {
srv := &http.Server{
Handler: s.Handler,
ReadTimeout: s.ReadTimeout,
WriteTimeout: s.WriteTimeout,
}
return srv.Serve(s.Ltn)
}
func main() {
sv, err := NewServer(":8080")
if err != nil {
panic(err)
}
log.Println(sv.Serve())
}
25
26. Functional Server Com Options
Como inserir um read time out no nosso functional server?
Opção 1: parametro na criação (muda algo?)
func NewServer(addr string, readTimeout time.Duration) (*Server, error) {
dftLtn, err := net.Listen("tcp", addr)
return &Server{
Ltn: dftLtn,
ReadTimeout: readTimeout,
WriteTimeout: 5 * time.Second,
}, err
}
26
27. Functional Server Com Options
Opção 2: mutar o server depois de criado
func main() {
sv, err := NewServer(":8080")
if err != nil {
panic(err)
}
sv.ReadTimeout = 3*time.Second
log.Println(sv.Serve())
}
Imperativo: baixo reaproveitamento
Poucas abstrações
Clara e distribuída quebra de imutabilidade
27
28. Functional Server Com Options
Opção 3: criar um método no server
func (s *Server) setTimeout(timeout time.Duration) error {
sv.ReadTimeout = timeout
}
func main() {
sv, err := NewServer(":8080")
if err != nil {
panic(err)
}
sv.setTimeout(3*time.Second)
log.Println(sv.Serve())
}
Clara e distribuída quebra de imutabilidade
28
29. Functional Server Com Options
Opção 4: criar uma função que altera timeout
func WithTimeout(read, write time.Duration) func(*Server) {
return func(s *Server) {
s.ReadTimeout = read
s.WriteTimeout = write
}
}
func main() {
sv, err := NewServer(":8080")
if err != nil {
panic(err)
}
f := WithTimeout(3*time.Second, 5*time.Second)
f(sv)
log.Println(sv.Serve())
}
Sensação de maior encapsulamento
Clara e distribuída quebra de imutabilidade
29
30. Functional Server Com Option
Opção 5: criar uma função que altera timeout E recebê-la somente no Serve
func WithTimeout(read, write time.Duration) func(*Server) {...}
func (s *Server) Serve(option func(*Server)) error {
option(s)
srv := &http.Server{
Handler: s.Handler,
ReadTimeout: s.ReadTimeout,
WriteTimeout: s.WriteTimeout,
}
return srv.Serve(s.Ltn)
}
func main() {
sv, err := NewServer(":8080")
if err != nil {
panic(err)
}
log.Println(sv.Serve(
WithTimeout(3*time.Second, 5*time.Second)
))
}
Mutação ainda acontece, mas somente no uso nal
31. Functional Server Com Options (plural)
func (s *Server) Serve(options ...func(*Server)) error {
for _, opt := range options {
opt(s)
}
srv := &http.Server{
Handler: s.Handler,
ReadTimeout: s.ReadTimeout,
WriteTimeout: s.WriteTimeout,
}
return srv.Serve(s.Ltn)
}
func main() {
sv, err := NewServer(":8080")
if err != nil {
panic(err)
}
log.Println(sv.Serve(
WithTimeout(3*time.Second, 5*time.Second)
))
}
31
32. Functional Server Com Options (plural)
Inserindo rotas
func WithRoutes() func(*Server) {
return func(s *Server) {
r := http.NewServeMux()
r.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("My Web Servern"))
})
r.HandleFunc("/api", func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("{"success":true}"))
})
s.Handler = r
}
}
32
33. Functional Server Com Options (plural)
Inserindo certi cado
func WithCertificate(cert, key string) func(*Server) {
return func(s *Server) {
cert, err := tls.LoadX509KeyPair(cert, key)
if err != nil {
panic(err)
}
config := &tls.Config{Certificates: []tls.Certificate{cert}, InsecureSkipVerify: true}
//Close old listener
if err := s.Ltn.Close(); err != nil {
panic(err)
}
//Keep old address
originalAddr := s.Ltn.Addr().String()
ltn, err := tls.Listen("tcp", originalAddr, config)
if err != nil {
panic(err)
}
s.Ltn = ltn
}
}
33
35. Functional Server Com Options (plural)
Criando o server
func WithCertificate(cert, key string) func(*Server) {...}
func WithTimeout(read, write time.Duration) func(*Server) {...}
func WithRoutes() func(*Server) {...}
func main() {
sv, err := NewServer(":8080")
if err != nil {
panic(err)
}
log.Println(sv.Serve(
WithRoutes(),
WithTimeout(2*time.Second, 3*time.Second),
WithCertificate("cert.pem", "key.pem"),
))
}
Toda a con guração do server está no uxo de criação do server
Permite o criação de uma API uente
35
37. Onde usar?
Con guração de serviços, clientes ou structs
De nição de strategy pattern
Cenários de sincronia -> passagem de comportamento vs
passagem de dados
37
38. Onde usar?
Con guração de serviços, clientes ou structs
De nição de strategy pattern
Cenários de sincronia -> passagem de comportamento vs
passagem de dados
38
54. Real world examples!!!
Gorm Database Client
Formato com encadeamento de métodos
db, _ := gorm.Open(...)
var user User
err := db.Where("email = ?", "email@host.com").
Where("age >= ?", 18).
First(&user).
Error
Exige a codi cação de uma estrutura
var user User
err = db.First(&user,
Where("email = ?", "jon@calhoun.io"),
Where("id = ?", 2),
)
54
55. Vantagens
API mais fáceis e compreensíveis no
uso
Possibilidade de se entregar a closure
pronta para uso
Facilidade de se estender (novas
closures)
Valores default já pré-con gurados
Criação de uent-API com
encadeamento de closures
Decorator pattern com baixa
complexidade
Desvantagens
Mutabilidade da estrutura de
con guração
Maior quantidade de código
Maior demanda cognitiva
Mudança de paradigma para criação
soluções
The right tool for the right job
55