Linguagens e Ambientes de Programação (2022/2023)



Teórica 06 (22/mar/2023)

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 e possui alguns conceitos muito sofisticados. Contudo, aqui na disciplina de LAP, vamos aprender apenas os aspetos mais básicos, focando a atenção em alguns cenários de utilização que já cobrem muitas necessidades práticas.

Utilização de módulos previamente criados

No interpretador ocaml, estão imediatamente disponíveis todos os módulos de biblioteca do OCaml. Portanto, esses módulos podem ser usados sem ser preciso escrever qualquer diretiva. (Index of modules).

Contudo, para aceder a módulos criados pelo utilizador, é preciso usar a diretiva #load assim:

Para referenciar elementos dum módulo (seja um módulo de biblioteca ou um módulo do utilizador) existem duas alternativas.

Na primeira alternativa, podemos usar referências qualificadas, como se exemplifica:

Na segunda alternativa, podemos abrir (importar) primeiro o módulo, para evitar ter de usar prefixos qualificadores. Por exemplo:

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.



Vídeos antigos

Estes vídeos poderão estar um pouco desatualizados, pois foram feitos no contexto duma edição anterior do LAP. Contudo, partes dos vídeos poderão continuar a ter alguma utilidade.

#60 60