Docsity
Docsity

Prepare-se para as provas
Prepare-se para as provas

Estude fácil! Tem muito documento disponível na Docsity


Ganhe pontos para baixar
Ganhe pontos para baixar

Ganhe pontos ajudando outros esrudantes ou compre um plano Premium


Guias e Dicas
Guias e Dicas


Programação Funcional em Haskell: Módulos, Funções e Tipos, Resumos de Programação Funcional

Este documento aborda a construção de módulos em haskell, com foco na importação de outros módulos e na aplicação de regras para a construção de funções. Além disso, são apresentadas funções básicas, como dobro e triplo, e a noção de linguagens com e sem tipos, destacando as vantagens das linguagens com disciplina de tipos rigorosa. Também é introduzido o conceito de tipos enumeráveis em haskell, como o tipo char, e funções para conversão entre inteiros e caracteres. Por fim, é apresentada a sintaxe para definição de funções em haskell.

Tipologia: Resumos

2020

Compartilhado em 02/10/2020

kleyton-d-martin-8
kleyton-d-martin-8 🇧🇷

1 documento

1 / 39

Toggle sidebar

Esta página não é visível na pré-visualização

Não perca as partes importantes!

bg1
Haskell Apontamentos
(Vers˜ao 1.16)
Pedro Quaresma de Almeida1
25 de Fevereiro de 2011
1Departamento de Matem´atica da Universidade de Coimbra.
pf3
pf4
pf5
pf8
pf9
pfa
pfd
pfe
pff
pf12
pf13
pf14
pf15
pf16
pf17
pf18
pf19
pf1a
pf1b
pf1c
pf1d
pf1e
pf1f
pf20
pf21
pf22
pf23
pf24
pf25
pf26
pf27

Pré-visualização parcial do texto

Baixe Programação Funcional em Haskell: Módulos, Funções e Tipos e outras Resumos em PDF para Programação Funcional, somente na Docsity!

Haskell — Apontamentos

(Vers˜ao 1.16)

Pedro Quaresma de Almeida^1

25 de Fevereiro de 2011

(^1) Departamento de Matem´atica da Universidade de Coimbra.

Conte´udo

Cap´ıtulo 1

Introdu¸c˜ao

A linguagem Haskell 1 (Bird, 1998; Hammond et al., 1997; Hudak, 2000; Hudak et al., 1997; Hutton, 2007; Peterson et al., 1997; Thompson, 1996) ´e uma linguagem funcional^2 (pura) com tipos de dados bem definidos (Bird, 1998), ou seja, os modelos para as estruturas de informa¸c˜ao s˜ao baseados na Teoria dos Conjuntos, e as opera¸c˜oes s˜ao descritas atrav´es de fun¸c˜oes entre conjuntos. Os programas mais divulgados para se trabalhar com a linguagem Haskell s˜ao o interpretador Hugs^3 (Jones & Peterson, 1997) e o compilador e interpretador GHC – the Glasgow Haskell Compiler 4 sendo que este ´ultimo providencia tamb´em um interpretador, o GHCI, com um modo de utiliza¸c˜ao idˆentica ao Hugs. A diferen¸ca mais vis´ıvel entre um compilador e um interpretador ´e dada pela inte- rac¸c˜ao com o utilizador. No caso de um compilador ´e necess´ario utilizar um editor de texto para escrever um programa que ´e depois transformado pelo compilador de uma forma autom´atica num execut´avel. No caso do interpretador n˜ao s´o ´e poss´ıvel escrever programas e avali´a-los atrav´es do pr´oprio interpretador (no caso do Hugs e do GHCI s´o ´e poss´ıvel calcular o valor de express˜oes), como no caso de se utilizar um editor externo para a escrita do programa ´e depois necess´ario ter o interpretador em mem´oria de forma a executar o programa. Os interpretadores permitem uma testagem dos programas de uma forma interactiva o que no caso de uma linguagem funcional (fun¸c˜oes independentes umas das outras) ´e bastante interessante. De seguida vai-se centrar a aten¸c˜ao na utiliza¸c˜ao dos interpretadores pois ser´a esta a forma mais usual de desenvolver programas em Haskell.

1.1 Utiliza¸c˜ao dos Interpretadores

Ao invocar-se o interpretador este carrega de imediato o m´odulo Prelude.hs, que constitui o m´odulo b´asico da linguagem Haskell. Toda a interac¸c˜ao com o programa Hugs, ou GHCi (The GHC Team, n.d.) desenvolve- se no ˆambito de um dado m´odulo, tal facto ´e posto em evidˆencia na linha de comando do programa:

Prelude>

(^1) http://www.haskell.org/ (^2) http://www.mat.uc.pt/~pedro/cientificos/Funcional.html (^3) http://www.haskell.org/hugs/ (^4) http://www.haskell.org/ghc/

1.2. UTILIZAC¸ AO DO COMPILADOR˜ GHC Vers˜ao 1.4 4

Se se invocar o interpretador conjuntamente com o nome de um ficheiro, j´a previa- mente constru´ıdo, e que contenha um m´odulo, por exemplo Fact.hs, contendo o m´odulo Fact, a linha de comando do interpretador passar´a a ser:

Fact>

A interac¸c˜ao com o interpretador limitar-se-´a a:

  1. Avalia¸c˜ao de express˜oes. As express˜oes tˆem de estar contidas numa s´o linha. Por exemplo: Prelude> 6 ∗ 4 24 Prelude>
  2. Avalia¸c˜ao de comandos. Entre estes temos: “:q”, para abandonar o interpretador; “:?”, para obter uma lista dos comandos dispon´ıveis; e “:load ” para carregar um novo m´odulo. Um comando interessante numa fase de aprendi- zagem ´e o comando set +t. Este comando set, com o argumento +t, modifica o comportamento do interpretador de molde a que este passe a mostrar a informa¸c˜ao acerca do tipo dos resultados dos c´alculos da express˜oes. Por exemplo Prelude> :set +t Prelude> 6 ∗3. 18.0 :: Double Prelude>

1.2 Utiliza¸c˜ao do Compilador ghc

A utiliza¸c˜ao do compilado ghc (The GHC Team, n.d.) requer que se defina o m´odulo Main e, dentro deste, a fun¸c˜ao zero-´aria main. Na constru¸c˜ao deste modo aplicam- se todas as regras que veremos a frente para a constru¸c˜ao de m´odulos em Haskell, nomeadamente a importa¸c˜ao de outros m´odulos. Vejamos como ´e que poderiamos construir ent˜ao um execut´avel que nos desse o valor da fun¸c˜ao de Ackermann para os valores de entrada 3 e 9. Primeiro definia-se um m´odulo (ficheiro) para a fun¸c˜ao de Ackermann (veremos os pormenores de uma tal defini¸c˜ao maisa frente.

module Ackermann(ackermann) where ackermann :: (Integer,Integer) → Integer ackermann (0,y) = y+ 1 ackermann (x,0) = ackermann(x-1,1) ackermann (x,y) = ackermann(x-1,ackermann(x,y-1)) De seguida constro´ı-se o m´odulo Main que mais n˜ao faz do que importar o m´odulo definido acima, definir um ponto de entrada o qual deve ter como resultado a escrita do valor que se pretende mostrar.

module Main where import Ackermann

main=print (ackermann (3,7)) Finalmente ´e s´o uma quest˜ao de usar o compilador ghc para produzir o execut´avel.

1.3. ESCRITA DE PROGRAMAS EM HASKELL Vers˜ao 1.4 6

dobro x = 2 ∗x ; triplo x = 3 ∗x

1.3.1 A utiliza¸c˜ao do (X)emacs

O desenvolvimento de programas em Haskell ´e muito facilitado caso se use o editor (X)emacs, caso em que se pode usar o haskell-mode^5 , um modo de edi¸c˜ao do (X)emacs especializado para o desenvolvimento de programas em Haskell. De seguida transcreve-se a informa¸c˜ao (C-h m) existente para este modo.

Haskell mode: Major mode for editing Haskell programs. Last adapted for Haskell 1.4. Blank lines separate paragraphs, comments start with ‘-- ’.

M-; will place a comment at an appropriate place on the current line. C-c C-c comments (or with prefix arg, uncomments) each line in the region.

Literate scripts are supported via ‘literate-haskell-mode’. The variable ‘haskell-literate’ indicates the style of the script in the current buffer. See the documentation on this variable for more details.

Modules can hook in via ‘haskell-mode-hook’. The following modules are supported with an ‘autoload’ command:

‘haskell-font-lock’, Graeme E Moss and Tommy Thorn Fontifies standard Haskell keywords, symbols, functions, etc.

‘haskell-decl-scan’, Graeme E Moss Scans top-level declarations, and places them in a menu.

‘haskell-doc’, Hans-Wolfgang Loidl Echoes types of functions or syntax of keywords when the cursor is idle.

‘haskell-indent’, Guy Lapalme Intelligent semi-automatic indentation.

‘haskell-simple-indent’, Graeme E Moss and Heribert Schuetz Simple indentation.

‘haskell-hugs’, Guy Lapalme Interaction with Hugs interpreter.

Module X is activated using the command ‘turn-on-X’. For example, ‘haskell-font-lock’ is activated using ‘turn-on-haskell-font-lock’. For more information on a module, see the help for its ‘turn-on-X’ function. Some modules can be deactivated using ‘turn-off-X’. (Note that ‘haskell-doc’ is irregular in using ‘turn-(on/off)-haskell-doc-mode’.) (^5) http://www.haskell.org/haskell-mode/

1.3. ESCRITA DE PROGRAMAS EM HASKELL Vers˜ao 1.4 7

Use ‘haskell-version’ to find out what version this is.

Invokes ‘haskell-mode-hook’ if not nil.


Font-Lock minor mode (indicator Font): Toggle Font Lock Mode. With arg, turn font-lock mode on if and only if arg is positive.

When Font Lock mode is enabled, text is fontified as you type it:

  • Comments are displayed in ‘font-lock-comment-face’;
  • Strings are displayed in ‘font-lock-string-face’;
  • Documentation strings (in Lisp-like languages) are displayed in ‘font-lock-doc-string-face’;
  • Language keywords ("reserved words") are displayed in ‘font-lock-keyword-face’;
  • Function names in their defining form are displayed in ‘font-lock-function-name-face’;
  • Variable names in their defining form are displayed in ‘font-lock-variable-name-face’;
  • Type names are displayed in ‘font-lock-type-face’;
  • References appearing in help files and the like are displayed in ‘font-lock-reference-face’;
  • Preprocessor declarations are displayed in ‘font-lock-preprocessor-face’;

and

  • Certain other expressions are displayed in other faces according to the value of the variable ‘font-lock-keywords’.

Where modes support different levels of fontification, you can use the variable ‘font-lock-maximum-decoration’ to specify which level you generally prefer. When you turn Font Lock mode on/off the buffer is fontified/defontified, though fontification occurs only if the buffer is less than ‘font-lock-maximum-size’. To fontify a buffer without turning on Font Lock mode, and regardless of buffer size, you can use M-x font-lock-fontify-buffer.

See the variable ‘font-lock-keywords’ for customization.


Column-Number minor mode (indicator): Toggle Column Number mode. With arg, turn Column Number mode on iff arg is positive.

Cap´ıtulo 2

Tipos de Dados

O Haskell ´e uma linguagem de programa¸c˜ao com uma disciplina de tipos rigorosa (“stron- gly typed”), quer isto dizer que toda a entidade num programa em Haskell tem um, e um s´o, tipo, sendo sempre poss´ıvel determinar o tipo de uma determinada entidade. O contra-ponto deste tipo de linguagem s˜ao as linguagens sem tipos, ou melhor lingua- gens em que todas as entidades partilham um mesmo tipo, um exemplo deste tipo de linguagens ´e a linguagem Lisp. Existem argumentos a favor e contra cada uma destas duas aproxima¸c˜oes, uma das grandes vantagens das linguagens com uma disciplina de tipos rigorosa reside na possibilidade de constatar a correc¸c˜ao dos programas atrav´es da verifica¸c˜ao dos tipos, as linguagens deste tipo s˜ao tamb´em as que melhor suportam a modularidade e a abstrac¸c˜ao. Em termos pr´aticos temos que um dado elemento tem sempre um tipo bem definido, de igual modo toda e qualquer fun¸c˜ao tem um dom´ınio e um co-dom´ınio bem definidos. Em Haskell temos ent˜ao um conjunto de tipos de base, pr´e-definidos, e um conjunto de construtores que permitem ao programador criar novos tipos a partir dos tipos de base.

2.1 Tipos de Base

O Haskell possui os seguintes tipos de base:

  • Tipo Unit´ario (≡ 1 );
  • Tipo L´ogico, Bool;
  • Tipo Caracter, Char;
  • Tipos Num´ericos:
    • Int;
    • Integer;
    • Float;
    • Double;

Existe ainda o tipo Vazio, Void, que vai ser importante aquando da defini¸c˜ao de fun¸c˜oes parciais. O ´unico elemento deste tipo, ⊥, ´e interpretado como o valor indefinido,

2.1. TIPOS DE BASE Vers˜ao 1.7 10

permitindo deste modo a defini¸c˜ao de fun¸c˜oes que s˜ao indefinidas para certa classe de argumentos. E de notar que, em consequˆ´ encia do que se acabou de dizer, o elemento ⊥ vai pertencer a todos os tipos. Conven¸c˜ao Sint´actica: Os identifi- cadores dos elementos come¸cam por uma letra min´uscula, os identificado- res de tipos, e os identifi- cadores dos construtores come¸cam por uma letra mai´uscula.

Vejamos cada um destes tipos mais em pormenor:

2.1.1 Tipo Unit´ario

O tipo unit´ario, Unit, ´e uma implementa¸c˜ao do conjunto 1.

Tipo () Valores ()

2.1.2 Bool

Valores L´ogicos e s´ımbolos proposicionais para uma l´ogica proposicional bivalente.

Tipo Bool Valores True; False; otherwise ( = True). Operadores && (conjun¸c˜ao); ‖ (disjun¸c˜ao); not (nega¸c˜ao) Predicados ==, / =, <, <=, >=, >

Os identificadores True, e False correspondem aos dois construtores 0-´arios do con- junto Bool. O identificador otherwise, cujo valor ´e igual a True vai ser ´util aquando da defini¸c˜ao de fun¸c˜oes com ramos.

2.1.3 Char

Caracteres, s´ımbolos do alfabeto “Unicode”.

Tipo Char Valores s´ımbolos da Tabela Unicode entre plicas, por exemplo ’a’ Operadores

A exemplo de outras linguagens o Haskell possui um conjunto de caracteres especiais: ’\t’, espa¸co tabular (tab); ’\n’, mudan¸ca de linha (newline); ’\’ barra inclinada para tr´as, (backslash); ’\’’, plica; ’" ’, aspas.

Tem-se acesso as duas fun¸c˜oes de transferˆencia usuais entre valores do tipo Char e valores do tipo Int, estas fun¸c˜oes designam-se por: ord :: Char -> Int e chr :: Int -> Char. Al´em destas fun¸c˜oes temos tamb´em acessoas fun¸c˜oes de transferˆencia definidas para todo e qualquer tipo enumerado. No caso do tipo Char estas fun¸c˜oes tˆem valores inteiros entre 0 e 2^16 − 1 inclusiv´e:

toEnum (Int → Char), por exemplo (toEnum 97)::Char = ’a’;

fromEnum (Char → Int), por exemplo fromEnum ’a’ = 97.

2.2. TIPOS ESTRUTURADOS Vers˜ao 1.7 12

2.1.5 Tipo Vazio

Tipo Void Valores ⊥

Dado que o Haskell ´e uma linguagem n˜ao-estrita (veremos mais `a frente o significado preciso desta afirma¸c˜ao) o valor ⊥ (indefinido) est´a presente em todos os tipos.

2.2 Tipos Estruturados

O Haskell possui um conjunto de construtores de tipos que possibilitam a constru¸c˜ao de novos tipos a partir dos tipos de base. Temos assim a possibilidade de construir novos tipos a partir do co-produto, do produto, e da exponencia¸c˜ao de conjuntos.

2.2.1 Produtos, (N-uplos)

O tipo correspondente a um dado n-uplo implementa o produto cartesiano dos tipos que comp˜oem o n-uplo.

T = T 1 × T 2 × · · · × Tm

Tipo (T 1 ,...,Tn) Valores (e 1 ,...,ek), com k ≥ 2 Construtores (,...,) Operadores fst, snd, s´o aplic´aveis a pares.

Os operadores fst(x,y) = x, e snd(x,y)=y d˜ao-nos as duas projec¸c˜oes pr´oprias do produto cartesiano.

2.2.2 Co-produto (Enumera¸c˜ao)

O Haskell possu´ı tamb´em tipos definidos por enumera¸c˜ao, podemos por exemplo consi- derar que os tipos simples podem ser definidos desse modo. O tipo co-produto implementa o co-produto de conjuntos, ou dito de outra forma, a uni˜ao disjunta de conjuntos.

T = T 1 + T 2 + · · · + Tm

Tipo T 1 |... |Tn. Com Ti um tipo, ou um construtor un´ario. Valores e, com e um elemento de um dos tipos que definem o co-produto. Construtores os construtores dos diferentes tipos, assim como cons- trutores un´arios definidos caso a caso.

A defini¸c˜ao de tipos por enumera¸c˜ao, e a utiliza¸c˜ao dos contructores pr´oprios dos co-produtos ir´a ser explorada mais `a frente aquando do estudo da defini¸c˜ao de novos tipos em Haskell.

2.2. TIPOS ESTRUTURADOS Vers˜ao 1.7 13

2.2.3 Listas

O conjunto das sequˆencias finitas, eventualmente vazias, de elementos de um dado tipo A, isto ´e, A∗^ = A^0 +A^1 +A^2 +· · ·+An, ´e implementado atrav´es do tipo listas homog´eneas. Para um dado tipo de base A temos:

Lista A = Vazia+A×Lista A

Tipo [A], listas de elementos de um tipo A Valores [] (lista vazia); [e 0 ,...,em] (lista n˜ao vazia) Construtores [] : 1 −→ Lista ∗ 7 −→ [] : : A × Lista A −→ Lista A (a,l) 7 −→ l′^ = a:l [e 0 ,e 1 ,...,em] =. e 0 :(e 1 :(...:(em : [])...)) Operadores ++, head, last, tail, init, nul, length, !!, foldl, foldl1, scanl, scanl1, foldr, foldr1, scanr, scanr1, iterate, repeat, replicate, cycle, take, drop, splitAt, takeWhile, dropWhile, span, break, lines, words, mlines, mwords, reverse, and, or, any, all, elem, notElem, lookup, sum, product, maximun, minimun, concatMap, zip, zip3, zipWith, zipWith3, unzip, unzip

De entre de todos estes operadores vejamos a defini¸c˜ao de last e de init: last([e 0 ,e 1 ,...,em]) = em init([e 0 ,e 1 ,...,em]) = [e 0 ,e 1 ,...,em− 1 ] O operador !! permite usar uma lista como uma tabela uni-dimensional com ´ındices a come¸car no zero. Por exemplo: [e 0 ,e 1 ,...,em] !! k = ek, 0 ≤ k ≤ m O operador ++ d´a-nos a concatena¸c˜ao de duas listas. Por exemplo: [1,2,3]++[4,5,6] = [1,2,3,4,5,6] O Haskell possu´ı duas formas de constru¸c˜ao de listas sem que para tal seja necess´ario escrever a lista com todos os seus elementos. Vejamos de seguida esses dois mecanismos.

Defini¸c˜ao de uma lista atrav´es de uma gama de varia¸c˜ao. Podemos definir uma lista de elemento num´ericos atrav´es dos seus limites, ou ainda atrav´es dos seus limites e do incremento entre valores. Vejamos esses dois casos:

  • [n..m] ´e a lista [n,n+1,n+2,...,m]. Se o valor de n exceder o de m, ent˜ao a lista ´e vazia. Por exemplo:

[2..5] = [2,3,4,5]

[3.1..7.0] = [3.1,4.1,5.1,6.1]

[7..3] = []

  • [n,p..m] ´e a lista de todos os valores desde n at´e m, em passos de p-n. Por exemplo:

2.3. EXPRESS OES˜ Vers˜ao 1.7 15

2.2.5 Fun¸c˜oes

As fun¸c˜oes v˜ao estar subjacentes a todas as manipula¸c˜oes da linguagem e implementam a exponencia¸c˜ao de conjuntos. Ou seja o conjunto de todas as fun¸c˜oes de A para B ´e dado por BA.

Tipo T 1 -> T 2 Valores id, const,... Construtores \ x -> exp(x) Operadores ·, curry, uncurry,...

O operador “·” d´a-nos a composi¸c˜ao de fun¸c˜oes. Os operadores curry e uncurry verificam, curry f x y = f (x,y) e uncurry f p = f (fst p) (snd p). A abstrac¸c˜ao lambda permite-nos definir uma fun¸c˜ao sem lhe dar um nome, o que n˜ao nos impede a sua aplica¸c˜ao a um dado argumento. Por exemplo (\x -> x^2) 4 daria o resultado 16. Embora a defini¸c˜ao de fun¸c˜oes seja habitualmente feita de forma diferente (mais `a frente veremos como), pode-se atribuir um nome a uma fun¸c˜ao definida atrav´es da abstrac¸c˜ao lambda, por exemplo quadrado = (\x -> x^2)

2.3 Express˜oes

A linguagem Haskell ´e uma linguagem com uma disciplina de tipos rigorosa, quer isto dizer que qualquer express˜ao em Haskell tem um e um s´o tipo bem definido. A este prop´osito ´e de referir uma s´erie de comandos que se revelam interessantes; um deles ´e o comando :type (ou :t). Este comando d´a-nos, para uma determinada express˜ao, a informa¸c˜ao acerca do tipo da express˜ao. Por exemplo:

Prelude> :t ’a’ ’a’ :: Char

A sintaxe :: tem o significado ´obvio de . Outro comando ´util, ´e o comando :set +t, ou melhor a aplica¸c˜ao do comando :set ao valor +t. Este comando modifica o comportamento do interpretador de modo a que este passa a explicitar (+) a informa¸c˜ao do tipo (t) da express˜ao que ´e sujeita a avalia¸c˜ao. A situa¸c˜ao inversa, valor por omiss˜ao, ´e conseguida atrav´es do comando :set -t. Por exemplo:

Prelude> 7+ 10 Prelude> :set +t Prelude> 7+ 10 :: Int

A avalia¸c˜ao de uma express˜ao ´e feita por redu¸c˜ao da express˜ao `a sua forma normal. Podemos modificar o comportamento do interpretador de forma a que este explicite da- dos estat´ısticos (:set +s) acerca das redu¸c˜oes necess´arias para a avalia¸c˜ao da express˜ao. Por exemplo

2.3. EXPRESS OES˜ Vers˜ao 1.7 16

Prelude> :set +s Prelude> 7+ 10 :: Int (10 reductions, 10 cells)

Posto isto a escrita de express˜oes e a sua avalia¸c˜ao segue as regras usuais. Por exemplo:

Prelude> "Haskell" "Haskell" :: String Prelude> 5+3* 35 :: Int Prelude> head [3,56,79] 3 :: Int Prelude> fst (’a’,1) ’a’ :: Char Prelude> 4*1. 4.0 :: Double

2.3.1 Precedˆencia e Associatividade

A precedˆencia dos v´arios operadores assim como o modo como se procede a sua asso- cia¸c˜ao ´e dada na seguinte tabela.

Precedˆencia associa a esquerda associaa direita n˜ao associativo 9 !!. — 8 — ^,^^,** — 7 *,/,‘div‘,‘mod‘, ‘rem‘,‘quot‘

5 — :,++ \

‘elem‘,‘notElem‘ 3 — && — 2 — || — 1 >>,>>= — — 0 — $,‘seq‘ —

3.1. DEFINIC¸ OES SIMPLES˜ Vers˜ao 1.8 18

3.1 Defini¸c˜oes Simples

A defini¸c˜ao de uma fun¸c˜ao passa pela escrita de uma “equa¸c˜ao” que determine o seu comportamento. A sintaxe ´e a seguinte:

< nome da fun¸c˜ao > < argumento > = < express˜ao >

por exemplo

cubo x = x∗x∗x

A defini¸c˜ao de fun¸c˜oes deve ser entendida como uma regra de re-escrita da esquerda para a direita, a qual est´a implicitamente quantificada universalmente nas suas vari´aveis. Ou seja a leitura da defini¸c˜ao de cubo ´e a seguinte:

∀x∈T cubo x → x × x × x

com T um dos tipos da linguagem Haskell, e → o operador de redu¸c˜ao. A avalia¸c˜ao de uma express˜ao que envolva a fun¸c˜ao cubo passa pela redu¸c˜ao da express˜ao `a sua forma normal, utilizando, entre outras, a regra de redu¸c˜ao escrita acima. Por exemplo:

Main> cubo 3 27

teve como passos da redu¸c˜ao cubo 3 → (3 × 3) × 3 → 9 × 3 → 27. Qual ´e o tipo da fun¸c˜ao cubo

Main> :t cubo cubo :: Num a ⇒ a → a

A resposta aparentemente confusa tem a seguinte leitura, cubo ´e uma fun¸c˜ao de dom´ınio A e co-dom´ınio A (a -> a) em que A ´e um qualquer tipo num´erico (Num a =>). Ou seja cubo ´e uma fun¸c˜ao polim´orfica cujo tipo (a->a) pertence `a classe dos tipos num´ericos (Num a). A defini¸c˜ao de uma instˆancia concreta para a fun¸c˜ao cubo s´o acontece no momento da avalia¸c˜ao. Um outro ponto que importa salientar ´e que as fun¸c˜oes em Haskell tˆem um, e um s´o, argumento. Como proceder ent˜ao para definir uma fun¸c˜ao a aplicar a dois argumentos? Existem duas solu¸c˜oes poss´ıveis: Em primeiro podemos agrupar os argumentos num n-uplo. Por exemplo:

soma1 :: (Int,Int) → Int soma1 (x,y) = x + y

A outra solu¸c˜ao ´e dada pela defini¸c˜ao de uma fun¸c˜ao curry.

soma2 :: Int → (Int → Int) soma2 x y = x + y

Estas duas defini¸c˜oes s˜ao equivalentes dado serem a express˜ao da propriedade uni- versal das fun¸c˜oes. E de relembrar que a aplica¸´ c˜ao de fun¸c˜oes associa `a esquerda, ou seja soma x y deve ler-se como (soma x) y. A vantagem da utiliza¸c˜ao de fun¸c˜oes curry em detrimento das fun¸c˜oes n˜ao-curry ´e dupla: por um lado utilizam-se argumentos n˜ao estruturados tornando deste modo a escrita mais simples; por outro lado ao aplicar-se uma fun¸c˜ao curry a um dos seus argu- mentos obtˆem-se uma outra fun¸c˜ao que pode por sua vez ser um objecto interessante.

3.2. DEFINIC¸ OES CONDICIONAIS˜ Vers˜ao 1.8 19

3.2 Defini¸c˜oes Condicionais

A defini¸c˜ao de uma fun¸c˜ao atrav´es de uma ´unica equa¸c˜ao n˜ao nos permite definir fun¸c˜oes por ramos. Temos duas possibilidades para o fazer: atrav´es de express˜oes condicionais.

menor1 :: (Int,Int) → Int menor1 (x,y) = if x ≤ y then x else y

ou ent˜ao atrav´es de equa¸c˜oes com “guardas”.

menor2 :: (Int,Int) → Int menor2 (x,y) | x ≤ y = x | x > y = y As duas defini¸c˜oes s˜ao equivalentes, no segundo caso as “guardas” s˜ao avaliados e a primeira a tomar o valor l´ogico de verdade determina o valor da fun¸c˜ao. A segundo defini¸c˜ao podia ser re-escrita do seguinte modo:

menor3 :: (Int,Int) → Int menor3 (x,y) | x > y = y | otherwise = x

dado que o valor l´ogico de otherwise ´e Verdade, ent˜ao este padr˜ao determina o valor da fun¸c˜ao para todos os casos em que os outros ramos que o precedem n˜ao tenham o valor l´ogico de Verdade.

3.3 Defini¸c˜oes Recursivas

A defini¸c˜ao de fun¸c˜oes recursivas em Haskell n˜ao s´o ´e poss´ıvel, como a sua sintaxe ´e muito simples, e segue de perto a sintaxe matem´atica. Por exemplo a defini¸c˜ao da fun¸c˜ao factorial ´e dada por:

fact :: Integer → Integer fact n | n == 0 = 1 | n > 0 = n∗fact(n-1) Na defini¸c˜ao desta fun¸c˜ao levanta-se uma quest˜ao: e no caso em n < 0? Para os valores de n < 0 a fun¸c˜ao factorial n˜ao est´a definida, ela ´e uma fun¸c˜ao parcial no conjunto dos inteiros, torna-se ent˜ao necess´ario saber lidar com fun¸c˜oes parciais. Em Haskell a forma de lidar com fun¸c˜oes parciais passa por especificar situa¸c˜oes de erro.

fact1 :: Integer → Integer fact1 n | n < 0 = error "A fun¸c~ao fact n~ao est´a definida para n<0" | n == 0 = 1 | n > 0 = fact1(n-1)∗n No entanto, e dado o tipo da fun¸c˜ao fact1, a fun¸c˜ao error tem de ser String -> Integer. O tipo da fun¸c˜ao error ´e na verdade String -> A, com A um tipo Haskell, ou seja error ´e uma fun¸c˜ao polim´orfica cujo tipo do co-dom´ınio depende da situa¸c˜ao em que ´e aplicada. O mesmo se passa com a fun¸c˜ao ‘*’, o seu tipo ´e A -> A -> A, sendo depois ins- tanciada para uma dada fun¸c˜ao concreta no momento da aplica¸c˜ao.