Programação Funcional em Haskell
29/02 e 07/03 de 2020
Table of Contents
Apresentação do Curso
Grandes empresas de tecnologia como Microsoft, Facebook, Target e também do ramo financeiro têm utilizado linguagens de programação funcionais em parte de seus projetos. Além disso, linguagens multi-paradigmas como Java e Python vêm incorporando tais conceitos a cada nova versão para aumentar a expressividade e produtividade.
O uso desse paradigma está ainda mais evidente nas áreas de Data Science, devido a necessidade de algoritmos que possam ser processados de forma distribuída e também na área de programação para Web, com a popularização de frameworks baseados em programação funcional reativa, como por exemplo o ReactJS.
Esse fato é evidenciado pelas diversas ofertas de emprego que solicitam, especificamente, por capacidades em linguagens funcionais como Elixir, Erlang e Scala. Contudo, na maior parte dos currículos de graduação em computação e em áreas correlatas é dado um maior enfoque nos paradigmas estruturados e orientados a objetos. Isso cria um obstáculo para esses profissionais quando precisam lidar com essas novas tecnologias ou para o preenchimento dos requisitos durante a busca por uma nova colocação no mercado.
Objetivos
Este curso tem como objetivo apresentar o paradigma funcional e seus benefícios através da linguagem Haskell como uma ferramenta viável de criação, entendimento e corretude de algoritmos com aplicações voltadas à data science e programação para web.
Esperamos que ao final do curso os alunos tenham condições de perceber as vantagens do uso de linguagens funcionais além de permitir novas abstrações para soluções de problemas. O aluno também estará apto a aplicar linguagens funcionais e seus conceitos para a solução de problemas básicos em data science e programação em geral, requisitos para uma grande fatia das ofertas de emprego atuais.
Neste oferecimento teremos o apoio da Caelum que gentilmente abriu as suas portas e cedeu o espaço para as aulas.
O curso é gratuito e será ministrado pelos professores da Universidade Federal do ABC, Fabrício Olivetti e Emílio Francesquini e terá duração de 12 horas.
Programação
- Dia 29/02 - Introdução à linguagem Haskell, tipos básicos, listas, funções de alta ordem
- Dia 07/03 - Tipos de dados algébricos, functores, functores aplicativos, mônadas
Agredecimento
Agradecemos à Caelum por ter gentilmente cedido o espaço para a realização deste curso.
O curso foi desenvolvido pelos Profs. Fabrício Olivetti e Emilio Francesquini da Universidade Federal do ABC.
Datas e local
A Caelum gentilmente nos cedeu o espaço para o curso que ocorrerrá:
- Datas: 29/02, 07/03
- Horário: 09h00 às 16h30 (1h30 de almoço)
- Local: Caelum - Rua Vergueiro, 3185, 8o andar, São Paulo.
O curso será ministrado pelos professores Fabrício Olivetti e Emílio Francesquini e terá duração de 12 horas.
Inscrições
- Número de vagas: 25
- Para se inscrever:
https://sig.ufabc.edu.br/sigaa/link/public/extensao/visualizacaoAcaoExtensao/973
- Para se cadastrar o usuário deve clicar em "Clique aqui para fazer a sua Inscrição" → "Ainda não possuo cadastro".
- Insira os dados solicitados e aguarde e-mail para finalização do cadastro. Após a finalização do cadastro, insira o e-mail e senha, clique em “Cursos e Eventos Abertos" e faça o cadastro no curso.
Pré-requisitos
Gostar de programar!
Como Chegar
Caelum
Rua Vergueiro, 3185, 8o andar
São Paulo - SP
Próximo à estaçào Vila Mariana do Metrô.
Material do Curso
Aula | Slides | Material |
---|---|---|
29/02 | ||
07/03 | Fonte SafeNum |
Notas de aula
Preparando o ambiente
Para instalar o compilador o jeito mais recomendado é via stack: https://docs.haskellstack.org/en/stable/install_and_upgrade/
Uma vez instalado vocês podem criar um novo programa com o comando
stack new nome-do-programa simple. Ele criará uma pasta com o nome
de seu programa e nela uma arquivo src/Main.hs
. Para programas
simples, podemos utilizar esse próprio código para implementar
todas as nossas funções.
Você consegue executar um programa com stack run
no diretório do
seu projeto (dado que ele consiga compilar :P).
Outra dica é utilizar o REPL do Haskell com stack ghci
, nele você
pode testar as funções padrões, verificar os tipos de funções e
definições, obter mais informações sobre os tipos e classes de
tipos, etc.
Prelude> [1 .. 10] [1,2,3,4,5,6,7,8,9,10] Prelude> :t [1 .. 10] [1 .. 10] :: (Num a, Enum a) => [a]
O comando :i
retorna informações sobre um tipo, quais classes
ele pertence e em qual código-fonte ele está implementado:
Prelude> :i Int data Int = GHC.Types.I# GHC.Prim.Int# -- Defined in ‘GHC.Types’ instance Eq Int -- Defined in ‘GHC.Classes’ instance Ord Int -- Defined in ‘GHC.Classes’ instance Show Int -- Defined in ‘GHC.Show’ instance Read Int -- Defined in ‘GHC.Read’ instance Enum Int -- Defined in ‘GHC.Enum’ instance Num Int -- Defined in ‘GHC.Num’ instance Real Int -- Defined in ‘GHC.Real’ instance Bounded Int -- Defined in ‘GHC.Enum’ instance Integral Int -- Defined in ‘GHC.Real’
Esse mesmo comando para uma função ou operador permite ver a ordem de precedência, se ele é avaliado da esquerda pra direita ou o contrário:
Prelude> :i (+) class Num a where (+) :: a -> a -> a ... -- Defined in ‘GHC.Num’ infixl 6 +
Nesse caso o operador (+)
é avaliado da esquerda pra direita (o
l
em infixl
e tem precedência 6 (quanto maior esse valor maior a
prioridade). E em classe de tipos ele mostra as funções suportadas
por uma classe:
Prelude> :i Num class Num a where (+) :: a -> a -> a (-) :: a -> a -> a (*) :: a -> a -> a negate :: a -> a abs :: a -> a signum :: a -> a fromInteger :: Integer -> a
Funções polimórficas
Idealmente suas funções devem ter uma assinatura mais abstrata possível, ou seja, se ela pode funcionar para diversos tipos, explicite isso. Inicialmente essa ideia é difícil de ser colocada em prática, então uma dica é criar a função com um tipo concreto e generalizar aos poucos:
-- calcula o seno do produto de uma lista de números produto :: [Double] -> Double produto xs = foldr (*) 1 xs f :: [Double] -> Double f xs = sin (produto xs)
Observando a função sin no ghci
com :i sin
verificamos que ela
faz parte da classe Floating
, com isso nossas funções podem ser
alteradas para:
-- calcula o seno do produto de uma lista de números produto :: Floating [a] -> a produto xs = foldr (*) 1 xs f :: Floating a => [a] -> a f xs = sin (produto xs)
Da mesma forma, olhando a função produto verificamos que a única
operação utilizada é (*)
que é aplicável a qualquer tipo da
classe Num
:
-- calcula o seno do produto de uma lista de números produto :: Num [a] -> a produto xs = foldr (*) 1 xs f :: Floating a => [a] -> a f xs = sin (produto xs)
Para finalizar, a função f
nada mais do que a aplicação de sin
após produto ou:
f = sin . produto
Sobre avaliação preguiçosa e programação dinâmica
Lembrando que a sequência de Fibonacci é definida por \(f(n) = f(n-1) + f(n-2)\).
Enumerando a sequência temos a lista [0, 1, 1, 2, 3, 5, 8, 13, ..]
.
Uma outra forma de construir uma lista é utilizando os elementos
existentes para definir o próximo:
lista :: [Integer] lista = 0 : 1 : f geraLista where f :: [Integer] -> [Integer] f (x:y:xs) = x+y : f (y:xs)
Se você fizer take 5 lista
você obtém os 5 primeiros números da
sequência de Fibonacci! Mas o que está acontecendo durante a
execução do programa?
lista = 0 : 1 : f lista take 5 lista
Ele consegue preencher os dois primeiros elementos requisitados,
pois sabe que são 0 e 1. O restante será gerado pela função f
da
seguinte forma (vamos usar o símbolo _
para representar que não
sabemos essa parte da lista):
take 5 lista = 0 : 1 : _ : _ : _ = 0 : 1 : f (0:1:_) f (0:1:_) = 0+1 : f _
Após esse ponto a lista é atualizada e temos:
take 5 lista = 0 : 1 : _ : _ : _ = 0 : 1 : 1 : f (1:1:_) : _ f (0:1:_) = 0+1 : f (1:1:_) f (1:1:_) = 1+1 : f (1:2:_)
e finalmente:
take 5 lista = 0 : 1 : _ : _ : _ = 0 : 1 : 1 : 2 : f (1:2:_) f (0:1:_) = 0+1 : f (1:1:_) f (1:1:_) = 1+1 : f (1:2:_) f (1:2:_) = 1+2 : f (2:3:_) [0,1,1,2,3]
Sobre foldl
, foldr
e listas infinitas…
O foldr
é definido como:
foldr :: (a -> b -> b) -> b -> [a] -> b foldr f v [] = v foldr f v (x:xs) = f x (foldr f v xs)
Examinando foldr (&&) True [True, True, False, True]
temos:
foldr (&&) True (True:bs) = True && (foldr (&&) True bs)
que usando o curto-circuito do operador (&&)
: (True && x = x)
gera:
foldr (&&) True bs
Prosseguindo temos:
foldr (&&) True (True:False:True:[]) = True && (foldr (&&) True (False:True:[])) = foldr (&&) True (False:True:[]) = False && (foldr (&&) True (True:[])) = False
Ou seja, o foldr
tem a capacidade de interromper o cálculo em
caso de curto-circuito. Se você possui uma lista infinitas, o
foldr
é a melhor opção. Vejamos o que acontece com o foldl
:
foldl :: (a -> b -> a) -> a -> [b] -> a foldl f v [] = v foldl f v (x:xs) = foldl f (f v x) xs
foldl (&&) True (True:True:False:True:[]) = foldl (&&) (True && True) (True:False:True:[]) = foldl (&&) (True) (True:False:True:[]) = foldl (&&) (True && True) (False:True:[]) = foldl (&&) (True) (False:True:[]) = foldl (&&) (True && False) (True:[]) = foldl (&&) (False) (True:[]) = foldl (&&) (False && True) [] = False
Nesse caso tivemos que percorrer toda a lista…
Um desafio para quem quiser praticar o uso de folds. É possível
definir as funções map
e filter
utilizando foldr
e
foldl
.
Atividades e Suporte
Atividades
As atividades do curso estão descritas e deverão ser submetidas pelo GitHub Classroom.
Aula | Prazo | Atividade | Link para submissão |
---|---|---|---|
Aula 01 | 14/03 | Lista 01 | https://classroom.github.com/a/O8r-K6aQ |
Aula 02 | 21/03 | Lista 02 | https://classroom.github.com/a/KB3tsFqW |
- Para submeter uma atividade, basta clicar no link da atividade para criar um novo repositório no Github.
- Não deixe de escolher o seu nome na lista para que possamos relacionar o usuário GitHub à você!
- O repositório é privado e todos os arquivos e alterações adicionados ao repositório poderão ser acessados apenas pelo aluno e pelo professor da disciplina.
Discussão e dúvidas
Durante o curso, utilizem o Discord (https://discord.gg/qDPxdbE) para tirar dúvidas. Nele também serão compartilhados materiais extras.
Metodologia e avaliação
Aulas práticas de programação em laboratório e palestra expositiva sobre o uso de Programação Funcional no ambiente de trabalho.
A avaliação será feita através de atividades de programação a serem entregues em até duas semanas após o término das aulas.
Considerar-se-á aprovado quem:
- obtiver no mínimo 75% de presença.
- entregar 60% das atividades corretamente.
Contato
- Emilio Francesquini - e.francesquini@ufabc.edu.br
- Fabrício Olivetti - folivetti@ufabc.edu.br
Ou via Discord em: https://discord.gg/qDPxdbE