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