Linguagens e Ambientes de Programação (2016/2017)



Teórica 05 (21/mar/2017)

Tipos produto (tuplos e registos).
Tratamento de exceções.
Funções parciais.



Tipos produto (tuplos e registos)

A maioria das linguagens de programação incluem nos seus sistemas de tipos uma construção específica que permite representar agrupamentos de dados heterogéneos. O nome genérico dessa construção, independente da linguagem particular, é tipo produto.

Numa linguagem com tipos produto é possível definir, por exemplo, um tipo de dados pessoa constituído por um nome (de tipo string), um ano de nascimento (de tipo int), uma morada (de tipo string), etc.

Os tipos produto do Pascal são os registos.
Os tipos produto do C são as estruturas.
Os tipos produto do Java, Smalltalk e C++ são as classes.
No Fortran, os tipos produto apareceram na versão Fortran 90 com o nome de tipos derivados.

Como é em OCaml?

Tipos produto em OCaml

Em OCaml há duas variedades de tipos produto: tipos produto não etiquetados e tipos produto etiquetados.

Tipos produto não etiquetados (tuplos)

Os produtos cartesianos do OCaml são exemplos de tipos produto, neste caso ditos não-etiquetados, e também conhecidos por tuplos. Por exemplo, para representar pessoas, pode usar-se em OCaml o seguinte tipos produto não etiquetado:

Literais: Para exemplificar, eis um valor do tipo anterior: Construção: Para exemplificar vejamos uma função que muda a morada duma pessoa, criando um tuplo novo: Processamento: Como se processam tuplos? De duas formas:

Tipos produto etiquetados (registos)

Mas o OCaml, também suporta tipos produto etiquetados, também conhecidos como registos, os quais requerem definição explícita. Eis um exemplo de tipo produto etiquetado: Literais: Eis um literal deste tipo: Construção: Para exemplificar vejamos uma função que muda a morada duma pessoa, criando um registo novo: Processamento: Como se processam registos? De duas formas:

Tratamento de exceções em OCaml

Durante a execução de um programa, por vezes verificam-se determinadas condições (geralmente anómalas, mas nem sempre) às quais é necessário reagir alterando o fluxo de execução normal. Tais condições chamam-se exceções.

As exceções podem ser geradas:

Captura e tratamento de exceções

Quando uma exceção é gerada, o controlo da execução do programa é transferido para o tratador de exceções (exception handler) mais recentemente cativado. É abortada a avaliação de todas as funções chamadas depois da ativação desse tratador de exceções.

Em OCaml, um tratador de exceções escreve-se usando uma expressão try-with, como se exemplifica de seguida. A expressão exp, no seu interior, diz-se uma expressão protegida.

Como é avaliada uma expressão try-with? Existe um tratador de exceções de sistema que apanha as exceções não tratadas e aborta a execução do programa com uma mensagem de erro apropriada a cada caso. Exemplos:

Definição de novas exceções

A lista de exceções predefinidas na linguagem encontra-se aqui: Index of exceptions.

O programador pode definir novas exceções. A sintaxe da definição duma nova exceção é igual à sintaxe da definição duma variante dum tipos soma.

Exemplos. O primeiro exemplo define uma exceção com argumento; o segundo define uma exceção sem argumento.



Funções parciais

Uma função parcial é uma função que só está definida em parte do seu domínio. (Não confundir com aplicação parcial, que é outra coisa.)

Por exemplo, a função fact é parcial porque só está definida para valores não-negativos:

    let rec fact n = (* pre: n >= 0 *)
        if n = 0 then 1
        else n * fact (n-1)
    ;;

Outro exemplo: A função maxList é parcial porque só está definida para listas não-vazias:

    let rec maxList l = (* pre: l <> [] *)
        match l with
            [x] -> x
          | x::xs -> max x (maxList xs)
    ;;
Quando se escreve uma função parcial, espera-se que essa função seja sempre aplicada a argumentos válidos. Mas os programas podem ter erros e é importante que os programas não disfarcem esses erros - é muito melhor a execução dum programar abortar do que terminar produzindo resultados errados. Por isso, convém garantir que a função não produz qualquer resultado quando aplicada a argumentos inválidos (por outras palavras, temos de obrigar o resultado a ser realmente indefinido).

A melhor forma de impedir uma função de produzir resultado, ao mesmo tempo gerando uma mensagem de erro clara, é gerar uma exceção. Assim:

Mas, repare, mesmo sem lançar exceções explícitas, as versões originais destas duas funções já garantem a não produção de resultados: a primeira aborta com "Stack overflow" e a segunda gera a exceção Match_failure.



#140