Conceitos Básicos - Parte 1


Playlists

1 Introdução ao Haskell

  • Surgiu em 1990 com o objetivo de ser a primeira linguagem puramente funcional.

  • Por muito tempo considerada uma linguagem acadêmica.

  • Atualmente é utilizada em diversas empresas (totalmente ou em parte de projetos).

Haskell tem como características:

  • Códigos concisos e declarativos: o programador declara o que ele quer ao invés de escrever um passo-a-passo. Programas em Haskell chegam a ser dezenas de vezes menores que em outras linguagens.
take 100 [x | x <- nat, primo x]
  • Imutabilidade: não existe um conceito de variável, apenas nomes e declarações. Uma vez que um nome é declarado com um valor, ele não pode sofrer alterações. Como consequência não precisamos nos preocupar se uma variável foi passada como referência ou não.
x = 1.0
x = 2.0

ERRO!

  • Funções Recursivas: com a imutabilidade, o conceito de laços de repetição também não existe em linguagens funcionais. (Por quê?) Eles são implementados através de funções recursivas.
int x = 1;
for (int i = 1; i <= 10; i++) {
    x = x * 2;
}
printf("%d\n", x);
f 0 = 1
f n = 2 * f (n-1) -- Note que f(x) é o mesmo que f x

print (f 10)
  • Funções de alta ordem: funções podem receber funções como parâmetros. Isso permite definir funções genéricas, compor duas ou mais funções e definir linguagens de domínio específicos (ex.: parsing).
print (aplique dobro [1,2,3,4])
> [2,4,6,8]
  • Tipos polimórficos: permite definir funções genéricas que funcionam para classes de tipos. Por exemplo, a função fst retorna o primeiro elemento de uma tupla, os tipos dos elementos não importam.
fst :: (a,b) -> a
fst (x,y) = x
  • Avaliação preguiçosa: ao aplicar uma função, o resultado será computado apenas quando requisitado. Isso permite evitar computações desnecessárias, estimula uma programação modular e permite estruturas de dados infinitas.
listaInf = [1..] -- 1, 2, 3, ...
print (take 10 listaInf)

1.1 Para saber mais

  • Livros
    • [GH] 1,2
    • [SGS] 1
    • [ML] 2

1.2 Olá Mundo

module Main where   -- indica que é o módulo principal

main :: IO ()
main = do                  -- início da função principal
  putStrLn "hello world"   -- imprime hello world

1.3 Funções

  • Em Haskell, a aplicação de função é definida como:
    • o nome da função…
    • … seguido dos parâmetros separados por espaço
    • A aplicação de funções tem a maior precedência
f a b       -- f(a,b)
f a b + c*d -- f(a,b) + c*d

A tabela abaixo contém alguns contrastes entre a notação matemática e Haskell:

Matemática Haskell
\(f(x)\) f x
\(f(x,y)\) f x y
\(f(g(x))\) f (g x)
\(f(x, g(y))\) f x (g y)
\(f(x)g(y)\) f x * g y

2 Convenções

2.1 Requisitos dos nomes

  • Os nomes das funções e seus argumentos devem começar com uma letra minúscula e seguida por \(0\) ou mais letras, maiúsculas ou minúsculas, dígitos, underscore, e aspas simples:
  • Apesar de haver suporte para caracteres Unicode, o seu uso é controverso pela dificuldade envolvida na sua digitação.
funcao, ordenaLista, soma1, x'

2.2 Nomes reservados

  • Os únicos nomes que não podem ser utilizados são:
case, class, data, default, deriving do, else,
foreign, if, import, in, infix, infixl, infixr,
instance, let module, newtype, of, then, type,
where

2.3 Convenção para listas

  • As listas são nomeadas acrescentando o caractere ’s’ ao nome do que ela representa.

  • Uma lista de números n é nomeada ns, uma lista de variáveis x se torna xs. Uma lista de listas de caracteres tem o nome css.

2.4 Regra de layout

  • O layout dos códigos em Haskell é similar ao do Python, em que os blocos lógicos são definidos pela indentação.
f x = a*x + b
     where
       a = 1
       b = 3
z = f 2 + 3
  • A palavra-chave where faz parte da definição de f, da mesma forma, as definições de a e b fazem parte da cláusula where. A definição de z não faz parte de f.

2.5 Tabs vs Espaço

  • A definição de tabulação varia de editor para editor.
  • Ainda que seja o mesmo editor, a tabulação varia de usuário para usuário.
  • Como o espaço é importante no Haskell, usem espaços em vez de tab.
  • Use Emacs. (embora Vim seja superior 😛)
Figure 1: https://www.youtube.com/watch?v=SsoOG6ZeyUI

Figure 1: https://www.youtube.com/watch?v=SsoOG6ZeyUI

2.6 Comentários

Comentários em uma linha são demarcados pela sequência --, comentários em múltiplas linhas são demarcados por {- e -}:

-- função que dobra o valor de x
dobra x = x + x

{-
dobra recebe uma variável numérica
e retorna seu valor em dobro.
-}

2.7 Para saber mais

3 Tipos de dados

  • Um tipo é uma coleção de valores relacionados entre si.

  • Exemplos:

    • Int compreende os valores de números inteiros.
    • Bool contém apenas os valores True e False, representando valores lógicos.

Em Haskell, os tipos são definidos pela notação

v :: T

Significando que v define um valor do tipo T.

False :: Bool
True :: Bool
10 :: Int

3.1 Tipos Básicos

  • O compilador GHC já vem com suporte nativo a diversos tipos básicos.
  • Durante o curso veremos como definir e criar os nossos próprios tipos.

Alguns destes tipos são:

  • Bool: contém os valores True e False. Expressões booleanas podem ser executadas com os operadores && (e), || (ou) e not.

  • Char: contém todos os caracteres no sistema Unicode. Podemos representar a letra 'a', o número '5', a seta tripla '⇶' e o homem de terno levitando1, 2 '🕴'.

  • String: sequências de caracteres delimitados por aspas duplas: "Olá Mundo".

  • Int: inteiros com precisão fixa em 64 bits. Representa os valores numéricos de \(-2^{63}\) até \(2^{63}-1\).

  • Integer: inteiros de precisão arbitrária. Representa valores inteiros de qualquer precisão, a memória é o limite. Mais lento do que operações com Int.

  • Float: valores em ponto-flutuante de precisão simples. Permite representar números com um total de \(7\) dígitos, em média.

  • Double: valores em ponto-flutuante de precisão dupla. Permite representar números com quase \(16\) dígitos, em média.

Note que ao escrever:

x = 3

O tipo de x pode ser Int, Integer, Float ou Double.

Pergunta: Qual tipo devemos atribuir a x?

3.2 Listas

Listas são sequências de elementos do mesmo tipo agrupados por colchetes e separados por vírgula:

[1,2,3,4]
[12]

Uma lista de tipo T tem tipo [T]:

[1,2,3,4]           :: [Int]
[False, True, True] :: [Bool]
['o', 'l', 'a']     :: [Char]

Também podemos ter listas de listas:

[ [1,2,3], [4,5] ]                        :: [[Int]]
[ [ 'o','l','a'], ['m','u','n','d','o'] ] :: [[Char]]

\(\cdot\) O tipo da lista não especifica seu tamanho
\(\cdot\) Não existem limitações quanto ao tipo da lista
\(\cdot\) Não existem limitações quanto ao tamanho da lista

3.3 Tuplas

Tuplas são sequências finitas de componentes, contendo zero ou mais tipos diferentes:

(True, False) :: (Bool, Bool)
(1.0, "Sim", False) :: (Double, String, Bool)
  • O tipo da tupla é definido como (T1, T2,...,Tn).

\(\cdot\) O tipo da tupla especifica seu tamanho
\(\cdot\) Não existem limitações dos tipos associados a tupla (podemos ter tuplas de tuplas)
\(\cdot\) Tuplas devem ter um tamanho finito
\(\cdot\) Tuplas de aridade 1 não são permitidas para manter compatibilidade do uso de parênteses como ordem de avaliação

3.4 Funções

Funções são mapas de argumentos de um tipo para resultados em outro tipo. O tipo de uma função é escrito como T1 -> T2, ou seja, o mapa do tipo T1 para o tipo T2:

not  :: Bool -> Bool
even :: Int -> Bool

Para escrever uma função com múltiplos argumentos, basta separar os argumentos pela ->, sendo o último o tipo de retorno:

soma :: Int -> Int -> Int
soma x y = x + y

mult :: Int -> Int -> Int -> Int
mult x y z = x * y * z

3.5 Para saber mais

4 Disclaimer

Estes slides foram preparados para os cursos de Paradigmas de Programação e Desenvolvimento Orientado a Tipos na UFABC.

Este material pode ser usado livremente desde que sejam mantidos, além deste aviso, os créditos aos autores e instituições.