Realidades Paralelas

Friday, September 10, 2004

Eine Kleine Nachtmusik

Oz é uma linguagem estranha. Seus conceitos fundamentais são uma mistura de programação imperativa, funcional e lógica, além de programação concorrente e distribuída. Intrinsecamente multi-paradigma.

Terminei os dois primeiros capítulos do CTMCP e ainda fica um certo estranhamento, até porque no começo parece tanto com uma linguagem funcional. Mas tem unificação, valores parciais e outras coisas estranhas que parecem Prolog.

Para exemplificar, vamos começar com algum background: Oz é dividida em uma kernel language que abriga os conceitos fundamentais, e uma full language que inclui sintaxe extra e outras conveniências. A kernel language é simples e é nela que se define a semântica da linguagem; além disso, especifica-se formalmente como traduzir programas na full language para a kernel language, e assim é possível especificar a semântica de qualquer programa na linguagem completa.

Pois bem, a kernel language define procedimentos, e não funções, como conceitos primitivos. Funções são uma abstração sintática da linguagem completa, que são traduzidas para procedimentos na kernel language. Como exemplo, a função

fun {Times2 N}

N * 2
end
é traduzida para o procedimento
proc {Times2 N ?R}

R = N * 2
end

O ?R é um parâmetro de saída que é atribuído com o resultado de retorno do procedimento (se houver um). O que era funcional vira imperativo na kernel language. O que é realmente interessante é a forma como isso interage com valores parciais e estruturas de dados. Vamos definir uma função Map que aplica uma função F a cada elemento de uma lista L. O operador | é o construtor de lista (cons em Scheme/Lisp, :: em ML, : em Haskell); {F H} é uma chamada da função F com argumento H; case faz pattern matching. Logo após a função tem a tradução dela para a kernel language.

fun {Map F L}

case L of H | T then {F H} | {Map F T}
else nil end
end

proc {Map F L ?R}
case L of H | T then
local Rr in
R = {F H} | Rr
{Map F T Rr}
end
else nil end
end

Note-se aí a atribuição do resultado (R) a um valor parcial, pois Rr (o "resto" do resultado) ainda não possui valor. O efeito disso é transformar uma função que não é tail recursive em um procedimento que é. O programador ganha automaticamente os benefícios da recursão final (tradução tentativa para tail recursion), sem se preocupar com isso.

Estranho, mas interessante.

0 Comments:

Post a Comment

<< Home