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



Teórica 06 (22/mar/2018)

Efeitos laterais em OCaml. Motivação do tipo "unit" e do operador ";".
Input/Output em OCaml. Utilização do método indutivo na programação de funções sobre ficheiros.
Introdução aos módulos em OCaml.



Efeitos laterais

O OCaml usa um modelo de input/output imperativo, no qual as operações de input/output são tratadas como efeitos laterais.

O que é um efeito lateral? Um efeito lateral é qualquer atividade que uma função desenvolva para além de calcular um resultado a partir dos argumentos. Por exemplo:

Como sabemos, no paradigma funcional é suposto uma função limitar-se a calcular um resultado a partir dos seus argumentos. O facto é que a linguagem OCaml ultrapassa os limites estritos do paradigma funcional, já que admite efeitos laterais nas funções.

Operador de sequenciação

Para sequenciar os efeitos laterais, o OCaml dispõe dum operador de sequenciação que se escreve ";" (como em Pascal!). Assim, por exemplo, para escrever no ecrã a string "ola", e logo a seguir a string "ole" usamos a seguinte função:

    let hello () =
        print_string "ola" ; print_string "ole"
    ;;
Tecnicamente, ";" é o nome duma função com o seguinte tipo:
    ";": unit -> 'b -> 'b
e a seguinte definição usada internamente pelo OCaml:
    ";" x y = y ;;

Tipo unit e valor ()

Havendo efeitos laterias em OCaml, faria sentido definir funções sem argumentos ou sem resultados. Mas o OCaml não o permite, pois obriga todas as funções a ter argumentos e um resultado. O tipo unit foi inventado para ser usado nessas situações.

O tipo unit é um tipo básico com apenas um valor, que se escreve (). Para comparação, note que o tipo bool é um tipo com apenas dois valores, que se escrevem true e false. Por seu lado, o tipo void do C é um tipo sem valores e por isso não resolveria o nosso problema em OCaml.

Exemplos de utilização de unit e ().

    print_string: string -> unit
    read_int: unit -> int
    print_newline: unit -> unit

    let x = read_int () in
        print_int (x+1)
Exemplo de função para escrever uma lista de inteiros no ecrã:

Input-Output em OCaml

Canais

As operações sobre ficheiros são efetuadas através de canais. Os canais são valores dos tipos predefinidos in_channel e out_channel.

Os "canais" do OCaml correspondem às "streams" do Java.

Em OCaml existem três canais predefinidos que são automaticamente abertos quando o programa começa a correr:

Primitivas de input/output Esta lista de primitivas não é exaustiva. Há mais primitivas listadas no manual de referência (ver Basic Operations).

Exemplo de função sobre ficheiros programada usando o método indutivo

Cópia de ficheiros: Esquema geral de utilização do método indutivo no tratamento de ficheiros linha a linha:

Introdução aos módulos em OCaml

Nenhuma linguagem moderna dispensa um sistema de módulos. Um sistema de módulos permite: O sistema de módulos do OCaml é muito rico. Para não dedicarmos excessivo tempo a este assunto, vamos ignorar quase todos os aspetos técnicos e vocabulário especializado associados ao sistema. Focamos a nossa atenção no estudo de alguns cenários de utilização que, afinal, cobrem a maioria das necessidades práticas.

Utilização de módulos existentes

Dentro do interpretador ocaml estão sempre disponíveis todos os módulos de biblioteca do OCaml (Index of modules).

Mas para aceder a módulos criados pelo utilizador dentro do interpretador ocaml, usa-se a diretiva #load assim:

Para referenciar elementos dum módulo, podem usar-se referências qualificadas, como se exemplifica: Mas é bastante mais prático abrir (importar) primeiro o módulo, para depois não ter de usar prefixos qualificadores:

Criação dum módulo aberto

Para criar um módulo aberto, no qual todas as definições são públicas, basta criar um ficheiro com o nome pretendido e extensão "ml", digamos "MySet.ml". Depois é necessário compilar o módulo usando o comando sendo gerados os ficheiros "MySet.cmi" e "MySet.cmo".

Exemplo de utilização do módulo aberto MySet:

Eis o conteúdo do ficheiro "MySet.ml":

Criação dum módulo fechado

Para ocultar a representação interna dos dados e as operações auxiliares é necessário criar adicionalmente um ficheiro interface "MySet.mli" onde se declaram todas as entidades públicas da forma que se pretende que sejam vistas do exterior. Depois compila-se o módulo assim sendo gerados os ficheiros "MySet.cmi" e "MySet.cmo".

Exemplo de utilização do módulo fechado MySet:

Eis o conteúdo do ficheiro "MySet.mli":

Repare que neste ficheiro interface omitimos a representação interna do tipo 'a set assim como a declaração da função auxiliar clear. Repare ainda na subtileza do tipo da função make (recebe uma lista mas retorna um set).

Quando se esconde a representação interna dum tipo de dados, diz-se que esse tipo é abstracto ou opaco.



#120