Programação Funcional
com Clojure
Ederson Lima
Sérgio Rodrigues
Agenda
- Conceitos Funcionais
- O que é?
- Características
- Conceitos de Clojure
- Pra que serve os parênteses?
- Introdução ao Clojure
- Código
Programação Funcional
O que é?
- Paradigma de programação
- Funções matemáticas
- Alto nível de abstração
- Funções que computam um resultado por meio de valores de entradas
Programação Funcional
Lambda Calculus:
- Sistema formal que estuda funções recursivas
- Criado por Alonzo Church na década de 30
Programação Funcional
Características de uma Linguagem Funcional:
- Funções de alta ordem:
- Passagem de funções por parâmetro
- Retorno de funções por outras funções
- Composição de funções
- Funções anônimas
Programação Funcional
Imutabilidade:
- Não é possível alterar valores definidos
- Uma função sempre irá retornar o mesmo resultado
- Evita efeitos colaterais
- Ideal para ambientes concorrentes e paralelos
// C
int total = 0;
int number = 10;
total = number * 2;
# Ruby
name = 'Cruzeiro'
name.upcase!
# CRUZEIRO
puts name
class Person
attr_reader :age
def initialize(age)
@age = age
end
def birthday
@age = age + 1
end
end
person = Person.new(22)
# 22
person.age
person.birthday
# 23
person.age
(def age 18)
; 18
(println age)
(defn change []
(let [age (+ age 1)]
(println age)))
; 19
(change)
; 18
(println age)
Programação Funcional
Funções Puras:
- Uma função com a mesma entrada sempre retornará o mesmo resultado
- Facilita a previsibilidade
- Requisições HTTP e algo que envolva acesso externa não são funções puras
; Funções Impuras
; 0.882573648494652
(rand)
; 0.03485585825505988
(rand)
; Funções Puras
; 3
(+ 1 2)
; 3
(+ 1 2)
Programação Funcional
Composição de Função:
- Utilizar várias funções para construir um resultado
- Passar o resultado de uma função como parâmetro de outra
; 7
(+ 1 (* 2 3))
Programação Funcional
Recursão:
- Uma função chama ela mesma
- Utilizada para diversos cálculos matemáticos
(defn sample-recur
[number]
(case number
0 0
1 1
(+ 3 (sample-recur (dec number)))))
; 28
(sample-recur 10)
(defn sample-recur
[number]
(if (= number 10)
number
(recur (inc number))))
; 10
(sample-recur 1)
Programação Funcional
Currying:
- Decomposição de função
- Converter uma função com múltiplos parâmetros em várias funções com um único parâmetro
- Uma função é aplicada parcialmente sobre seus parâmetros
add = ->(a, b) { a + b }
plus_two = add.curry[2]
# 6
plus_two[4]
# 7
plus_two[5]
function add (a) {
return function (b) {
return a + b;
}
}
add(3)(4);
var add3 = add(3);
// 7
add3(4);
(def hundred-times (partial * 100))
; 500
(hundred-times 5)
Conceitos de Clojure
(LISP):
- (Abrindo um parêntese
- (Família de linguagens concebidas por John McCarthy
- (Seu nome vem de LiSt Processing
- (A lista é um elemento fundamental na linguagem
- (Tanto dados e programas são listas
- )))))
(defun fatorial (n)
(if (= n 0)
1
(* n (fatorial (- n 1)))))
Conceitos de Clojure
JVM:
- Hospedado na JVM
- Compartilha GC, tipos, threads
- Todas as funções são compiladas em bytecodes JVM
- Interoperabilidade
Conceitos de Clojure
REPL (Read Eval Print Loop):
- Tipo o IRB do Ruby
- Avaliação de expressões
- Console
- Desenvolvimento interativo
- Alta integração com editores de texto (emacs)
Conceitos de Clojure
Leiningen:
- Tipo o rake + bundler do Ruby
- http://leiningen.org
- Criação de projetos, compilação, testes, tarefas
Clojure Way
; Tudo se resume à:
(OPERADOR OPERANDOS)
Conceitos de Clojure
Sequences:
- Um nome bonito para lista
- É a interface genérica de todas as estruturas no Clojure, inclusive o código
Conceitos de Clojure
Características da Linguagem:
- Criada por Rich Hickey em 2007
- Dialeto mais famoso do LISP
- Dinâmica
- Fortemente tipada
- Ideal para ambientes concorrentes
Conceitos de Clojure
Características da Linguagem:
- Compilada, apesar de todas as features também estarem disponível em tempo de execução
- Não há distinção entre código e estruturas de dados
- Possibilidade de extensão da linguagem com macros
- Roda na JVM, V8, .NET
Introdução a Linguagem
Agora é a hora do código:
- Instalem o Leiningen
- Entrem no REPL:
- $ lein repl
; Não sabe o que a função faz?
(doc def)
; Continua não sabendo?
http://clojuredocs.org
Hello World e Operações
(println "Hello World")
(+ 1 2)
(- 25 5)
(* 25 2)
(/ 10 2)
(+ (* 25 2) 2)
Tipos
; Number
10
10.55
; String
"Hello World"
; Vector
[100 "Hello"]
Tipos
; Keyword
:hello-world
; Map
{:name "Clojure" :since 2007}
; Set
#{150 160 "Hello World"}
Tipos
; A pai de todas:
; List
'(1 2 3 4)
; Quer saber qual a classe java?
(type 10)
Nomeação de valores
; Os nomes definidos são constantes.
; Não podem ser alterados.
; Somente redefinidos.
; O escopo é global.
(def space 50)
(def hour 0.5)
; 100km/h
(/ space hour)
Nomeação de valores
; O let é usado para escopo interno.
(let [course "Clojure"] course)
; Unable to resolve symbol: course
(println course)
Controle de Fluxo
(def cruzeirao {:name "Cruzeiro" :brasileiros 4})
(if (= (:brasileiros cruzeirao) 4)
(do
(println "TETRACAMPEAO!!!")
(println "Cruzeirao maior do brasil!"))
(println "ALGUMA COISA TA ERRADA?!"))
(when (= (:brasileiros cruzeirao) 4)
(println "TETRACAMPEAO!!!")
(println "Cruzeirao maior do brasil!"))
Controle de Fluxo
(defn pos-neg-or-zero
[n]
(cond
(< n 0) "negative"
(> n 0) "positive"
:else "zero"))
; positive
(pos-neg-or-zero 10)
Controle de Fluxo
(defn campeao
[titulos]
(case titulos
4 "Tetra campeão"
3 "Tri campeão"))
; Tri campeão
(campeao 3)
Repetição
; (3 4 5 6)
(for [number [1 2 3 4]]
(+ 2 number))
; 10 8 6 4 2
(loop [x 10]
(when (> x 1)
(println x)
(recur (- x 2))))
Standard Library
(defn upper
[title]
(clojure.string/upper-case title))
; CRUZEIRO CORINTHIANS
(map upper ["cruzeiro" "corinthians"])
(def numbers [1 2 3 4])
; 2 3 4 5
(map inc numbers)
; 10
(reduce + numbers)
(reduce (fn [acc elem]
(+ acc elem)) numbers)
Standard Library
(def numbers [1 2 3 4])
; [1 2 3 4 10 20]
(conj numbers 10 20)
;[1 2 3 4 [10 20 30]]
(conj numbers [10 20 30])
; [1 2 3 4 1 2 3 4]
(into numbers '(1 2 3 4))
; 4
(count numbers)
; False
(empty? numbers)
Standard Library
; (1 3)
(filter (fn [number]
(odd? number))
numbers)
; (2 4)
(filter #(even? %) numbers)
; Lazy Sequences
; ?
(take 10 (repeatedly (fn [] (rand-int 10))))
; (0 1 2 3 4 5 6 7 8 9)
(take 10 (range))
Standard Library
(defn twice-numbers
([] (twice-numbers 0))
([n] (cons n (lazy-seq (twice-numbers (+ n 3))))))
; (0 3 6 9 12 15 18 21 24 27)
(take 10 (twice-numbers))
Criando o Projeto
$ lein new app dna-to-rna
$ cd dna-to-rna
$ touch src/dna_to_rna/converter.clj
Exercício
Converter DNA para RNA:
- Dada uma cadeia de DNA, retorne seu complemento RNA
- Os quatro nucleotídeos do DNA são: adenina (A), citosina (C), guanina (G) e timina (T)
- Os quatro nucleotídeos do RNA são: adenina (A), citosina (C), guanina (G) e uracila (U)
Exercício
Converter DNA para RNA:
- Input: ACGTGGTCTTAA
- Output: UGCACCAGAAUU
DNA -> RNA
- G -> C
- C -> G
- T -> A
- A -> U
; src/dna_to_rna/converter.clj
(ns dna-to-rna.converter)
(def dna-rna {\G \C
\C \G
\T \A
\A \U})
(defn- strand-complement
[strand]
(dna-rna strand))
(defn to-rna
[dna-strand]
(apply
str (map strand-complement dna-strand)))
; test/dna_to_rna/converter_test.clj
(ns dna-to-rna.converter-test
(:require [clojure.test :refer :all]
[dna-to-rna.converter :refer :all]))
(deftest to-rna-test
(testing "It ACGTGGTCTTAA strand of DNA returns UGCACCAGAAUU RNA"
(is (= "UGCACCAGAAUU" (to-rna "ACGTGGTCTTAA")))))
(Avançando nos parênteses)
Macros
(defmacro unless [pred a b]
`(if (not ~pred) ~a ~b))
; 5
(unless true 10 5)
(defmacro funcao-mais
[infixed]
(list
(second infixed)
(first infixed)
(last infixed)))
; 2
(funcao-mais (1 + 1))
Macros
; Tread-first
(def person
{:name "Bob"
:address {:street "Rua X" :city "Sao Paulo"}})
(->
person
:address
:city
clojure.string/upper-case)
; Thread-last
(->>
(range)
(take 10)
(map +))
(macroexpand '(->> (range) (take 10) (map +)))
Records
(defrecord Person
[name age weight city])
(def charlie (Person. "Charlie" 22 80 "Sao Paulo"))
(def victor (->Person "Victor" 30 50 "Sao Paulo"))
(def bob (map->Person {:name "Bob" :age 23}))
; "Charlie"
(:name charlie)
Protocols
(defprotocol
Mammal
(weight-increase [this quantity]))
(defrecord Person
[name age weight city]
Mammal
(weight-increase
[this quantity]
(+ quantity (:weight this) 2)))
(defrecord Dog
[name weight]
Mammal
(weight-increase
[this quantity]
(+ quantity (:weight this) 1)))
(def charlie (Person. "Charlie" 22 80 "Sao Paulo"))
(def spyke (Dog. "Spyke" 30))
(extends? Mammal Person)
(extends? Mammal Dog)
(weight-increase spyke 2)
(def mammals [charlie spyke])
(map (fn [elem]
(weight-increase elem 10)) mammals)
Interoperabilidade
(.toUpperCase "Course Clojure")
(* 2 java.lang.Math/PI)
(String. "Course")
(new String "Course")
(let [array-list (java.util.ArrayList.)]
(.add array-list 10)
(.add array-list 20)
array-list)
Future
(future (Thread/sleep 4000) (println (+ 1 2 3 4)))
(def calculator (future (Thread/sleep 10000) (+ 1 2 3 4)))
; pending
calculator
; 10
@calculator
Atom
(def number (atom 0))
; 0
@number
(swap! number inc)
; 1
@number
Destructuring
(def numbers [1 2 3 4])
(let [[a b c d] numbers]
(println a b c d))
Memoize
(defn fibo [n]
(case n
0 1
1 1
(+ (fibo (dec n)) (fibo (- n 2)))))
(time (fibo 30))
(def m-fibo
(memoize (fn [n]
(case n
0 1
1 1
(+ (m-fibo (dec n)) (m-fibo (- n 2)))))))
(time (m-fibo 30))
Exercício
Consumir API e calcular o total do PIB de todos os anos:
- Consumir rota com o JSON
- Parsear json com alguma lib
- Retornar a soma do PIB
Encerramento
Indo além
- Concorrência e paralelismo
- core.async
- Pedestal
- Aleph
- Clojurescript
Links
Referências
Clojure
By Sérgio Rodrigues
Clojure
Workshop de Programação Funcional com Clojure
- 1,051