Âmbito das definições O âmbito de um nome introduzido por val é o contexto que se segue textualmente à sua definição. Ou seja o âmbito do nome apenas começa depois de terminada a definição. O âmbito de um nome introduzido por fun é mais lato: inclui a expressão que define a função, ou seja inclui o lado direito da definição. Isto acontece para que se possam definir funções recursivas. O âmbito de um parâmetro (definição por fun) é o da própria definição. A redefinição de nomes é possível: um nome é redefinido quando surge uma nova definição para ele num contexto em que ele já era conhecido. Universidade da Madeira Departamento de Matemática e Engenharias Elsa Carvalho 28 Programação em Lógica e Funcional (2000/01) (Actualizado em 2005/06) Âmbito das definições Para definir funções mutuamente recursivas (que estão definidas em termos uma da outra) utiliza-se a palavra chave and que introduz a definição simultânea de dois ou mais nomes. fun f x = ... g x ... and g y = ... f y ... Para definições val usa-se a palavra chave and como outra forma de definir tuplos. val V1 = E1 and V2 = E2 Universidade da Madeira Departamento de Matemática e Engenharias Elsa Carvalho é equivalente a val(V1, V2) = (E1, E2) 29 Programação em Lógica e Funcional (2000/01) (Actualizado em 2005/06) Definições locais Podemos fazer definições locais a outras definições. As primeiras serão apenas conhecidas pelas segundas. Uma das razões para a introdução de funções auxiliares locais é o facto de sabermos que elas apenas serão utilizadas naquela outra função. Desta forma podemos definir funções locais não robustas (que não estejam definidas para todos os valores do seu domínio) pois sabemos que quando as utilizarmos apenas utilizaremos valores do seu conjunto de partida. Universidade da Madeira Departamento de Matemática e Engenharias Elsa Carvalho 30 Programação em Lógica e Funcional (2000/01) (Actualizado em 2005/06) Definições locais local in fun potenciaLocal(b, e) = if e=0 then 1 else b*potenciaLocal(b,e-1) fun potencia(b, e) = if e<0 then error “potencia indefinida para expoentes negativos” else potenciaLocal(b, e) end fun quadrado n = potencia(n, 2) Esta é uma forma de definir potencia tendo como definição local potenciaLocal, cujo âmbito se restringe à definição da 1ª função. Universidade da Madeira Departamento de Matemática e Engenharias Elsa Carvalho 31 Programação em Lógica e Funcional (2000/01) (Actualizado em 2005/06) Definições locais Qualquer definição que se siga (após a palavra chave end), não ‘vê’ a função local. A construção local é mais genericamente dada por local DL1 DL2 ... DLn in D1 D2 ... Dm end em que DL1, DL2, ..., DLn, D1, D2, ..., Dm são definições (val ou fun). DL1, DL2, ..., DLn são definições locais às definições D1, D2, ..., Dm, só visíveis por estas últimas. Em relação a DL1, ..., DLn entre si a regra normal para âmbitos aplica-se: DL2 ‘vê’ DL1, etc.. Universidade da Madeira Departamento de Matemática e Engenharias Elsa Carvalho 32 Programação em Lógica e Funcional (2000/01) (Actualizado em 2005/06) Definições locais As definições efectivamente introduzidas e que são vistas por outras introduzidas após a palavra end são D1, ..., Dm. É possível encadear definições locais, colocando, por exemplo, no lugar de DLi ou de Dj uma definição local. Podem-se também fazer definições locais a uma expressão, de forma a simplificar expressões mais complexas. Estas terão como âmbito a expressão a definir. Estas definições locais são feitas dando nomes a sub-expressões e introduzindo funções temporárias nessas sub-expressões. Esta característica torna-se importante quando utilizamos várias vezes a mesma sub-expressão dentro de uma expressão. Universidade da Madeira Departamento de Matemática e Engenharias Elsa Carvalho 33 Programação em Lógica e Funcional (2000/01) (Actualizado em 2005/06) Definições locais Assim, podemos utilizar uma tal expressão numa definição de digit fun digit d = let val n = ord d in n>=0 codeof0 andalso n<=codeof9 end Desta forma a expressão ord é avaliada uma só vez enquanto que o valor n é usado por duas vezes na expressão que define a função. Se não utilizássemos esta definição local, a função digit seria definida como fun digit d = ord d>=0 codeof0 andalso ord d<=codeof9 em que temos a expressão ord d avaliada duas vezes. Universidade da Madeira Departamento de Matemática e Engenharias Elsa Carvalho 34 Programação em Lógica e Funcional (2000/01) (Actualizado em 2005/06) Definições locais De uma forma geral let DL1 DL2 ... DLn in E end é uma expressão com o mesmo valor de E, mas que usa as definições tornadas locais DL1, DL2, ..., DLn. Universidade da Madeira Departamento de Matemática e Engenharias Elsa Carvalho 35 Programação em Lógica e Funcional (2000/01) (Actualizado em 2005/06) Exercícios Diga qual o âmbito de V1 e V2 em cada uma das expressões let val V1 = E1 in let val V2 = E2 in E end end O âmbito de V1 é E2 e E O âmbito de V2 é E let val V1 = let val V2 = E2 in E1 end in E end O âmbito de V1 é E O âmbito de V2 é E1 Universidade da Madeira Departamento de Matemática e Engenharias Elsa Carvalho 36 Programação em Lógica e Funcional (2000/01) (Actualizado em 2005/06) Polimorfismo Funções polimórficas são aquelas que podem ser aplicadas a valores de diferentes tipos. Por exemplo, a função identidade e as projecções: fun id x = x id 5 = 5 id “ola” = “ola” id (1, true, size “ola”) = id (1, true, 3) = (1, true, 3) primeiro (1, 5) = 1 segundo (false, 2) = 2 Universidade da Madeira Departamento de Matemática e Engenharias Elsa Carvalho 37 Programação em Lógica e Funcional (2000/01) (Actualizado em 2005/06) Polimorfismo Não é necessário fazer várias versões de uma função se a queremos aplicar a valores de diferentes tipos. Tal coisa não acontece em linguagens como o Pascal. De forma a ser possível designar o tipo de funções como estas, irá agora ser considerada uma extensão ao sistema de tipos. Passaremos a introduzir variáveis como , e para designar tipos genéricos (polimórficos) nos quais a igualdade pode estar ou não definida e =, = e = para designar tipos genéricos nos quais está definida a igualdade. Universidade da Madeira Departamento de Matemática e Engenharias Elsa Carvalho 38 Programação em Lógica e Funcional (2000/01) (Actualizado em 2005/06) Polimorfismo Assim temos id: primeiro: ( x ) segundo: ( x ) isto significa que id é uma função cujo parâmetro é de um qualquer tipo e cujo resultado é do mesmo tipo que o parâmetro. O parâmetro da função segundo é um par com componentes de quaisquer tipos e respectivamente e o resultado é um valor do tipo . Universidade da Madeira Departamento de Matemática e Engenharias Elsa Carvalho 39 Programação em Lógica e Funcional (2000/01) (Actualizado em 2005/06) Polimorfismo Mais alguns exemplos de funções polimórficas são a função constantemente igual a 5 e a igualdade: fun const5 x = 5 const5: int = : (= x =) bool Relativamente à igualdade tal como é implementada em ML surge uma pequena restrição. Não é possível comparar valores de um qualquer tipo. Há domínios onde não é possível / eficiente e/ou não faz sentido fazer comparações: não se comparam funções, por exemplo. Universidade da Madeira Departamento de Matemática e Engenharias Elsa Carvalho 40 Programação em Lógica e Funcional (2000/01) (Actualizado em 2005/06) Polimorfismo Vale a pena neste ponto fazer referência ao caso da sobrecarga de identificadores. Quando um nome é utilizado para designar duas ou mais funções diferentes, espera-se que o contexto forneça a informação adicional para que o ‘type checker’ consiga inferir qual das funções está em causa. Assim, por exemplo, as expressões como 5 + 7 e 5.5 + 7.7 envolvem o nome ‘+’ para designar duas funções totalmente distintas: a soma de inteiros e a soma de reais. Note-se porém que esta situação não é, de forma alguma, uma situação de polimorfismo. Universidade da Madeira Departamento de Matemática e Engenharias Elsa Carvalho 41 Programação em Lógica e Funcional (2000/01) (Actualizado em 2005/06) Polimorfismo Falamos em polimorfismo quando está em causa uma única função que pode ser invocada com argumentos de mais do que um tipo. Falamos em sobrecarga de identificadores quando estão em causa funções diferentes que podem ser designadas por um mesmo nome. Ao longo das aulas assumimos um sistema de tipos que não dá lugar a casos de sobrecarga de identificadores. Por exemplo não trabalhamos com reais e portanto o problema do ‘+’ não se põe. Universidade da Madeira Departamento de Matemática e Engenharias Elsa Carvalho 42 Programação em Lógica e Funcional (2000/01) (Actualizado em 2005/06) Polimorfismo Convém no entanto notar que, se por exemplo os reais fizessem parte do sistema seríamos obrigados a qualificar expressões como x + y pois esta não fornece contexto suficiente para se saber de que soma se trata. Seria necessário qualificá-la: x + y:int (é a soma de inteiros já que y fica obrigado a ser do tipo int). Universidade da Madeira Departamento de Matemática e Engenharias Elsa Carvalho 43 Programação em Lógica e Funcional (2000/01) (Actualizado em 2005/06) Funções de Ordem Superior A programação funcional fornece um nível de abstracção mais elevado, no qual é possível englobar os conceitos de função e de valor num único conceito. A esse nível, as funções podem ser vistas elas próprias como valores. Para que uma função possa ser usada como qualquer outro valor é necessário que se possa: aplicar funções a funções, i.e., permitir parâmetros do tipo função. Isto não é novidade pois devido ao operador de tipos passou a fazer sentido falar em tipos como (int int) bool, que é o tipo de todas as funções com domínio (int int) e codomínio bool; o domínio é pois um conjunto de funções. Universidade da Madeira Departamento de Matemática e Engenharias Elsa Carvalho 44 Programação em Lógica e Funcional (2000/01) (Actualizado em 2005/06) Funções de Ordem Superior dar funções como resultado de funções, exº: id aplicado a dobro dá como resultado dobro que é uma função. formar tuplos tendo funções como componentes. De facto o operador de tipos x permite escrever expressões como (id, id dobro) cujo tipo é ( ) x (int int). Uma função é de ordem superior quando aceita argumentos funcionais e/ou quando produz resultados funcionais. Assim, id, primeiro e segundo são funções de ordem superior. Universidade da Madeira Departamento de Matemática e Engenharias Elsa Carvalho 45 Programação em Lógica e Funcional (2000/01) (Actualizado em 2005/06) Funções de Ordem Superior O tipo de uma função de ordem superior, envolve, tipicamente, o construtor de tipos (). Por exemplo, (int int) (int x bool) é o tipo de uma função de ordem superior que espera receber como argumento uma função do tipo int int e retornar o tuplo (int x bool). Por outro lado bool (int int) é o tipo de uma função de ordem superior que, quando aplicada a um valor do tipo booleano, retorna uma função do tipo (int int). Universidade da Madeira Departamento de Matemática e Engenharias Elsa Carvalho 46 Programação em Lógica e Funcional (2000/01) (Actualizado em 2005/06) Funções de Ordem Superior Outro caso é a composição de funções. Em ML é utilizado o operador infixo ° para cômpor funções. Para qualquer argumento x temos (f ° g) (x) = f(g x) Assim, a composição tem como parâmetro um par de funções e como resultado a função composta: °: ( ) x ( ) ( ) Pode-se agora cômpor funções evitando o parâmetro, e consequentemente, utilizando val em vez de fun val seisvezes = dobro ° triplo Universidade da Madeira Departamento de Matemática e Engenharias Elsa Carvalho 47 Programação em Lógica e Funcional (2000/01) (Actualizado em 2005/06) Funções de Ordem Superior O tipo de ° é algo complicado, mas também interessante. Primeiro podemos verificar que a composição combina um par de funções e retorna ainda uma função. Assim o seu tipo há-de ser algo do género: ((T1 T2) x (T3 T4)) (T5 T6) No entanto nem todas as funções podem ser compostas e o tipo do resultado não é independente do tipo dos argumentos. Para que f ° g faça sentido é necessário que o tipo do resultado de g coincida com o do argumento de f. Assim T4 = T1. Além disso o tipo do resultado será o mesmo que o do resultado de f e o tipo do argumento da composição será o mesmo que o do argumento de g. Assim T6 = T2 e T5 = T3. Desta forma chegamos ao resultado apresentado acima. Universidade da Madeira Departamento de Matemática e Engenharias Elsa Carvalho 48 Programação em Lógica e Funcional (2000/01) (Actualizado em 2005/06)