functional-first web development rodrigo vidal f# developer @ vtex twitter & github:...
TRANSCRIPT
Functional-FirstWeb Development
Rodrigo VidalF# Developer @ VTEX
Twitter & Github: @rodrigovidal
F# é Open Source
github.com/Microsoft/visualfsharp
github.com/fsprojects/
Monads, Functor, Traversables, Lens, Applicative, Monoids, Arrows, Monad Transformers..
7 formas de baixo risco para usar
• REPL• Testes de unidade, integração ou aceitação• Scripts• Prototipar• Build• Gerenciamento de Pacotes• Escrever uma pequena parte do sistema em F#
Testes
Buildando seu projeto com FAKE
Funcionalidades
• Concisa• Imutabilidade por default• Funções de Primeira Classe• Composição • Funções “curried” por
default• Pattern Matching• Record Types
• Pipes• Discriminated Unions• Type Providers• Computation
Expressions• Poderoso Type System• Active Patterns
Por que nós usamos F# na VTEX?
NULL
F# Elimina classes inteiras de Erros
Em F# tudo é imutável e mesmo as classes não podem ser nulas*
http://theburningmonk.com/2014/12/seven-ineffective-coding-habits-many-f-programmers-dont-have/
Visualização
Pipeline
Record Types
High Order Functions
let ok, result = recoverConfigurations () if okthen launch result.xelse log result.s
Boolean Blindness
let result = recoverConfigurations () match result with| Success x -> launch x| Failure s -> log s
Boolean Blindness
DEMO Web Api
Requisito
• “Ao cadastrar um usuário, enviar uma email solicitando que seu login seja ativado.”
Modelagem de Domínio
type User = {FirstName: stringLastName: stringEmail: stringPassword: stringIsActivate : bool
}
Modelagem de Domínio
type User = {FirstName: string50LastName: string30
optionEmail: EmailPassword: Password
}
String Oriented Programming
Units of Measure
type Product = { Name : string Price : decimal Quantity : decimal}
type Product = { Name : string50 Price : decimal<real> Quantity : decimal<quantity>}
A abordagem Funcional para tratamento de Erros
• O Caminho Feliz• O Caminho Triste
O Caminho Feliz
string CreateUser(Request request) {validateRequest(request);verifyEmail(request);db.createUser(request);
smtpServer.sendEmail(request.Email)return “OK”;
}
O Caminho Feliz em F#
let createUser = validateRequest>> verifyEmail>> db.createUser>> smtpServer.sendEmail>> returnMessage
O Caminho Triste
string Create(Request request) {var isValid = validateRequest(request);if (!isValid) {
return “Request is not valid”}verifyEmail(request);db.createUser(request);smtpServer.sendEmail(request.Email)return “OK”;
}
O Caminho Triste
string Create(Request request) {var isValid = validateRequest(request);if (!isValid) {return “Request is not valid”;}verifyEmail(request);var result = db.createUser(request);if (!result) {return “Conflict”;}smtpServer.sendEmail(request.Email)return “OK”;
}
O Caminho Tristestring Create(Request request) {
var isValid = validateRequest(request);
if (!isValid) {return “Request is not valid”;
}
verifyEmail(request);
try {
var result = db.createUser(request);if (!result) {
return “Conflict”;}
} catch {
return “DB error: User record not created”
}
smtpServer.sendEmail(request.Email)
return “OK”;
}
O Caminho Tristestring Create(Request request) {
var isValid = validateRequest(request);
if (!isValid) {return “Request is not valid”;
}
verifyEmail(request);
try {
var result = db.createUser(request);if (!result) {
return “Conflict”;}
} catch {
return “DB error: User record not created”
}
if (!smtpServer.sendEmail(request.Email)) {log.Error(“User email not sent.”);
}
return “OK”;
}
É possível manter a elegância do Caminho Feliz
no Caminho Triste?
Pense nas funções como um Fluxo
let createUser = validateRequest>> verifyEmail>> db.createUser>> smtpServer.sendEmail>> returnMessage
Este era o código do Caminho Feliz
Pense nas funções como um Fluxo
let createUser = validateRequest>> verifyEmail>> db.createUser>> smtpServer.sendEmail>> returnMessage
Este é o código do Caminho Triste
Design não funcional
Funções
Como uma função pode ter mais de uma saída?
type Result = | Success|
ValidationError| CreateError| SmtpError
type Result = | Success| Failure
type Result = | Success| Failure
type Result<'TEntity> = | Success of 'TEntity| Failure of string
type Result<'Tentity, 'TFailure> = | Success of 'TEntity| Failure of 'Tfailure list
Retornando um Resultlet validateInput input =
if input.name = “” thenFailure “Name must not be blank”elif input.email = “” thenFailure “Email must not be
blank”elseSuccess input
type Input = {name : stringemail :
string}
type Result<Input> = | Success of Input| Failure of string
let validateInput input = if input.name = “” then
Failure NameMustNotBeBlankelif Regex(emailP, input.email)
thenFailure EmailNotValid
else Success input
type Result<Input, ‘TFailure> =
| Success of Input| Failure of ‘TFailure
type ErrorMessage = |
NameMustNotBeBlank | EmailNotValid of
Lidando com Result + Async
O padrão importa!
Obrigado!