haskell_logo.png 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.

caelum.png

O curso foi desenvolvido pelos Profs. Fabrício Olivetti e Emilio Francesquini da Universidade Federal do ABC.

ufabc.png

Datas e local

caelum.png

A Caelum gentilmente nos cedeu o espaço para o curso que ocorrerrá:

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 PDF  
07/03 PDF 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

Ou via Discord em: https://discord.gg/qDPxdbE

Author: Emilio Francesquini e Fabrício Olivetti

Created: 2021-06-11 sex 16:39