Você está na página 1de 141

Tutorial de Mozart-Oz

Por Pedro Miguel Flix Alpio Agosto de 2000

Para a disciplina de Projecto do 5 Ano do Curso de Engenharia Informtica Ramo Computadores e Sistemas
Pedro Miguel Flix Alpio N 920385 E-mail: pedro.alipio@clix.pt

ndice
1. INTRODUO 1.1 - Os principais paradigmas da computao 1.2 Histria do Mozart-Oz 1.3 Resumo das Caractersticas do Oz 3 2. A LINGUAGEM OZ 2.1 O Modelo de programao do Oz 2.2 Comparao entre o Oz e o Prolog 2.3 Comparao entre o Oz e o Java 3. O OZ DISTRIBUDO 3.1 Caractersticas do Oz Distribudo 3.2 O grafo de distribuio 3.3 A Arquitectura de Implementao 3.3.1 O Motor Estendido 3.3.2 A Camada de Protocolo 3.3.3 A Camada de gesto de memria 3.3.4 Camada de Rede 3.4 Computao Aberta 3.4.1 Conexes e tickets 3.4.2 Servidores de computao 3.5 Deteco e Tratamento de Falhas 3.5.1 O Principio da deteno 3.5.2 Falhas no grafo da distribuio 3.5.3 Handlers e watchers 3.6 Controlo de Recursos e Segurana 3.6.1 Sites Virtuais 4. TUTORIAL DE OZ 7 7 8 9 11 11 14 14 16 18 19 20 21 21 22 22 22 22 23 23 24 25 25 26 27 28

4.1 O Ambiente de Desenvolvimento 4.1.1 Arrancar o ambiente de desenvolvimento 4.1.2 O primeiro programa 4.1.3 Principais caractersticas do OPI 4.2 Introduo programao em Oz 4.3 Os tipos bsico de dados 4.3.1 Instanciao de variaveis 4.3.2 Nmeros 4.3.3 Literais 4.3.4 Registos e Tuplos 4.3.6 Listas 4.3.7 Strings Virtuais 4.4 Igualdade e Teste de Igualdade 4.4.1 Teste de Igualdade 4.5 Estruturas Bsicas de Controlo e Procedimentos 4.5.1 A instruo vazia 4.5.2 A instruo condicional if 4.5.3 Procedimentos 4.5.4 Lexical Scoping 4.5.5 Semntica dos procedimentos 4.5.6 Inicializao de variveis 4.5.7 Pattern Matching 4.5.8 Aninhamento de instrues 4.5.9 Procedimentos como valores 4.5.10 Abstraces de controlo 4.6 Tratamento de Excepes 4.6.1 As excepes do sistema 4.7 Mdulos e Interfaces 4.8 Concorrncia 4.8.1 Tempo 4.8.2 Comunicao entre streams 4.8.3 Prioridades das Threads 4.8.4 Execuo orientada ao pedido 4.8.5 Deteco de terminao de threads 4.8.6 Composio concorrente 4.9 Tipos de dados baseados em estados 4.9.1 Ports 4.9.2 Comunicao Cliente-Servidor 4.9.3 As clulas 4.9.4 Chunks 4.9.5 Locks

28 28 29 30 34 35 35 35 36 36 38 39 39 40 41 41 41 43 45 45 46 47 49 50 51 52 53 54 56 58 58 60 61 62 63 64 64 65 66 67 68

4.10 Classes e Objectos 4.10.1 Implementao de Classes utilizando estruturas mais primitivas 4.10.2 Implementao de Objectos utilizando estruturas mais primitivas 4.10.3 As Classes em Oz 4.10.4 Chamadas estticas a mtodos 4.10.5 Herana de classes 4.10.6 Propriedades 4.10.7 Classes parametrizadas 4.10.8 Mtodos pblicos e privados 4.10.9 Valores por omisso dos argumentos 4.11 Objectos e Concorrncia 4.11.1 Trocas atmicas em atributos 4.11.2 Locks de threads reentrantes 4.11.3 Aplicando locks a objectos 4.11.4 Canal FIFO concorrente 4.11.6 Eventos 4.11.7 Objectos Activos 4.12 Arrays e Dicionrios 4.13 Programao Lgica 4.13.1 Armazenamento de restries 4.13.2 Espaos computacionais 4.13.3 Vinculao e desvinculao de restries 4.13.4 Disjunes 4.13.6 Execuo orientada determinao 4.13.7 Lgica Condicional 4.13.8 Expresses condicionais paralelas 4.13.9 Programas no deterministicos e busca 4.13.10 Exemplos 5. TUTORIAL DE OZ DISTRIBUDO 5.1 Nomes Globais 5.1.1 Conectar aplicaes atravs de tickets 5.1.2 Estruturas de dados persistentes usando pickes 5.1.3 Computaes Remotas e Functors 5.1.4 Servidores 5.1.5 Servidor de computaes 5.1.6 Servidor de computaes com funtors 5.1.7 Servidor dinamicamente extensvel 5.2 Agentes Mveis 5.2.1 Instalao de Agentes 5.2.2 Programao de agentes 5.2.3 A Definio do servidor de agentes 5.3 Tolerncia a falhas

69 69 70 70 71 72 73 75 76 77 78 78 78 79 80 80 81 83 84 84 84 85 85 86 87 87 88 90 93 93 94 95 95 96 97 97 98 99 99 101 102 103

5.3.1 O servidor de Ol Mundo com tolerncia a falhas 5.3.2 Tolerncia a falhas em objectos estacionrios 5.3.3 Tolerncia a falhas usando guardas 5.3.4 Tolerncia a falhas baseada em excepes 6 PROGRAMAO GRFICA 6.1 Toplevel e Objectos Widget 6.1.1 Frames 6.1.2 Labels 6.1.3 Imagens 6.1.4 Mensagens 6.2 Gestores de Geometria 6.2.1 O Packer 6.2.2 Grids 6.3 Outros Widgets 6.3.1Botes e Aces 6.3.2 CheckButtons, RadioButtons e variveis 6.3.3 Menus 6.3.4 Eventos 6.3.5 Entries 6.3.6 Scales 6.3.7 Listboxes 6.3.8 Manipular o toplevel 6.3.9 DialogBoxes predefinidas 6.4 O Canvas 6.4.1 Criar um grfico de barras 6.4.2 Eventos 6.5 Caixas de Texto 6.5.1 Manipulao do texto 6.5.2 Etiquetas de texto e marcas 6.6 Ferramentas para o Tk 6.6.1 Dialogs 6.6.2 Mensagens de Erro 6.6.3 Barras de Menus 6.6.4 Listas de Imagens 7. CONCLUSO APNDICE A Os programas dos testes de performance entre o Oz e o Java A.1 Produtor Consumidor Centralizado em Java A.2 Produtor Consumidor Centralizado em Oz

103 106 107 109 111 111 111 112 113 113 113 113 115 115 116 116 117 118 119 120 120 121 121 121 122 123 124 124 124 125 125 126 126 127 128 129 129 129 131

A.3 Produtor Consumidor Distribudo em Java A.4 Produtor Consumidor distribudo em Oz APNDICE B Nomes de Cores APNDICE C Glossrio de alguns termos BIBLIOGRAFIA

131 135 137 137 138 138 140

1. Introduo
As linguagens de programao descrevem comportamentos computacionais baseando-se em diferentes filosofias ou paradigmas. Uma distino mais rude dos paradigmas divide-os em duas categorias : programao baseada e no baseada em estados. A programao baseada em estados representa dados que mudam ao longo do tempo, enquanto que, a no baseada em estados, representa dados que so imutveis depois de serem criados. A distino entre estas duas filosofias importante pois a programao baseada em estados apresenta um modelo de programao que consiste numa aproximao ao mundo real, enquanto o modelo no baseado em estados permite uma maior simplicidade no raciocnio. A evoluo das linguagens de programao baseadas em estados culminou no modelo orientado a objectos, enquanto as no baseadas em estados (linguagens declarativas) deram origem computao directa e indirecta. Da computao directa nasceram as linguagens funcionais. Da computao indirecta surgiram as linguagens lgicas. A programao multi-paradigma consiste na integrao de vrios paradigmas num nico modelo. Neste modelo, as vrias formas de computao podem ser entendidas, como partes de uma filosofia nica. Esta forma de computao permite um estilo natural de programao, isto , adaptar a cada problema especifico que surge no decorrer da elaborao de uma aplicao, o paradigma mais adequado implementao de um algoritmo para o resolver. Por exemplo, para problemas relacionados com avaliao de funes, usar-se-ia o paradigma funcional, para problemas relacionados com informao parcial, usar-seia o paradigma da programao baseada em restries, para problemas em que seja necessrio representar entidades do mundo real ou estados, usar-se-ia o paradigma da programao orientada a objectos.

1.1 - Os principais paradigmas da computao


Os principais paradigmas da computao que so utilizados no Oz so os seguintes:

Programao Lgica
A programao lgica consiste na realizao de computaes como dedues. As caractersticas principais destas linguagens utilizadas pelo Oz so a utilizao de variveis lgicas e a pesquisa de solues (por exemplo: backtracking). Este tipo de programao deu mais tarde origem computao especulativa com base em informao parcial, ou seja, a computao baseada em restries. A linguagem mais conhecida que implementa este paradigma o Prolog.

Programao Funcional
As principais caractersticas das linguagens funcionais usadas pelo Oz so a utilizao de entidades first-class (funoes, variveis, etc... podendo ser passadas como argumento), sintaxe composicional e lexical scoping (est relacionado com o

espao contextual que a varivel abrange). Estes conceitos irao ser estudados em maior detalhe ao longo deste documento. Uma das linguagens que implementa este paradigma o Haskell.

Programao Orientada a Objectos


Actividades computacionais so organizadas em entidades chamadas de objectos, que encapsulam estados e mtodos que servem para os manipular. Com frequncia implementada a possibilidade de herana para facilitar o desenvolvimento incremental e reutilizao do cdigo. Utilizar objectos uma forma bastante poderosa de implementao de modelos baseados em estados ou computao imperativa, e so principalmente utilizados em aplicaes que se relacionam com o mundo exterior.

1.2 Histria do Mozart-Oz


O Oz e o sistema Mozart foram desenvolvidos pelos grupos de investigao Smolka no DFKI (Centro Alemo de Pesquisa para a Inteligncia Artificial), pelo Seif Haridi no SICS (Instituto Sueco de Cincias de Computao), e finalmente por Peter Van Roy no UCL (Universidade Catlica de Louvain). A primeira verso do Oz foi o Oz 1, e foi lanado em 1995. Caracterizavase sobretudo, por basear-se num modelo de concorrncia, que assume que qualquer expresso pode potencialmente ser executada de modo concorrente. Mais tarde surgiu outra verso Oz 2, que a principal novidade que trazia eram melhoramentos relativamente ao modelo de concorrncia anterior. Em vez de cada expresso ser executada de modo concorrente, as threads passam a ser criadas explicitamente. O modelo de concorrncia do Oz 1, dificultava bastante o controlo sobre os recursos partilhados e o debug dos programas. Passou-se assim a um modelo em que s existe concorrncia quando esta for desejada. Finalmente surgiu o Oz 3 e o sistema Mozart, a ltima verso da linguagem multi-paradigma Oz. As principais diferenas da verso anterior esto relacionadas com a Introduo de functors, que consistem em componentes de software espalhados por diferentes URLs, e as futures utilizadas na sincronizao do fluxo dos dados na internet.

1.3 Resumo das Caractersticas do Oz 3

Programao
Programao Orientada a Objectos
O Oz uma linguagem concorrente orientada a objectos. Esta linguagem pode inicialmente ser programada de modo similar a outras, como por exemplo o Java. O programador, atravs da experiencia, tender a simplificar os seus programas, usando os poderosos conceitos de execuo dependente no fluxo de dados e procedimentos first-class. O Oz possui as estruturas bsicas encontradas em todas as linguagens orientadas a objectos, tais como : classes, objectos, mtodos, atributos e herana.

Programao Concorrente
O Oz iminentemente uma linguagem concorrente. O sistema Mozart implementa um sistema de threads ultraleves utilizando um escalonamento preemptivo baseado na diviso do tempo (time slice) de processamento pelas threads Fair scheduling. Possui dois mecanismos de sincronizao : baseados no fluxo dos dados ou de forma transparente pela utilizao de variveis lgicas.

Multi-Paradigma
Quase todas as linguagens de programao tm um modelo baseado num s paradigma. O Oz usa de forma coerente e simples os paradigmas: funcional, orientado a objectos, e programao lgica. Isto possivel porque possui uma implementao muito geral e poderosa do paradigma de programao concorrente baseada em restries.

Inferncia
Programao Baseada em Restries
O Oz uma poderosa linguagem de programao por restries com variveis lgicas, domnios finitos, conjuntos finitos, rvores relacionais e restries de registo. O sistema competitivo em desempenho, com as solues comerciais, mas mais expressivo e flexvel, disponibilizando espaos computacionais fistclass, estratgias programveis de pesquisa de solues, uma ferramenta grfica para a explorao interactiva das rvores de pesquisa, motores de pesquisa paralelos explorando a possibilidade de distribuir a computao na rede e um interface por programao para construir eficientes sistemas novos de restries.

Programao Lgica
O Oz implementa tanto programao declarativa lgica directa como indirecta. Possui poderosas ferramentas construdas sobre os conceitos espaos de computao first-class e disjunes. Torna o Mozart ideal para a implementao de sistemas Multi-Agentes Inteligentes ou sistemas de pesquisa paralelos.

Distribuio
Sistemas Distribudos Abertos
O sistema Mozart a plataforma ideal para este tipo de sistemas porque torna a rede completamente transparente. Cria a iluso de uma rea de armazenamento comum, que se encontra na realidade estendida a varias localizaes. O suporte para isso, so protocolos bastante eficientes. O Mozart tem

controlo absoluto sobre as comunicaes na rede permitindo o uso de modo bastante eficiente os recursos por ela disponibilizados. Permite tambm fcil desenvolvimento de aplicaes robustas, com mecanismos de tolerncia a falhas.

Programao Baseada em Componentes Distribudos


O Mozart permite o desenvolvimento especificaes de componentes firstclass (functors) e componentes (modules). Os mdulos facilitam o desenvolvimento de aplicaes. Estes componentes podem ser acedidos atravs de URLs, absolutas e relativas, carregados quando necessrios (lazy). Permite a implementao de polticas de segurana flexveis pelos gestores do modulo.

Agentes Mveis
Tendo uma tecnologia de componentes dinmicos, suporte para computao aberta, e todas as outras caractersticas, o Mozart uma plataforma ideal para programao de agentes mveis. Uma computao capaz de criar computaes num espao distribudo e dinmico.

Plataforma
Compatibilidade
Tal como o Java, o Oz do tipo escreve um vez, executa em qualquer sitio possui mecanismos locais e distribudos de garbage colection. O Oz possui uma virtual machine, que portvel e pode ser executada em sistemas Unix ou Windows.

Interface Grfica
O sistema Mozart tem uma biblioteca orientada a objectos que disponibiliza funcionalidades de interface grfico baseadas em Tcl/Tk.

Mdulos de Expanso Nativos


O sistema Mozart foi projectado para que fosse facilmente expandido de forma a aumentar as suas funcionalidades atravs de DLLs.

10

2. A Linguagem Oz
O Oz uma linguagem poderosa que se baseia num pequeno conjunto de conceitos simples. Este captulo tenta explicar o modelo de programao do Oz,e ainda compar-lo com outras linguagens semelhantes. Apesar das razes do Oz serem a programao lgica concorrente baseada em restries, esta linguagem pretende ir muito mais longe. O objectivo criar uma infra-estrutura slida para qualquer tipo de computao, no apenas para a programao declarativa.

2.1 O Modelo de programao do Oz


Threads de Fluxo de Dados
Bloqueiam quando no tem disponiveis os dados Executa sequncias de expresses

S1

S1

Sn

rea de armazenamento abstacto

X=23 Z Y=Pessoa(idade:27)

No memria fisica Contm variveis e atribuies Apenas admite operaes vlidas para as entidades envolvidas

Figura 1 Threads e Armazenamento

O modelo do Oz constitudo por uma rea de armazenamento abstracta acedida por threads de fluxo de dados. As thread executam sequncias de expresses e bloqueiam quando na falta de disponibilidade dos dados. A rea de armazenamento no a memria fsica. S autoriza operaes que sejam validas para as entidades envolvidas, isto , no permite apontadores tipo linguagem C, nem casting. A rea de armazenamento tem trs componentes: a rea de restries, que contem as variveis e os valores a elas atribudos (instanciaes); a rea de procedimentos, que contem a definio dos procedimentos; e finalmente a rea das clulas, que contm apontadores mutveis (Cells). A rea de armazenamento de restries e a rea de armazenamento de procedimentos so monotnicas ou monotonas, isto , os dados nelas contidos no podem ser alterados ou removidos, apenas se podem adicionar. As threads, bloqueiam com a no disponibilidade de dados que necessitem na rea armazenamento de restries.

11

<Expresso> ::= | | | | | | | | | | | | | | | |

<Expresso1> <Expresso2> X = f(l1:Y1 ... ln:Yn) X = <nmero> X = <atomo> X = <booleano> {NewName X} X = Y local X1 ... Xn in S1 end proc {X Y1 ... Yn} S1 end {X Y1 ... Yn} {NewCell Y X} {Exchange X Y Z} {Access X Y} if B then S1 else S2 end thread S1 end try S1 catch X then S2 end raise X end

Figura 2 Oz Programming Model

As threads executam uma linguagem que consiste num forma reduzida do Oz designada de OPM (Oz Programming Model). Na figura 1, encontra-se descrito o sintaxe desta linguagem. As sequncias de expresses so reduzidas de forma sequencial na thread. Os valores (Registos, nmeros, etc ...), so introduzidos explicitamente e podem ser igualados a variveis. Todas as variveis so lgicas e definidas num espao explicito definido pela palavra local. Os procedimentos so criados em tempo de execuo atravs da palavra proc, e referenciados por uma varivel. Na rea de armazenamento so representados na forma :z/E, em que o nome, o z um argumento formal (varivel), e o E o corpo do procedimento. A reduo de um procedimento na forma proc{x y} E, processa-se escolhendo um nome , indicando na rea de armazenamento de restries que x = , escrevendo de seguida na rea de armazenamento de procedimentos o novo procedimento (:z/E). A chamada ao procedimento x ( {x y} ), bloqueia enquanto no estiver na rea de armazenamento de procedimentos a definio :z/E para a varivel x, que por sua vez est definida na rea de armazenamento de restries sob a forma de x=. Neste caso desta chamada a reduo feita substituindo todas as ocorrncias de z no corpo do procedimento E por y. As variveis de estados so criadas explicitamente atravs de NewCell, que cria uma clula que consiste num apontador altervel para rea de armazenamento de restries. As clulas podem ser alteradas atravs de Exchange e Access. As condies so implementadas atravs da palavra chave case que bloqueia, enquanto a condio, na rea de armazenamento de restries, no for verdadeira ou falsa. A palavra chave if reservada para aplicaes baseadas em restries. As threads, so criadas de forma explicita atravs da palavra thread, e ficam com um identificador prprio. O tratamento de excepes feito atravs das palavras try que determina o espao de abrangncia e catch que indica a aco a tomar caso ocorra nesse espao uma excepo. As expresses do Oz completo so executadas traduzindo essas expresses noutras na forma reduzida ou OPM. O Oz completo disponibiliza outras estruturas, tais como objectos, classes, locks reentrantes, e ports. Que consistem no seguinte:

12

Objectos
Um objecto essencialmente um procedimento com um argumento ({Obj M}) que referencia uma clula escondida pelo lexical scoping. Essa clula detm o estado do objecto. O argumento M indexa o mtodo na tabela de mtodos do objecto. O mtodo um procedimento que a partir de um determinado estado calcula um novo estado.

Classes
A classe um registo uma tabela de mtodos e os nomes dos atributos. Os conflitos de herana mltipla so resolvidos na altura da criao da criao da classe, de modo a ser possvel construir a tabela de mtodos.

Locks Reentrantes
Um lock reentrante consiste num procedimento com um argumento ( {Lck P} ) usado para efectuar excluso mtua (por exemplo podem ser usados para excluso mtua nos corpos dos mtodos, criado uma espcie de entrys de objectos protegidos do ADA95). A thread que acede ao lock pode reentrar nele. Isto permite a utilizao de Nesting. O lock libertado quando a thread no seu corpo terminar, ou se surgir uma excepo durante a execuo, que faa abandonar corpo do lock.

Ports
Os Ports canais assncronos que suportam um modelo de comunicao um para muitos. Estas estruturas encapsulam streams, que consistem em listas com a cauda no instanciada. A operao ( {Send P M} ) adiciona M ao fim do stream encapsulado por P. Envios sucessivos partindo da mesma thread, aparecem no stream, com a mesma ordem de envio. Mais tarde ir ser detalhado como se implementa cada uma destas estruturas.

13

2.2 Comparao entre o Oz e o Prolog


Pode dizer-se que o Oz um sucessor do Prolog, pois esta linguagem pode ser utilizada para resolver os mesmo tipo de problemas, que o Prolog e as linguagens lgicas baseadas em restries tm sido usadas. Tal como o Prolog o Oz tem um subconjunto de instrues declarativas e um sistema arbitrrio de restries. O desempenho do Oz est muito prximo com os melhores sistemas de Prolog emulados. O Oz no possui um sintaxe reflectivo, isto , dados e programas com o mesmo sintaxe, nem possui mecanismos de meta-programao como por exemplo, o Assert no prolog, nem um sintaxe definivel pelo utilizador. O motivo do xito do Prolog o nvel elevado de abstraco do seu subconjunto declarativo, mas a tudo o que est fora desse domnio foi dada muito pouca ateno.
Programao lgica Concorrente No, excepto no AKL Fine-grainded Oz Solver com ask e tell Thread explicitas de fluxo de dados, pequisa encapsulada Procedimentos firstclass e lexical scoping Objectos, clulas

Restries Controlo

High-order Estados

Restrita Objectos baseados em streams

Tabela 1 Comparao entre as linguagens lgicas concorrentes e o Oz

2.3 Comparao entre o Oz e o Java


O Mozart e o Java, de modo geral em aplicaes centralizadas tm desempenhos comparveis, embora o Mozart tenha melhores mecanismos de fluxo de dados e multi-thread.
Java Tempo de Execuo Linhas de cdigo 17.6 segundos 108 28 Mozart 3.9 segundos

Tabela 2: Estatsticas relativas s diferena de desempenho entre o Oz e o Java executando uma aplicao Produtor/Consumidor (jdk 1.2 e mozart 1.0.1 num solaris UltraSparc II)

Em aplicaes distribudas o desempenho do Java muito menor que o do Mozart. A principal razo que para cada elemento que o Produtor produz, enviada uma mensagem de rede, e a thread sincronizada depois do retorno de uma chamada a RMI (Remote method evocation). O sistema Mozart agrupa automaticamente grupos de elementos que o Produtor produz, consequentemente reduzindo o numero de mensagens na rede. Para alm disso, no Mozart apenas o consumidor necessita ser sincronizado. O java pode ainda ser optimizado para a lgica Produtor/Consumidor, mas isso iria fazer com que o programa fosse bastante maior e mais complexo, e tornaria o programa distribudo cada vez mais distinto da verso centralizada. Conclui-se que a programao distribuda eficiente consideravelmente mais difcil de implementar em Java que em Mozart.

14

Java Tempo de Execuo Linhas de cdigo 1 hora 220

Mozart 8.0 segundos 32

Tabela 3 Resultados da aplicao Produtor/Consumidor distribuida.

15

3. O Oz Distribudo
Cada vez mais os computadores so ligados em redes. A transmisso de informao de um ponto para outro qualquer no mundo, tornou-se trivial. A internet, construda sobre a famlia de protocolos TCP/IP, tem vindo a duplicar o numero de servidores desde 1981. O trabalho colaborativo que inicialmente consistia em trocas de e-mail e newsgroups, usa agora tecnologias como workflow, multimdia, e ambientes verdadeiramente distribudos. Fontes de informao heterogneas e fisicamente separadas, so ligadas. Surgem os agentes, permitindo a delegao de tarefas na rede. Criam-se protocolos seguros que possibilitam o comercio electrnico. Apesar de todo este desenvolvimento espantoso, a computao distribuda continua um grande desafio. Um sistema distribudo um conjunto de processos autnomos, interligados por uma rede. Para especificar que os processos no correm necessariamente na mesma mquina, deu-se-lhes o nome de sites. Um sistema como este muito diferente de um s processo a correr numa mquina. Estes sistemas so inerentemente concorrentes e no deterministicos. No existe informao global nem tempo global. Os atrasos na comunicao entre os processos imprevisvel. Existe uma grande probabilidade de falhas localizadas. O sistema partilhado, tendo os utilizadores, de estar protegidos dos outros utilizadores e dos seus agentes computacionais. Uma aplicao distribuda deve ter bom desempenho, ser segura, permitir comunicao de forma simples com outras aplicaes. Actualmente, o desenvolvimento de uma aplicao distribuda com estas caractersticas, requer conhecimentos muito para l dos que so necessrios para desenvolver uma aplicao numa mquina s. Por exemplo, uma aplicao Cliente-Servidor pode ser desenvolvida em Java-RMI. Uma determinada aplicao pode comunicar com outra atravs de um implementao de CORBA (por exemplo o Orbix). Nestes casos, ambas as ferramentas so insatisfatrias. A reorganizao da aplicao para suportar uma estrutura distribuda implica rescrever toda a aplicao. O java no usa threads de fatias temporais, por isso, a reorganizao em Java exige profundas alteraes na aplicao. Para alm disso, cada nova funcionalidade que acrescentada, por exemplo adicionar tolerncia a falhas, a complexidade da aplicao aumenta bastante. Para se dominar cada problema, necessrio estudar e compreender vrias ferramentas complexas para alm do ambiente base do Java, isto , algum que apenas saiba desenvolver aplicaes centralizadas no est qualificado para desenvolver aplicaes distribudas. Algumas solues foram tentadas para integrar mecanismos de resoluo de problemas de vrias reas, numa nica plataforma. Por exemplo, a Ericsson Open Telecom Platform (OTP), baseada na linguagem Erlang, integra uma soluo para estruturas distribudas e tolerncia a falhas. O Erlang transparente quanto utilizao da rede a nvel de processo, isto , as mensagens entre processos so enviadas da mesma forma independentemente dos processos estarem ou no na mesma mquina. A OTP vai bastante mais longe que plataformas mais populares tais como o Java, e tem sido usado em produtos comerciais relacionados com telecomunicaes (Software para centrais telefnicas PBX, routers ATM, etc...) . O sucesso do Erlang sugere aumentar o alcance das ferramentas de desenvolvimento de aplicaes a outras reas da computao distribuda. Existem

16

quatro reas, nomeadamente estrutura de distribuio, computao aberta, tolerncia a falhas e segurana. Se for includa a funcionalidade da aplicao, existiro cinco principais preocupaes: Funcionalidade: O que que a aplicao faz, ignorando tudo o que diz respeito distribuio. Estrutura de distribuio: Repartir a aplicao por vrios sites. Computao Aberta: Aplicaes independentes que comunicam entre si. Tolerncia a falhas: A aplicao continuar a providenciar um servio mesmo que ocorram falhas parciais. Segurana: A capacidade da aplicao continuar a providenciar um servio mesmo que haja interferncia intencional. Uma parte muito importante da tolerncia a falhas e segurana o controlo dos recursos. Uma soluo possvel para integrar estas preocupaes, separar a funcionalidade das outras (figura 2). Idealmente, o maior volume da aplicao deveria ser ocupado com o cdigo relacionado com as suas funcionalidades, as outras preocupaes deveriam ser, nada mais, do que pequenos mdulos adicionais. O primeiro passo para atingir isso, ser separar a funcionalidade da estrutura de distribuio. O sistema dever ser transparente relativamente ao uso da rede. Para isso as computao de aplicaes centralizadas e distribudas tero de ser semelhantes, ou seja, dever ser possvel programar a aplicao sem ter em conta a existncia de uma rede de computadores (network-transparent system). Outro aspecto importante, manter, por parte do programador, o controlo sobre a localizao das computao e das comunicaes na rede, decidindo onde so executadas e detendo o controlo sobre a mobilidade e replicao dos dados e do cdigo (network-aware system).
Estrutura de distribuio Funcionalidade Computao Aberta

Estrutura de distribuio Computao Aberta Tolerncia a falhas Tolerncia a falhas Funcionalidade Controlo de recursos e segurana

Controlo de recursos e segurana

No afectam a funcionalidade Figura 2 Simplificao da programao distribuda

17

3.1 Caractersticas do Oz Distribudo


O Oz distribudo separa a funcionalidade da estrutura de distribuio da aplicao. Esta linguagem consiste apenas na expanso do Oz centralizado. A converso de aplicaes programadas em Oz centralizado para o Oz distribudo, uma tarefa muito simples. Existem trs caractersticas que tornam o Oz uma linguagem muito poderosa para implementar sistemas distribudos: O Oz tem uma base formal solida que no prejudica a expressividade nem a eficincia na implementao. O Oz baseia-se em high-order, state-aware, computao baseada em restries concorrente. O Oz apresenta-se ao programador como uma linguagem concorrente orientada a objectos, com capacidades idnticas s linguagens modernas como o Java. Para alm das principais tcnicas da programao concorrente orientada a objectos, o Oz, disponibiliza alguns novos mecanismos que o Java, por exemplo, no possui. O Oz uma linguagem state-aware e orientada ao fluxo dos dados, que permitem ao programador, o controlo sobre as comunicaes de rede de uma forma natural. State-aware quer dizer que a linguagem distingue entre dados que no referem estados (procedimentos ou valores), que podem ser copiados de forma segura entre sites (mquinas), e dados que referem estados (Objectos), que num dado instante apenas podem residir num site. A sincronizao pelo fluxo dos dados permite distribuir clculos, o que bastante importante na tolerncia de falhas, nomeadamente na construo de rplicas. O Oz disponibiliza segurana a nvel da linguagem, isto , a referencia a qualquer entidade passada e criada de forma explicita. Uma aplicao incapaz de forjar ou aceder a referencias que no lhe tenham sido atribudas explicitamente. A representao da entidades da linguagem inacessvel ao programador. O Oz usa uma rea de armazenagem abstracta com lexical-scoping, e procedimentos first-class. Estas caractersticas so fundamentais para implementar uma linguagem com capacidade de ter uma poltica de segurana dentro dela prpria. A separao entre a funcionalidade e a estrutura de distribuio de uma aplicao impe grandes restries a uma linguagem de programao. Seria praticamente impossvel em C++ por causa da semntica ser informal e bastante complexa, e tambm porque o programador tem acesso total a todas as representaes de entidades (ex: usando endereos de memria). Em Oz possvel porque implementa as caractersticas atrs descritas. No foi necessrio alterar a semntica da linguagem alm de pequenas modificaes para permitir a distribuio (por exemplo, os ports foram alterados para o modelo assncrono de comunicao entre sites). Continua-se a trabalhar para separar a funcionalidade das outras trs preocupaes restantes. Actualmente, o Oz distribudo, disponibiliza a semntica de linguagem para o Oz e para complementos de quatro formas: Tem construes para exprimir a estrutura da distribuio de forma independente da funcionalidade. Tem primitivas de computao aberta, baseadas no conceito de tickets. Isto permite aplicaes de execuo independente se ligarem e trocarem dados e cdigo. Tem primitivas para deteco e tratamento de falhas, baseadas nos conceitos de handlers e watchers, que permitem um primeiro nvel de tolerncia a

18

falhas. Suporta uma poltica de segurana dentro da prpria linguagem e possui primitivas para o controlo de recursos baseadas no conceito de site virtual. No Oz distribudo, o desenvolvimento de uma aplicao esta separado em duas partes independentes. Em primeiro lugar, apenas a arquitectura lgica da tarefa considerada. A aplicao desenvolvida em Oz, sem que se tenha em ateno a distribuio da sua computao por diversos sites. Assim pode-se verificar a segurana e a durabilidade da aplicao executando em apenas um site. Depois, a aplicao tornada eficiente pela especificao dos comportamentos de rede nas suas entidades. Em particular, a mobilidade das entidades com estados (objectos), deve ser especificada. Por exemplo, alguns objectos podem ser colocados em certos sites, enquanto outros podem ter um caracter mvel. O Oz distribudo expande o Oz com quatro algoritmos complexos. Trs deles foram desenvolvidos para entidades especificas da linguagem, nomeadamente variveis lgicas, objectos-registos, e objectos-estados de objectos. As variveis lgicas so instanciadas atravs do variable binding protocol. Os objectos-registos so replicados pelos sites atravs do lazy replication protocol. Os objectos-estados so movidos entre sites atravs do mobile state protocol. O quarto protocolo um algoritmo de recolha de lixo (garbage collection) distribudo. Este mecanismo uma parte da gesto de entidades distribudas, serve portanto de suporte aos outros protocolos.

3.2 O grafo de distribuio


Varivel no instanciada Registo com campos Thread com referncias

Clula

Procedimento com referncias externas

Figura 3 As entidades da linguagem representadas com ns do grafo

O modelo pode ser entendido de uma forma simples mas precisa usando o conceito de grafo de distribuio. O grafo de distribuio pode ser obtido em dois passos a partir de um estado arbitrrio do sistema. O primeiro passo independente da distribuio. Modela-se o estado de execuo utilizando um grafo chamado grafo de linguagem, onde cada entidade da linguagem com a excepo dos objectos que correspondam a ns (Figura 3). No segundo passo, introduz-se a noo de site. Assume-se um conjunto finito de sites e coloca-se cada n no seu site respectivo (Figura 4). Se um n for referenciado por pelo menos outro n noutro site, organiza-se um conjunto, por exemplo para N2, cria-se o conjunto {P1,P2,P3,M}. A este conjunto d-se o nome de estrutura de acesso do n. Uma estrutura de acesso e constituda por um n proxy Pi para cada site que referencia o n e um n de gesto M para toda a estrutura. O grafo que resulta, contendo tanto os ns locais como as estruturas de acesso necessrias, chamado de grafo de distribuio. Os exemplos do funcionamento dos protocolos sero apresentados segundo esta notao. A cada estrutura de acesso dada um endereo global nico no sistema global. Este endereo global codifica diversa informao incluindo o site de gesto.

19

Os ns proxy so unicamente identificados pelo par (endereo global, site). Em cada site, os endereos globais indexam uma tabela que referencia o proxies. Cada site tem pelo menos um proxy. As mensagens entre ns so enviadas nas estruturas de acesso. No corpo das mensagens existem referencias a ns no site destino. Esses ns so identificados pelo endereos globais das suas estruturas de acesso. Quando as mensagens chegam, os ns so pesquisados na tabela do site. Os procedimentos e os outros valores (registos, nmeros, etc) so copiados de forma eager, isto , no resultam em estruturas de acesso. Um procedimento enviado uma vez s para um site e apenas existe uma cpia nesse site. O procedimento consiste numa clausula e um bloco de cdigo, sendo-lhes atribudo um endereo global. As mensagem apenas contm o endereo global. Depois de serem recebidas imediatamente feito o pedido dos blocos de cdigo e clausulas que faltam.
Estrutura de acesso para N2

N1

N2

N3

N1

P1

P2

P3

N3

Site 1

Site 2

Site 3

Site 1

Site 2

Site 3

Figura 4 Grafo da linguagem e Grafo da distribuio

3.3 A Arquitectura de Implementao


A figura 5, mostra a arquitectura de implementao do Oz distribudo. De seguida iro ser detalhadas todas as camadas desta implementao.
Rede Camada de Rede

Camada de gesto de memria

Camada de protocolo

Motor estendido

Figura 5 A arquitectura de implementao

20

3.3.1 O Motor Estendido


Site1 Site2 Site3

M 2 3 P P 4 1.A thread inicia o processo de instanciao e bloqueia. 2.O proxy faz um pedido de instanciao 3.O n gestor (M) concede a intanciao e efectua um multicast para todos os proxies 4.O proxy informa a thread permitindo que ela continue a execuo 3 1 T

Figura 6 Instanciao de uma varivel

Este motor expande o Oz centralizado sob a forma de um interface para a camada de protocolo. O motor reconhece certos eventos e passa-os camada de protocolo. Um exemplo tpico a instanciao de variveis (figura 6) atravs dos seus proxies. A camada de protocolo pode tambm desencadear operaes do motor, por exemplo chegada do contedo de uma clula vindo de outro site, ou a passagem de procedimentos da rede para o motor.

3.3.2 A Camada de Protocolo


Os ns no grafo so modelados como objectos concorrentes. Estes objectos trocam mensagens atravs do grafo. Os algoritmos que controlam essas trocas de mensagens so chamados de protocolos de distribuio da implementao. Estes protocolos definem o comportamento do sistema. Cada estrutura de acesso tem o seu prprio protocolo. Existem trs tipos: Estrutura de acesso de variveis. Representa a varivel globalizada. Os proxies representam a varivel em cada site. Os proxies tornam-se referencias quando a estrutura de acesso da varivel fundida com outra estrutura de acesso. O papel do n Gestor (normalmente representado por M no grafo), esperar o primeiro pedido de fuso e pedir a todos os proxies que modifiquem os gestores. O conjunto dos proxies formam um grupo de multicast. Este protocolo usado para adicionar informao rea de armazenamento de restries. Estrutura de acesso de procedimentos. Representa os procedimentos globalizados no sistema. Sendo os procedimentos informao sem dependncia de estados podem ser replicados para outros sites quando necessrios. Este protocolo assegura que cada clausula e bloco de dados do procedimento seja nico em cada site. O n gestor gere os pedidos para as cpias. Este n esta associado a um n local que representa a estrutura ou o procedimento. Estrutura de acesso de mobilidade. Representa um clula ou um port. O estado localizado, isto , encontra-se sempre num determinado site, e pode ser

21

movido de um proxy para outro. O proxy que necessitar de ler um estado pede ao gestor, este decide quem ir aceder a seguir e envia uma mensagem ao proxy respectivo.

3.3.3 A Camada de gesto de memria

Esta camada converte as estruturas de distribuio do grafo em sequncias de bytes para ser enviadas atravs da rede. As mensagens trocadas entre ns do grafo de distribuio podem referenciar subgrafos. Quando os subgrafos so transportados de um site para outro, os seu ns tornam-se membros das estruturas de acesso que tm ns tanto no site emissor como no receptor. As estruturas de acesso so identificadas por um endereo nico na rede chamado endereo de rede (network address). Este endereo recuperado quando a estrutura de acesso que identifica deixa de ser acessvel localmente de nenhum dos seus sites. Esta situao detectada por mecanismos locais de garbage collection com um mecanismo de crdito. Cada endereo de rede criado com um numero grande fixo de crditos. O n gestor inicialmente detm os crditos e atribui-os a qualquer site (incluindo mensagens em transito) que tm o endereo de rede. Quando um site deixa de referenciar localmente a estrutura de acesso os crditos so devolvidos ao gestor. Quando o gestor recupera todos os crditos e deixa de ser referenciado localmente o endereo de rede recuperado (libertado).

3.3.4 Camada de Rede


A camada de rede implementa uma cache de conexes TCP, garantindo confiana e eficincia na transferncia de informao entre sites numa qualquer rede local ou wan. Assume-se que no existem barreiras temporais nem ordem relativa das mensagens (FIFO) para todos os protocolos de distribuio excepto para estruturas de acesso fixas. Para enviar mensagens de tamanhos variveis provenientes de threads concorrentes baseadas em fatias temporais, a implementao gere os seus prprios buffers e usa chamadas ao sistema no bloqueantes do tipo send e receive.

3.4 Computao Aberta


Computao distribuda aberta consiste na execuo de aplicaes independentes que interagem umas com as outras. Normalmente, isto implica que o sistema tenha que ter a mesma base, isto , a utilizao dos mesmos frameworks ou linguagens utilizadas pelas aplicaes para interagir. Os exemplos mais tpicos so formatos comuns na transferencia de informao, protocolos comuns para comercio electrnico, etc... Para isto ser possvel necessrio que as aplicaes consigam estabelecer conexes com computaes que foram iniciadas de forma independente noutra localizao na rede. tambm necessrio que a aplicao possa iniciar novas computaes distribudas.

3.4.1 Conexes e tickets


O Oz distribudo usa um mecanismo baseado em tickets para estabelecer

22

conexes entre sites independentes. O sistema, na fase final do seu desenvolvimento deve garantir segurana, tanto a nvel das conexes como a nvel dos tickets. O site servidor cria o ticket atravs do qual os sites clientes podem estabelecer a conexo. O ticket consiste numa string de caracteres que pode ser guardada e transportada de qualquer forma que suporte texto, por exemplo por correio electrnico, modem, etc... O ticket identifica o site servidor e a entidade da linguagem qual ser feita uma referencia remota. Podem ser feitas conexes independentes a diferentes entidades num mesmo site. O estabelecimento de uma conexo gera uma conexo a nvel da camada de rede (por exemplo TCP), e gera tambm no espao computacional do Oz, uma referencia no site cliente a uma entidade da linguagem no lado do site servidor. Isto pode ser implementado de vrias formas, passando procedimentos sem argumentos, unificando duas variveis, ou passando um port que ser utilizado para posteriormente para enviar valores. Depois de uma conexo inicial ter sido criada, todas as outras que a aplicao necessitar podem ser geradas a partir das abstraces disponibilizadas pela linguagem. Por exemplo, possvel definir uma classe C num site, passar C para outro site, definir a classe D derivada de C nesse site, e finalmente passar D para o site inicial. Existem dois tipos de tickets: one-shot tickets para conexes um para um, e many-shot tickets para conexes muitos para um.

3.4.2 Servidores de computao


O Oz possibilita a criao de servidores de computao. Servidores de computao so aplicaes que usam a rede para aumentar a velocidade da computao. Estes servidores podem ser acedidos como objectos, e so implementados utilizando um mecanismo de tickets. Depois de um servidor ser inicializado podem lhe ser atribudas tarefas. Estas tarefas so passadas ao servidor sob a forma de procedimentos. A inicializao dos servidores feita em dois passos : primeiro criado um site independente pelo potencial cliente, de seguida estabelecida a conexo entre o potencial cliente e o servidor. Isto feito passando um ticket one-shot do potencial cliente para o servidor.

3.5 Deteco e Tratamento de Falhas


Uma aplicao tolerante a falhas quando continua a cumprir as finalidades para que foi desenvolvida apesar de acontecerem falhas acidentais em componentes dessa aplicao. Existem muito pouco trabalho no sentido de integrar abstraces de tolerncia a falhas numa linguagem de programao de modo a que se possa construir um sistema tolerante a falhas. A maior parte do trabalho foi concentrado em reas como a persistncia e transaces, adicionando modelos destes conceitos s linguagens de programao. No entanto possvel suportar tolerncia a falhas de um modo mais simples. O Oz foi expandido de modo a suportar falhas parciais em sites e detectar e tratar entidades individuais da linguagem. Existem meios para o programador decidir qual a aco a executar no caso de uma falha. Esses meios so implementados atravs de handlers e watchers sobre as entidades da linguagem.

23

3.5.1 O Principio da deteno


A tolerncia a falhas algo que por vezes est para l dos limites da abstraco. Vejamos o seguinte exemplo : A maior parte dos sistemas no lidam com o tempo de uma forma correcta. O que fazem deixar que uma camada mais baixa tomar uma deciso irreversvel. Relativamente a um time-out no deixam o sistema prosseguir. Se houver um time-out numa camada baixa, por exemplo na camada de transporte da rede, ir atravessar todas as camadas at aparecer no nvel mais alto ao utilizador. No existe a possibilidade de comunicar com camada onde ser verificou o time-out, o que limita bastante a flexibilidade do sistema. Deveria ser possvel construir um sistema, que permitisse ao utilizador aguardar, em vez de abortar. Em muitos casos no oferecida esta possibilidade. O principio da deteno diz que um comportamento anormal de uma qualquer camada dever ser contido numa camada mais alta. Isto , uma camada onde se verifique uma situao anormal no deve tomar decises irreversveis, estas devem ser decididas pelo programador, ele que dever decidir que camada indicada para tratar o problema. Por exemplo, na questo do time-out, o programador deveria ter a oportunidade de decidir, se queria ou no um time-out numa camada mais baixa, e deveria ser ele a decidir qual a aco a tomar caso existisse time-out e fosse ultrapassado.

24

3.5.2 Falhas no grafo da distribuio

Site A

Site B

Site C

M P P

M P P

Site D

Site E

Site F

Site em falha Site normal N afectado N normal

Figura 7 Deteco remota de falhas no Oz distribudo

A causa de uma falha no Oz distribudo est relacionada com falhas num ou mais sites, ou com a falha de uma rea da rede. Estas situaes revelam-se nas estruturas de acesso do grafo da distribuio. Uma estrutura de acesso afectada se tem pelo menos um n que se situe num site com falha ou se tem pelo menos uma ligao estraves de uma rea da rede com falha. Muitas vezes estruturas de acesso continuam a funcionar apesar de serem afectadas. Por exemplo, um objecto pode continuar a ser usado apesar de ter uma referencia remota a um site em falha. Uma estrutura de acesso falha quando os sites no conseguem operar nela. Isto pode suceder quando os componentes principais da estrutura de acesso falham, por exemplo, quando falha o n de gesto. Tendo em conta que as redes podem ter falhas temporrias, tambm as falhas das estruturas de acesso podem ter um caracter temporrio ou permanente.

3.5.3 Handlers e watchers


O Oz distribudo detecta falhas ao nvel das estruturas de acesso,

25

revelando-se na linguagem sob a forma de entidades da linguagem (objectos, variveis e ports). Quando h um problema a efectuar uma operao efectuada sobre uma entidade, essa operao por omisso, bloqueia indeterminadamente. Qualquer outro comportamento deve ser explicitamente especificado pelo programador. Para isso devem ser instalados watcher e handlers na entidade. Um handler invocado se for detectado um erro quando tentada a execuo de uma operao sobre uma entidade (deteco lazy). Um watcher invocada quando um erro detectado para uma entidade, mesmo que nenhuma operao seja tentada sobre essa entidade (deteco eager). A semntica dos handlers e dos watchers bastante simples. Se a tentativa de execuo de uma operao falha, se existir um handler com uma condio de trigger que seja verificada, efectuada uma chamada a esse handler. O funcionamento idntico para os watchers, se o sistema verificar uma falha de uma determinada entidade, todos os watchers so verificados e aquelas que tiverem condies de trigger que sejam verdadeiras, so executados em threads. Os handlers e os watchers tem dois argumentos: o primeiro diz respeito entidade que falhou e o segundo ao tipo de erro. Os handlers podem ser instalados em entidades por site e por thread. Os handlers baseados em threads sobrepem-se aos baseados em sites, isto , se forem ambos aplicados os sistema apenas responde ao handler baseado em threads. Os watchers podem ser instalados por site ou por entidade. So passados trs argumentos para utilizar os handlers e os watchers: a entidade, informao de controlo, e o prprio handler ou watcher. A informao de controlo consiste no tipo de erro que o handler ou o watcher suposto detectar. No caso dos handlers a informao de controlo tambm indica se o handler baseado em site ou em threads, e se depois do handler ser executado se a operao deve ser tentada de novo.

3.6 Controlo de Recursos e Segurana


Uma aplicao considerada segura se continua a cumprir os objectivos para que foi desenvolvida, apesar de falhas intencionais nos seus componentes. O controlo de recursos bastante semelhante tolerncia a falhas. A principal diferena diz respeito ao tipo de falhas que so detectadas e tratadas, isto , no caso do controlo de recursos, as falhas so malignas e intencionais. As tcnicas de deteno e replicao devem tambm ser usadas para o controlo de recursos e segurana. O controlo de recursos uma questo fundamental para a segurana porque muitas vezes so utilizadas tcnicas de sobrecarga dos recursos para provocar falhas intencionais (denial of service). Os recursos encontram-se divididos entre recursos do site e recursos da rede. Os recursos do site incluem os recursos computacionais (memria e processador) e outros recursos como sistemas de ficheiros e perifricos. Os mesmos recursos do site aparecem por vezes representados de formas distintas nas vrias camadas do site, por exemplo, o programa em Oz, o emulador, e o sistema operativo. A questes de segurana tambm so distintas em cada camada: Segurana da linguagem uma caracterstica da linguagem. implementada atravs de meios de restrio do acesso aos dados. No Oz o acesso sem restries memria no permitido, apenas permitido o acesso a dados aos quais foram atribudas referencias explicitas.

26

Segurana da implementao uma caracterstica da linguagem no processo de implementao. Protege as computaes e os dados de tentativas de interferncia com o programas compilados (Oz bytecode). As duas aplicaes mais importantes deste nvel de segurana so: na integridade do site e o no controlo de recursos. Os problemas de segurana surgem quando o cdigo passado directa ou indirectamente de um site para outro. Por exemplo, enviado um procedimento para um servidor o executar (directa), ou evocando um mtodo de um objecto mvel (indirecta). A soluo para impedir que o cdigo enviado seja malicioso a implementao de tcnicas de verificao do cdigo bytecode e o uso de autenticao a nvel da compilao. Segurana do sistema operativo e da rede no uma propriedade da linguagem, mas do sistema operativo e da rede. Protegem as computaes e os dados de serem corrompidos por tentativas de interferncia com o emulador de Oz e com o run-time system de um processo do sistema operativo, e tambm tentativas de interferncia com sistema operativo e com a rede. A segurana da rede pode ser conseguida atravs da utilizao de TCP/IP seguro.

3.6.1 Sites Virtuais


Um site pode criar vrios sites na mesma mquina, isto virtuais, que se comportam exactamente da mesma forma que os sites normais, com a excepo que o site master monitoriza e controla os slaves. Se um slave falha o master notificado. O master controla os recursos dos slaves, incluindo os recursos computacionais e outros recursos como o acesso a sistemas de ficheiros. Por exemplo, a um site slave pode ser dada a possibilidade de criar e apagar ficheiros num determinado directrio e em nenhum sitio mais. Para alm das limitaes impostas pelo master, um site slave comporta-se exactamente da mesma forma que um site normal. Pode partilhar entidades com o master ou com um site noutra localizao da rede. Como os sites virtuais residem na mesma mquina as comunicaes so mais eficientes porque no existe camada de rede. Para tirar partido da proteco e controlo de recursos do sistema operativo, um slave normalmente reside num processo diferente do master. Os sites virtuais podem ser usados para tirar partido dos recursos em mquinas de memria partilhada com multiprocessadores. Basta alocar um site a cada processador. No tendo camada de rede, o paralelismo numa mquina deste tipo, bastante mais eficiente que em rede.

27

4. Tutorial de Oz
Neste tutorial ir ser introduzida a linguagem Oz, assim como o seu ambiente de trabalho. O contedo ir incidir sobre as caractersticas fundamentais da linguagem Oz, nomeadamente sobre as caractersticas funcionais, concorrncia, objectos e classes, objectos e concorrncia e programao lgica. Num outro capitulo ir ser abordado o Oz distribudo.

4.1 O Ambiente de Desenvolvimento


O ambiente de desenvolvimento do Oz chamado de OPI (Oz Programming Interface). Este ambiente de desenvolvimento usa o Emacs, expandindo os seus menus e colorao sintctica, de forma a facilitarem a programao em Oz.

4.1.1 Arrancar o ambiente de desenvolvimento


Sob ambientes Unix, o OPI pode ser arrancado atravs do commando oz <nome do ficheiro.oz> na shell. Sob o Windows confesso que no experimentei, mas o processo simples basta instalar o Emacs para Windows, correr os ficheiros de instalao do Oz, que esto disponveis no site oficial, e ser acrescentado um grupo de programas ao menu do Windows. Eu usei o Xemacs e o aspecto do OPI o seguinte:

Figura 8 O aspecto do OPI, usando o Xemacs

28

A janela inicial est separada em dois buffers. O buffer de cima com o nome de oz, onde escrito o programa que pode ser executado interactivamente. O buffer de baixo contm as mensagens do compilador. Para trabalhar com o Emacs aconselhvel a leitura de um tutorial desta ferramenta.

4.1.2 O primeiro programa


Comecemos com o o tradicional Ol mundo, para isso basta escrever no buffer de cima o seguinte cdigo:
{Show Ol Mundo}

Isto nada mais, que a invocao de um procedimento em Oz, isto , a chaveta significa que se vai chamar o procedimento Show com o argumento Ol mundo. Para se poder executar este primeiro programa, basta ir ao menu Oz no Emacs e escolher feed line para que o compilador compile a linha.

Figura 9 Compilao do Ol mundo

No buffer do compilador aparece uma mensagem que indica que a linha compilada foi aceite pelo compilador. Embora possa parecer estranho no termos nenhum resultado aparente, mas o que se passa efectivamente que o resultado encontra-se noutro buffer chamado Oz Emulator. Se escolhermos a opo do menu Oz Show Hide e de seguida Emulator, veremos o resultado.

29

Figura 10 O buffer Oz Emulator contendo o resultado da execuo.

4.1.3 Principais caractersticas do OPI


Edio de Cdigo O OPI contem um modo de edio Oz que adicionado ao Emacs, e consiste em indentao automtica e colorao do sintaxe do Oz. Teclas de Atalho Todas as aces de interaco com o Oz podem ser atribudas a teclas de atalho, por exemplo: Ctr +. Ctr +. Ctr +. M + Ctr + x Ctr +. Ctr +. Ctr +.
Tabela 4 Teclas de atalho do OPI

Ctr +l Ctr +r +b

Compila a linha corrente

Compila a regio seleccionada Ctr Compila o buffer Compila o pargrafo (limitado por linhas vazias) Ctr Compila o pargrafo

+p c e Mostra o buffer do compilador Mostra o Emulator Buffer

Erros do Compilador O OPI tem mecanismos bastante simples para procurar e indicar os erros de compilao. A melhor maneira de perceber como funciona atravs de um exemplo. Se introduzirmos o seguinte cdigo com erros:
Local A B in A = 3 Proc {B} {Show A + Teste } end {B 7} end

30

Ao compilar sero reportados dois erros como se pode verificar.

Figura 11 Erros na compilao

Se for primido Ctr + x e de seguida `, o cursor no buffer do programa ir deslocar-se para a posio onde foi detectado o primeiro erro.

Figura 12 Localizao do primeiro erro no buffer do programa

O erro diz-nos que obviamente um inteiro no pode ser adicionado a um

31

tomo. Se for primido de novo Ctr + x e depois `, o cursor vai posicionar-se no prximo erro do buffer do programa.

Figura 13 Segundo erro encontrado no programa

Este erro sucedeu porque o procedimento B no leva argumentos. O Browser O Oz tem bastantes ferramentas grficas de apoio ao desenvolvimento. O browser uma ferramenta que serve para obter uma visualizao dos resultados da execuo de um programa atravs de um interface grfico. Se compilarmos a seguinte linha:
{Browse Ol mundo}

Iremos obter o seguinte:

Figura 13 O browser

Compilemos o seguinte cdigo:


declare W H in {Browse foo(comprimento:W altura:H area :thread W*H end)}

O browser ir mostrar os nomes das vaiveis porque estas no se encontram instanciadas e no caso da rea ir mostrar _ porque no conhecendo o valor das variveis no pode efectuar o calculo.

32

Figura 14 O browser mostrando variveis no instanciadas

Se igualarmos o W a 3, o browser, ser automaticamente refrescado e colocar o valor 3 em vez de W.

Figura 15 O browser mostrando uma varivel no instanciada e outra instanciada.

Finalmente se instanciarmos H com o valor 5, iremos obter o resultado da rea porque a variveis que esse calculo depende esto todas instanciadas.

Figura 16 O browser mostrando varivel instanciada.

O browser permite-nos ver a evoluo da instanciao de termos ou variveis ao longo da execuo de computaes concorrentes (threads).

33

4.2 Introduo programao em Oz


A melhor maneira de comear a programar em Oz comear pela programao sequencial. Segundo esta abordagem, uma computao pode ser entendida como um conjunto de instrues executadas sequencialmente. A este conjunto de instrues d-se o nome de thread. A thread pode aceder a uma rea de armazenamento para manipular informao, isto , para ler, adicionar ou actualizar dados. O acesso informao feito a partir de variveis visveis thread, quer directa ou indirectamente. As variveis em Oz so variveis lgicas, isto s podem ser instanciadas um vez. Este tipo de variveis bastante usada em linguagens lgicas com por exemplo no Prolog. O uso de variveis que no possam ser alteradas, no implica que no possam representar estados, pois podem ser atribudas a clulas. As clulas podem ser modificadas. A expresso:
local X Y Z in S end

Define as variveis lgicas X,Y e Z dentro do espao lxico S. Ou seja S corresponde a um conjunto de instrues onde essas variveis podem ser acedidas. As variveis comeam por uma letra maiscula e seguidas de caracteres alfanumricos. As variveis tambm podem ter a forma de uma string limitada por apstrofos. Imediatamente depois de serem declaradas as variveis no tm nenhum valor associado, dizem-se no instanciadas. Qualquer varivel em Oz deve ser definida antes de ser utilizada excepto, como veremos mais tarde em algumas situaes no pattern matching. Outra forma de definir variveis :
declare X Y Z in S

A diferena est relacionada com a rea de aco das variveis. Em vez de estarem definidas num espao limitado pela palavra chave end, neste caso atravs do declare, as variveis iro ter um alcance global, a no ser que existam blocos que entretanto definam variveis como o mesmo nome.

34

4.3 Os tipos bsico de dados


Int Number Float Register Tuple Pro edure Array Cell Valor Chunk Space BitArray Thread Class ByteString Object BitString Lo k Port Dictionary Literal Name Unit Atom Bool FDInt Char

Figura 17 Os tipos de dados do Oz

A figura 17, mostra a hierarquia que existe entre os tipos de dados no Oz. Qualquer varivel pode instanciar valores de qualquer dos tipos representados. A maior parte dos tipos so comuns noutras linguagens de programao com a excepto os Chunk, Cell, Space, FDInt e Name. Ao longo deste tutorial iro ser explicados na devida altura. O tipo da varivel determinado quando instanciada, isto , dependendo do valor que se lhe atribuir, assim ser o seu tipo.

4.3.1 Instanciao de variveis


A forma mais comum de se instanciar uma varivel atravs do operador de igualdade =. Se tivermos uma varivel X e a quisermos instanciar com o valor 1, poderia ser feito da seguinte maneira:
local X in X=1 end

Se a varivel X j estivesse instanciada com o valor 1, esta operao seria interpretada com um teste varivel X. Se X fosse instanciada com um valor diferente de 1, seria levantada a excepo correspondente.

4.3.2 Nmeros
O Oz suporta nmeros inteiros e de virgula flutuante. Os inteiros tm um preciso infinita, isto , podem tem um qualquer tamanho. A representao dos valores inteiros pode ser feita utilizando vrias notaes, nomeadamente a notao binria, octal, decimal e hexadecimal. Para representar um octal, nmero deve ser precedido de um O, para representar um hexadecimal o nmero deve ser precedido de 0x. Os caracteres so inteiros entre 0 e 255 e podem ter uma

35

representao sintctica, por exemplo o caracter t pode ser representado por &t, ou nova linha pode ser representado por &\n. Os nmeros de virgula flutuante distinguem-se dos inteiros por terem um ponto decimal. O sinal menos representado por ~. Vejamos alguns exemplos: ~3.141 4.5E3 ~12.0e~2 No Oz no existe converso automtica de tipos, isto , 5.0 = 0 ir levantar uma excepo. Obviamente existem mtodos de converso de tipos para permitir este tipo de comparaes. As operaes sobre nmeros encontram-se no mdulo Number.

4.3.3 Literais
Existem dois tipos de literais: os tomos e os nomes. O tomo um entidade simblica representada por um conjunto de caracteres, em que o primeiro caracter minsculo, ou ento, quaisquer caracteres limitados por apstrofos. Por exemplo:
a foo = := Oz 3.0 Ola Mundo

Os tomos possuem uma ordenao baseada num ordem lexicogrfica. Outro tipo de literais so os nomes. Os nomes consistem em sequncias nicas de caracteres. A criao de um nome feita usando o procedimento {NewName X}, onde X ficar com o nome gerado. Os nomes gerados no podem ser forjados ou visualizados e so normalmente usados para questes relacionadas com segurana. Um subtipo particular dos nomes so os valores booleanos, que consistem em dois nomes protegidos, isto , no podem ser redefinidos e so representados pelas palavras reservadas true e false. O tipo unit tambm um nome, que usado para sincronizao em programao concorrente. O cdigo seguintes mostra a definio destes tipos de dados.
local X Y B in X = foo {NewName Y} B = true {Browse [X Y B]} end

4.3.4 Registos e Tuplos


Os registos so entidades estruturadas compostas. So constitudos por um identificador e um nmero fixo de componentes ou argumentos. Tambm existem registos com um numero varivel de componentes que so chamados de registo abertos. Vejamos o exemplo da definio de um registo:
tree(chave:K valor:V esquerda:LT direita:RT)

Neste caso o registo tem quatro argumentos e um identificador tree. Cada argumento constitudo pelo par propriedade:campo. No exemplo as propriedades so: chave, valor, esquerda e direita. Os campos so K,V,LT e RT. Se omitirmos as

36

propriedades teremos uma estrutura que normalmente designada por tuplo.


tree(K V LT RT)

exactamente o mesmo que ter o seguinte registo:


tree(1:K 2:V 3:LT 4:RT)

Vejamos o seguinte exemplo:


declare T K V LT RT W in T = tree(chave:K valor:V esquerda:LT direita:RT) K = pedro V = 13 LT = nil RT = nil W = tree(K V LT RT) {Browse [T W]}

O resultado ser :
[tree(chave:pedro valor:13 esquerda:nil direita:nil) tree(pedro 13 nil nil)]

Grande parte das operaes que podem ser executadas sobre os registos encontram-se no modulo Record. Vejamos agora algumas operaes bsicas que podem ser feitas sobre estas estruturas de dados. Para seleccionar um campo de uma determinada propriedade de um registo usa-se o operador infixo ponto (.). Por exemplo:
{Browse T.chave} % mostra pedro no ecr {Browse W.1} % tambm mostra pedro no ecr

O arity de um registo a lista das suas propriedades ordenadas lexicograficamente. O procedimento {Arity R X} obtm o arity do registo R e colocao na varivel X. Por exemplo:
local X in {Arity T X} {Browse X} end local X in {Arity W X} {Browse X} end

O resultado ser :
[chave valor esquerda direita] % no primeiro caso [1 2 3 4] % no caso do tuplo

Outra operao que pode ser bastante til a seleco condicional de um campo de um registo. O procedimento CondSelect recebe um registo R, a propriedade F, um valor de campo por omisso D e o resultado colocado num ultimo argumento X. Funciona da seguinte maneira: Se a propriedade F existe em R ento X fica com o valor de R.F, seno X fica com o valor de D.
local X in {CondSelect W chave omissao X} {Browse X} end local X in {CondSelect T chave omissao X} {Browse X} end

Na primeira expresso, como no existe a propriedade chave, porque W um tuplo, o resultado ser omissao. Na segunda expresso, o registo T possui uma propriedade chave, ento X fica com o valor da chave que pedro.

37

Um operador bem comum na utilizao dos tuplos o #. Este operador define um tuplo com dois ou mais elementos. Por exemplo a expresso 1#2#3 define o tuplo #(1 2 3). Se desejarmos criar tuplos com um ou sem elementos, devemos defini-lo da forma habitual, ou seja: # (X) para um elemento ou #() para um tuplo sem elementos. A operao {AdjoinAt R1 F X R2} instancia R2 com o registo resultante da adio a R1 da propriedade F e do campo X. Se a propriedade F j existir em R1 em R2 aparecer com o valor de X. A operao {AdjoinList R LP S} recebe um registo R, uma lista de pares propriedade campo, e retorna um novo registo S tal que: o identificador de R o mesmo de S; Todas as propriedades que no existam em R so adicionados a partir da lista LP, caso existam, o valor que ir passar para S ser o que est definido no par propriedade campo na lista LP. Vejamos um exemplo:
local S in {AdjoinList tree(a:1 b:2) [a#3 c#4] S} {Show S} end % O resultado seria S=tree(a:3 b:2 c:4)

4.3.6 Listas
As listas no pertencem a um tipo de dados no Oz. So uma estrutura conceptual. Podem ser representadas de vrias maneiras: o tomo nil representa uma lista vazia; por elementos separados pelo operador | em que o elemento da esquerda a cabea da lista e o da direita a cauda; uma lista fechada pode ser representada entre parnteses recto com os valores separados por espaos; ou pela notao de registos. Vejamos os exemplos:
1|2|3|nil % lista com os valores 1 2 e 3 [1 2 3] % lista fechada com os valores 1 2 e 3 1|2|X % lista com os dois primeiros elementos e X instancia a cauda |(1 |(2 X))

Existe outra notao para um tipo especial de listas que so as cadeias de caracteres ou strings. As strings so representadas por um conjunto de caracteres limitados por aspas. Por exemplo a string:
oz 3.0

convertida na lista:
[79 90 32 51 46 48] ou [&o &z & &3 &. &0]

38

4.3.7 Strings Virtuais


Uma string virtual um tuplo especial que representa concatenao virtual, isto a concatenao s efectuada quando necessrio. As strings virtuais podem ser usadas em ficheiros, sockets e janelas. Todos os tomos podem ser usados excepto o nil e o #. O operados # pode ser usado para criar as strings virtuais. Por exemplo o seguinte tuplo:
123#-#23# = #100

Resulta na string:
123-23 = 100

Nota: Recomenda-se a consulta do manual, pois associado a cada tipo de dados existe um mdulo da linguagem com vrias operaes, entre as quais converses de tipos.

4.4 Igualdade e Teste de Igualdade


At agora j foi visto como se instancia uma varivel usando o operador de igualdade. No entanto, convm entender o que est por de trs de uma atribuio ou instanciao. Quando por exemplo X igualado a Y (X=Y). Existe uma rea de armazenamento que consiste num array dinmico em que cada vez que definida uma varivel acrescentada uma posio a esse array contendo o valor unknown (desconhecido). Uma posio do armazenamento contendo este valor corresponde a uma varivel no instanciada. Quando se tenta igualar duas variveis, o que realmente se passa uma fuso das duas variaveis, isto , a operao X=Y, tentar fundir os ns correspondentes na rea de armazenamento. Este processo chamado de Incremental Tell ou operao de unificao. Se X e Y referencia o mesmo n na rea de armazenamento. A operao executada com sucesso Se o n X for no instanciado e Y estiver instanciado, todas as referencias a X so substitudas por Y e X descartado Processo de fuso. Se X e Y referenciam ns diferente na rea de armazenamento contendo os registo Rx e Ry respectivamente: Se Rx e Ry tiverem diferentes identificadores de registo, diferentes aritys, ou ambas as situaes completada a operao e levantada uma excepo. Se no, os argumentos de Rx e Ry com o mesmo atributo so fundidos.

39

Na fuso de dois grafos (Registos), podem existir ciclos. No entanto a operao de fuso guarda informao sobre os pares, aos quais foi feita uma tentativa de fuso anterior, permitindo que a operao seja correctamente executada. Quando uma varivel deixar de ser acessvel os mecanismos de garbage collection iro libertar o n que ela referenciava. Vejamos algumas algumas operaes de igualdade (unificaes) vlidas:
local X Y Z in f(1:X 2:b) = f(a Y) f(Z a) = Z {Browse [X Y Z]} end % O resultado ser [a b R14=f(R14 a)] no browser. % R14 = f(R14 a) um grafo cclico

Nota: Para poder ver a representao finita de Z (grafo cclico), ter de ser seleccionado no Browser, o menu Option, Representation field e depois escolher Minimal Graph.
O prximo exemplo mostra uma operao de igualdade errada:
local X X Y X End Y = = = Z in f(c a) f(Z b) Y

Note-se que a atribuio de c a Z correcta, mas quando tenta atribuir, a a b, levantada um excepo.

4.4.1 Teste de Igualdade


O operador de teste de igualdade o ==. Vejamos como funciona o teste de igualdade partindo de um exemplo:
local X X=1 {Show {Show {Show {Show end Y in X==1} %% X==2} %% X==f(a b)} %% Y==1} %% escreve escreve escreve No faz true false false nada

Quando se verifica uma igualdade o resultado ser true. Caso no se verifique o resultado ser false. Existe um terceira situao que quando uma varivel no instanciada testada. Neste caso, como a igualdade no pode ser testada, a thread bloqueia at conhecer o valor da varivel. Quando duas estruturas (tuplos, registos) so testadas, pode passar-se o seguinte: Retorna true se os grafos gerados tiverem a mesma estrutura e se cada par propriedade campo corresponder a valores idnticos ou ao mesmo n (unificao).

40

Retorna falso se as estruturas forem distintas ou existir alguma desigualdade nos pares propriedade campo. Suspende a execuo quando faz o teste de um par com outro no instanciado. importante referir que os tipos de dados que foram discutidos at agora possuem igualdade estrutural. Vejamos alguns exemplos:
5 == 5.0 %% retorna false porque numeros de virgula flutuante e inteiros nunca so iguais f(A B) == f(C D) %% retorna true se A==C e B==D

local X Y in {Show f(a {Show f(a {Show f(a {Show f(a end

b)==f(a b)==f(a b)==f(c _)==f(a

b)} c)} d)) b)}

%% %% %% %%

escreve true escreve false escreve false suspende!!!

4.5 Estruturas Bsicas de Controlo e Procedimentos


O execuo de um programa em Oz feita sequencialmente, isto , executada uma sequncia de instrues:
S1 S2 S3 Sn

Apesar da execuo das thrads ser sequencial, existe uma grande diferena das linguagens convencionais. As threads podem ser suspensas, o que significa que por exemplo se a execuo for suspensa na execuo de S1, S2 s ser executada quando forem satisfeitas as necessidades de dados que S1 tem retomando a execuo. Pode acontecer que S2 nem seja executado caso S1 levante uma excepo.

4.5.1 A instruo vazia


A instruo de Oz correspondente instruo vazia o skip.

4.5.2 A instruo condicional if


A instruo condicional if funciona de forma bastante semelhante a todas as outras linguagens de programao, e tem o seguinte sintaxe:
If B then S1 else S2 end

Em que B um valor booleano, ou expresso que resulte um valor booleano.

41

A semntica da instruo if a seguinte: Se B for true a instruo S1 executada Se B for false a instruo S2 executada Se B for um valor no booleano levantada uma excepo Se B for uma varivel no instanciada a thread suspende. Os operadores disponveis para fazer comparaes so os seguintes:
== \= =< => > < Igual a Diferente de Menor ou igual a Maior ou igual a Maior que Menor que
Tabela 5 Operadores booleanos

Exemplo:
declare X Y Z X = 5 Y = 10 if X >= Y then Z = X else Z = Y end

Abriviaes: Em Oz existe a palavra reservada elseif: Pode-se escrever o seguinte cdigo:


if B1 then S1 elseif B2 then S2 else end

Em vez de:
if B1 then S1 else if B2 then S2 else S3 end end

Pode-se escrever a instruo if sem a parte do else:


if B1 then S1 end

equivalente a:
if B1 then S1 else skip end

42

4.5.3 Procedimentos
Os procedimentos podem ser passados a outros procedimentos ou atribudos a registos ou a variveis (first-class), e so definidos da seguinte maneira:
declare proc {P X1 Xn} S end

So chamados da seguinte forma:


{P Y1 Yn} % Y1 ... Yn so os argumentos

Passados como argumento


{Q P 1}

Guardados numa qualquer estrutura de dados


X=rec(P)

Vejamos um exemplo:
declare Max X Y Z proc {Max X Y Z} if X >= Y then Z = X else Z = Y end end X = 5 Y = 10 {Max X Y Z} {Show Z} %% escreve 10

Os procedimentos no retornam valores directamente. Para retornar valores, basta definir como argumentos que iro ser instanciadas dentro do procedimento com os resultados. Quando o procedimento for chamado no lugar desses argumentos colocam-se variveis no instanciadas. Depois de executada a chamada essas variveis iro conter os valores retornados. Por exemplo:
{Max In1 In2 Return}

Vejamos um exemplo de um procedimento que retorna dois valores:


declare Two F S proc {Two In First Second} First=In.1 Second= In.2 end {Two f(a b) F S} % F instanciado com a, S com b

recomendado usar as convenes do Oz, ou seja, os argumentos que retornam valores devem sempre ser retornados ao fim, e sempre que quisermos definir um procedimento que apenas retorne um valor, devemos usar uma funo em vez de um procedimento.

43

fun {Max X Y} if X >= Y then X else Y end end

proc{Max X Y Aux} if X >= Y then Aux=X else Aux=Y end end

Figura 18 Funo vs. Procedimento

Os procedimentos como so first-class, podem ser atribudos a qualquer entidade do Oz. Para isso utiliza-se uma notao designada de procedimentos annimos. O smbolo $ usado para indicar que um determinado procedimento annimo.
P = proc{$ X1 ... Xn} S end

Neste caso, a varivel P ir ser instanciada com um procedimento. Esta varivel pode ser utilizada da mesma forma que qualquer outra varivel lgica e pode tambm ser utilizada para chamar o procedimento utilizando a forma j conhecida de:
{P X1 ... Xn}

Sendo estas variveis que instanciam procedimentos tratadas de igual modo s outras, podem surgir algumas questes. Por exemplo no que diz respeito ao teste de igualdade. Vejamos um exemplo:
declare proc {Max X Y Z} if X >= Y then Z = X else Z = Y end end proc {Max2 X Y Z} if X >= Y then Z = X else Z = Y end end proc {Min X Y Z} if X >= Y then Z = Y else Z = X end end Alias=Max {Show Max==Min} %% escreve false {Show Max==Max2} %% escreve false {Show Max==Alias} %% escreve true

S a ultima igualdade verdadeira porque a nica que possui igualdade estrutural. Vejamos um exemplo que ilustra como podemos passar directamente o resultado de um procedimento a outro procedimento:
{Show {Max3 1 5 4 $}} %% mostra 5

Se em vez de se colocar uma varivel no instanciada no argumento de retorno, se colocar o smbolo $, o valor automaticamente passado ao procedimento Show. Vejamos agora um exemplo da atribuio de procedimentos a estruturas de

44

dados.
declare fun {Max A B} end fun {Min A B} end MyMathModule=math(max:Max min:Min) declare Max Min {MyMathModule.max 1 3 Max} %% Max=3 {MyMathModule.min 1 3 Min} %% Min=1

Finalmente, um exemplo de uma funo que recebe uma funo (Less) e que retorna uma funo que no programa atribuda a P.
declare fun {GenMax Less} fun{$ A B} if {Less A B} then B else A end end end P={GenMax fun{$ A B} A<B end} {Show {P 5 3}} %%% Shows 5

4.5.4 Lexical Scoping


Num conjunto de instrues S, algumas ocorrncias das variveis esto ligadas sintacticamente, isto , s fazem sentido dentro do espao desse conjunto de instrues, outras esto livres. Uma ocorrncia de uma varivel X est ligada sintacticamente se: Se estiver dentro da definio de um procedimento sendo X um argumento. Se estiver dentro do espao definido pelas instrues de declarao de variveis (por exemplo: local). Caso contrrio, encontra-se livre. As ocorrncias de variveis livres so eventualmente instanciadas na pela instruo de atribuio mais prxima.

4.5.5 Semntica dos procedimentos


Para o procedimento definido por:
P = proc{$ X1 ... Xn} S end

A varivel P deve ter sido definida anteriormente. A expresso anterior ir dar origem uma expresso lambda seguinte:

45

(X1 Xn).S.

P instanciado com esta abstraco ou clausula que contem o cdigo e referncias s entidades referenciadas pelas variveis livres dentro do procedimento.

4.5.6 Inicializao de variveis


Como j vimos anteriormente as variveis ou registos so inicializados atravs do operador = e definidas atravs da instruo local ou declare. O que ainda no vimos foi que as variveis tambm podem ser inicializadas no momento da sua definio. Apenas as variveis correspondentes ao lado direito das expresses so inicializadas. Vejamos um exemplo.
local Y = 1 in local M = f(M Y) [X1 Y] = L L = [1 2] in {Show [M L]} end end

A varivel Y definida no primeiro local invisvel dentro da instruo local interior. O resultado que ir resultar ser o seguinte:
[R1=f(R1 2) [1 2]]

Como L igualado a [1 2], a X1 consequentemente atribudo o valor do primeiro elemento de L e a Y o segundo. Logo o tuplo cclico M, ter o seu segundo elemento igualado a 2. Este conjunto de instrues e semelhante a ter:
local Y in Y = 1 local M X1 Y L in M = f(M Y) L = [X1 Y] L = [1 2] {Show [M L]} end end

O smbolo ! pode ser utilizado para suprimir a inicializao de uma varivel. Vejamos o seguinte exemplo:

46

local Y = 1 in local M = f(M Y) [X1 !Y] = L L = [1 2] in {Show [M L]} end end

Neste exemplo a inicializao de Y suprimida, logo Y ficar com o valor atribudo no espao lxico exterior, que 1. semelhante a ter :
local Y in Y = 1 local M X1 L in M = f(M Y) L = [X1 Y] L = [1 2] {Show [M L]} end end

Repare-se que neste exemplo, no segundo local no definida a varivel Y, o que significa que o valor que Y ir ter dentro dessa rea, ser o valor que Y tem na rea exterior, ou seja 1.

4.5.7 Pattern Matching


A implementao de reconhecimento de padres (pattern matching), feita utilizando a instruo case. O seu sintaxe o seguinte:
case E of Pattern1 then S1 [] Pattern2 then S2 [] ... else S end

As variveis introduzidas (que ainda no foram declaradas) nos patterns, tm um espao de aco dentro das respectivas instrues (S1,S2). Imaginemos que E comparado com uma expresso V. A semntica da instruo case a seguinte: A comparao de V com os patterns feita de modo sequencial pela ordem que foram definidos. O teste de comparao de V com um pattern feita de esquerda para a direita e utilizando o critrio do primeiro em profundidade. Se V for comparado com um pattern sem que nenhuma varivel seja instanciada a instruo correspondente ser executada. Se V for comparado com um pattern e apenas algumas das variveis forem instanciadas a thread fica suspensa. Se a comparao de V com um pattern falha, V ser comparado com o prximo pattern, caso todos falhem ser executada a instruo relativa ao else. A parte do else pode ser omitida, mas caso todos os patterns falhem

47

ser levantada uma excepo. Pode ser suprimida a introduo de uma nova varivel local usando o operador ! da seguinte forma:
case f(X1 X2) of f(!Y Z) then ... else ... end

Vejamos este exemplo que consiste num procedimento que serve para introduzir valores numa rvore binria.
proc {Insert Key Value TreeIn ?TreeOut} case TreeIn of nil then TreeOut = tree(Key Value nil nil) [] tree(K1 V1 T1 T2) then if Key == K1 then TreeOut = tree(Key Value T1 T2) elseif Key < K1 then T in TreeOut = tree(K1 V1 T T2) {Insert Key Value T1 T} else T in TreeOut = tree(K1 V1 T1 T) {Insert Key Value T2 T} end end end

O smbolo ? serve para indicar que se trata de um argumento de retorno (output).

48

4.5.8 Aninhamento de instrues


O aninhamento (nesting) um processo que consiste em colocar instrues dentro de outras instrues. Para se entender este conceito mais facilmente vejamos o seguinte exemplo:
% So definidas as seguintes variveis transitrias local T0 T1 T2 T3 in {Insert a 3 nil T0} {Insert b 15 T0 T1} {Insert c 10 T1 T2} {Insert d 7 T2 T3} end

Como o Oz possui sintaxe para chamar procedimentos dentro de procedimentos, pode-se substituir a seguinte expresso
local Y in {P ... Y ...} {Q Y ... } end

por
{Q {P ... $ ...} ... }

O smbolo $ indica que o valor que est nesse argumento, o valor que passado ao procedimento exterior. No caso do nosso exemplo, o conjunto de chamadas ao procedimento insert poderia ser substitudo por:
{Insert d 7 {Insert c 10 {Insert b 15 {Insert a 3 nil}}}}}

O aninhamento tambm pode ser aplicado a listas, tuplos ou registos. Por exemplo:
Zs = X|{SMerge Xr Ys $}

Que substitui a seguinte expresso:


local Zr in Zs = X|Zr {SMerge Xr Ys Zr} end

Vejamos um exemplo de um procedimento que faz a fuso entre duas listas:


proc {SMerge Xs Ys Zs} case Xs#Ys of nil#Ys then Zs=Ys [] Xs#nil then Zs=Xs [] (X|Xr) # (Y|Yr) then if X=<Y then Zs = X|{SMerge Xr Ys $} else Zr in Zs = Y|{SMerge Xs Yr $}

49

end end end

4.5.9 Procedimentos como valores


O procedimento BinaryTree verifica se uma determinada estrutura ou no uma rvore binria. O argumento B ser instanciado com true ou false se a estrutura for ou no uma rvore binria.
local proc {And B1 B2 ?B} if B1 then B = B2 else B = false end end in proc {BinaryTree T ?B} case T of nil then B = true [] tree(K V T1 T2) then {And {BinaryTree T1} {BinaryTree T2} B} else B = false end end end

A chamada {And {BinaryTree T1}{BinaryTree T2} B} vai fazer trabalho desnecessrio, isto , atendendo s regras do aninhamento, a segunda expresso vai ser avaliada mesmo que a primeira seja falsa. O que deve ser feito para que isso no suceda definir um procedimento AndThen, que recebe como argumentos dois procedimentos, onde s executa o segundo se o primeiro retornar true. Este processo chama-se Lazy e s possvel porque os procedimentos em Oz, assim como as outras entidades so High-Order.
local proc {AndThen BP1 BP2 ?B} if {BP1} then B = {BP2} else B = false end end in proc {BinaryTree T ?B} case T of nil then B = true [] tree(K V T1 T2) then {AndThen proc{$ B1} {BinaryTree T1 B1} end proc{$ B2} {BinaryTree T2 B2} end B} else B = false end end end

50

4.5.10 Abstraces de controlo

J existem definidas em vrios mdulos vrias abstraces de controlo, por exemplo no mdulo Control ou no mdulo List. O facto de os procedimentos em Oz serem High-Order pode ser aproveitado para criar abstraces de controlo. Por exemplo podemos facilmente criar uma abstraco para percorrer todos os elementos de uma lista, aplicando a cada elemento um procedimento P.
proc {ForAll Xs P} case Xs of nil then skip [] X|Xr then {P X} {ForAll Xr P} end end

O Oz possui uma instruo para iteraes.


for <iteradores> do <S> end

Onde os iteradores so: X in L itera sobre uma lista L. X in I..J itera sobre os inteiros, de I at J inclusive. X in I..J;K itera sobre os inteiros, de I at J inclusive, com incrementos de K Exemplo:
for X in [1 2 3] do Show end

ou utilizando o nosso exemplo inicial:


{ForAll [1 2 3] Show}

Vejamos agora outro exemplo de iterao:


local proc {HelpPlus C To Step P} if C=<To then {P C} {HelpPlus C+Step To Step P} end end proc {HelpMinus C To Step P} if C>=To then {P C} {HelpMinus C+Step To Step P} end end in proc {For From To Step P} if Step>0 then {HelpPlus From To Step P} else {HelpMinus From To Step P} end end end {For 3 7 2 proc{$ X} {Show X} end} %% ou alternativamente

51

for X in 3..7;2 do {Show X} end

4.6 Tratamento de Excepes


Para levantar uma excepo definida pela expresso E, utiliza-se a seguinte expresso:
raise E end

Vejamos um exemplo:
proc {Eval E ?R} case E of plus(X Y) then {Browse X+Y} [] times(X Y) then {Browse X*Y} else raise illFormedExpression(E) end end end

O tratamento de falhas baseado no conjunto de instrues try e catch, e so usadas de forma semelhante a outras linguagens. O sintaxe o seguinte:
try S catch Pattern1 then S1 Pattern2 then S2 Patternn then Sn end

A semntica a seguinte: Se no for levantada nenhuma excepo a instruo S executada normalmente. Se S levanta uma excepo, essa excepo comparada com os pattens. Se alguma dessas comparaes for verificada executada a instruo correspondente. Se a comparao com os patterns no se verificar, a excepo e propagada para fora da instruo try catch e eventualmente tratada pelo sistema Exemplo:

52

try {ForAll [plus(5 10) times(6 11) min(7 10)] Eval} catch illFormedExpression(X) then {Browse ** X **} end

Na conjunto try catch tambm podemos garantir que uma determinada instruo seja executada sendo ou no sendo levantada uma excepo. Isto pode ser feito atravs da palavra finally, que depois de ser executada a instruo correspondente a excepo levantada, ou se nenhuma excepo for levantada, depois de ser executada a instruo sob a palavra try, ser executada a instruo indicada a seguir a esta palavra.
try S catch Pattern1 then S1 Pattern2 then S2 Patternn then Sn finally Sfinal end

Vejamos um exemplo: Assumindo que F um ficheiro aberto, o procedimento Process/1 manipula o ficheiro e o procedimento CloseFile/1 fecha o ficheiro. Este programa garante que o ficheiro seja fechado quer seja ou no levantada uma excepo.
try {Process F} catch illFormedExpression(X) then {Browse ** X **} finally {CloseFile F} end

4.6.1 As excepes do sistema


As excepes levantadas pelo Oz so registos que podem ter os seguintes identificadores: failure, error, e system. failure Indica uma tentativa de execuo de uma igualdade invlida. error Indica um erro de runtime que no deveria ter sucedido, pode ser consequencia por exemplo da adio de um inteiro a um tomo. system Indica um erro de runtime a nvel interno do sistema do Oz, pode surgir devido a um ficheiro ou janela fechados imprevisivelmente, ou devido a falhas de comunicao entre processos.

53

proc {One X} X=1 end proc {Two X} X=2 end try {One}={Two} catch failure(...) then {Show caughtFailure} end

O pattern failure(...) ir capturar qualquer excepo cujo identificador seja failure. A excepo no tratada, apenas escrita no emulator-buffer a mensagem de erro correspondente e depois termina a execuo. Numa aplicao compilada a mensagem aparecer no standard error e depois termina a execuo. Este comportamento pode ser alterado para algo mais desejvel para algumas aplicaes, por exemplo para aplicaes com tolerncia a falhas.

4.7 Mdulos e Interfaces

Os mdulos so componentes de software first-class que consistem num conjunto de entidades Oz (procedimentos, objectos, e outros valores) que so agrupados para disponibilizar certas funcionalidades. Um mdulo constitudo por um conjunto de entidades privadas, isto , que no so visveis do exterior, e por um interface que possibilita o acesso s funcionalidades do mdulo. O lexical scoping e certas estruturas de dados como por exemplo os registos facilitam a construo destes componentes.
declare local proc proc proc proc in List List {Append ... } ... end {Partition ... } ... end {Sort ... } ... {Partition ...} ... end {Member ...} ... end = 'export'(append: Append sort: Sort member: Member ... )

end

Pode-se aceder s funcionalidades da seguinte forma:


{Browse {List.append [1 2 3] [3 4]}}

Outra forma de criar mdulos usando functors. Os functors especificam a criao de um mdulo partindo de outros mdulos (import), especificando o seu interface (export), e a definio das suas funcionalidades (define). O functor correspondente ao exemplo anterior seria:
functor export append:Append sort:Sort member:Member define proc {Append ... } ... end proc {Partition ...} ... end proc {Sort ... } ... {Partition ...} ... end

54

proc {Member ...} ... end end % guardado em /home/person/List.oz % compilado no ficheiro /home/person/List.ozf

Como os functors so entidades first-class podem ser definidas da seguinte maneira:


define functor LF export append:Append sort:Sort member:Member define proc {Append ... } ... end proc {Partition ...} ... end proc {Sort ... } ... {Partition ...} ... end proc {Member ...} ... end end declare [List]= {Module.link [LF]}

Os mdulos podem ser construdos a partir dos functors atravs da seguinte expresso:
declare [List]= {Module.link [/home/person/list.ozf]}

Quando o functor referenciado so executadas as instrues entre o define e o end. Como j foi dito anteriormente podem-se criar functors partindo de outros functors. Para importar os functors basta usar a palavra chave import na definio do functor da seguinte maneira:
functor import Browser FO at 'file:///home/person/FileOperations.ozf' define {Browser.browse {FO.countLines '/etc/passwd'}} end

55

4.8 Concorrncia
Como j foi dito anteriormente, existe uma rea de armazenamento onde toda a informao guardada monotnicamente, isto , apenas adicionada. As threads adicionam informao a esta rea atravs da operao tell e testam a informao atravs da operao ask. Quando as threads testam a informao podem ficar suspensas. Depois de serem lanadas, as thrads alternam entre os estados de execuo e suspenso um numero arbitrrio de vezes at terminarem. Conceptualmente a rea de armazenamento est sempre a crescer, mas existem mecanismos de garbage collection (recolha de lixo), que removem a informao que deixou de ser acessvel. Em Oz uma thread pode ser lanada da seguinte maneira:
Thread S end

As threads so executadas de forma concorrente. Todas as threads em execuo que no esto bloqueadas -lhes eventualmente alocada um tempo de processamento. Isto significa que as threads so executadas de modo fair (justo). Cada thread uma thread de fluxo de dados (dataflow), o que significa que bloqueiam quando tm necessidade de dados.
declare X0 X1 X2 X3 in thread local Y0 Y1 Y2 Y3 in {Browse [Y0 Y1 Y2 Y3]} Y0 = X0+1 % inicialmente a thread suspende aqui Y1 = X1+Y0 Y2 = X2+Y1 Y3 = X3+Y2 {Browse completed} end end {Browse [X0 X1 X2 X3]}

Se forem inseridos um de cada vez os seguintes valores:


X0 X1 X2 X3 = = = = 0 1 2 3

A thread ir suspender na linhas seguinte medida que as suas necessidades de dados vo sendo preenchidas. Vejamos agora um exemplo:
fun {Map Xs F} case Xs of nil then nil [] X|Xr then thread {F X} end |{Map Xr F} end end

56

proc {Map Xs F Rs} case Xs of nil then Rs = nil [] X|Xr then R Rr in Rs = R|Rr thread R = {F X} end Rr = {Map Xr F} end

Se escrevermos as seguintes instrues:


declare F X Y Z {Browse thread {Map X F} end}

Uma thread executando a funo Map ser criada, mas como a varivel X no est instanciada a thread ser imediatamente suspensa. Se escrevermos :
X = 1|2|Y fun {F X} X*X end

X ser instanciado com a lista [1 2 Y] e F com a funo X*X. Os requisitos da thread encontram-se satisfeitos e sero criadas duas novas threads para os dois primeiros elementos da lista. Como o terceiro valor da lista no est instanciado a nova thread suspende. Se escrevermos:
Y = 3|Z Z = nil

Por fim todas as necessidades de informao das threads so completadas, retomando a execuo. O resultado ser a lista [1 4 9]. O Programa que se segue, uma forma muito ineficiente de calcular sries de Fibonacci, pois cria um nmero exponencial de threads. No entanto pode ser til para verificar o nmero de thread suportados pelo sistema. Por exemplo pode-se tentar:
{Fib 20}

Se continuar a funcionar deve tentar-se um valor maior que 20. Entretanto deve ser executado o programa panel, que pode ser acedido pelo menu Oz do OPI. O objectivo determinar o limite de threads que o sistema suporta, para que os programas concorrentes possam ser planeados de uma forma mais modular.
fun {Fib X} case X of 0 then 1 [] 1 then 1 else thread {Fib X-1} end end

+ {Fib X-2} end

57

Figura 19 O Oz panel

4.8.1 Tempo
O Oz implementa algumas funcionalidades de soft real-time. Destacam-se os procedimentos: {Alarm I ?U} cria imediatamente a sua prpria thread e instancia U com unit depois de I milisegundos. {Delay I } suspende a execuo da thread por pelo menos I milisegundos.
local proc {Ping N} if N==0 then {Browse 'ping terminated'} else {Delay 500} {Browse ping} {Ping N-1} end end proc {Pong N} {For 1 N 1 proc {$ I} {Delay 600} {Browse pong} end } {Browse 'pong terminated'} end in {Browse 'game started'} thread {Ping 50} end thread {Pong 50} end end

4.8.2 Comunicao entre streams


O facto de o Oz ser uma linguagem dependente do fluxo de dados possibilita uma facil implementao de threads que comunicam segundo o modelo produtor - consumidor. Um stream uma lista que incrementada por uma thread (produtor) e consumida por uma ou vrias threads (consumidores). Todos os consumidores consomem os mesmos elementos do stream.

58

fun {Generator N} if N > 0 then N|{Generator N-1} else nil end end local fun {Sum1 L A} case L of nil then A [] X|Xs then {Sum1 Xs A+X} end end in fun {Sum L} {Sum1 L 0} end end local L in thread L = {Generator 150000} end {Browse {Sum L}} end

Este programa gera uma lista com os valores menores que um determinado nmero (150000) e soma-os. O resultado dever ser 11250075000. Um produtor vai incrementado um stream (lista), isto feito de uma forma eager, ou seja :
fun {Producer ...} ... volvo|{Producer ...} ... end

O consumidor espera que os elementos cheguem ao stream.


proc {Consumer Ls ...} case Ls of volvo|Lr then Consume volvo... end {Consumer Lr} end

Por causa do comportamento dependente do fluxo de dados enquanto no chegar a stream o prximo elemento o consumidor fica suspenso. A chamada recursiva permite o consumidor repetir o processo. No exemplo seguinte cada vez que o consumidor recebe mil automveis escreve uma mensagem.
fun {Producer N} if N > 0 then volvo|{Producer N-1} else nil end end proc {Consumer Ls} proc {Consumer Ls N} case Ls of nil then skip [] volvo|Lr then if N mod 1000 == 0 then {Browse driving a new volvo'} end {Consumer Lr N+1} else {Consumer Lr N} end end in {Consumer Ls 1} end

Este exemplo pode ser executado da seguinte forma:

59

{Consumer thread {Producer 10000} end}

4.8.3 Prioridades das Threads


Depois de executar o exemplo anterior, se verificarmos no panel o comportamento da memria, facilmente concluiremos que no um comportamento correcto. O motivo tem haver com a passagem assncrona de mensagens. Se o produtor envia mensagens, isto , cria novos elementos no stream, de uma forma mais rpida que o consumidor consegue consumir, ser necessria o recurso intenso a buffers. Uma soluo possvel para o problema atribuir ao consumidor uma prioridade maior, que corresponde a maior tempo de processador. Existem trs nvel de prioridade: Alta, mdia e baixa (por omisso). O nvel de prioridade determina a frequncia com que alocado um time-slice thread. Em Oz uma thread de maior prioridade no pode impedir que uma de menor prioridade seja executada. Todas so executadas de forma fair (Justa). Cada thread tem um nome nico. Para obter o nome da thread que est em execuo usa-se o procedimento Thread.this/1. Obtendo a referencia a uma thread, atravs do seu nome, possvel efectuar certas operaes. Estas operaes esto no modulo Thread. Por exemplo:
fun {Thread.state T} %% Retorna o estado da thread proc{Thread.injectException T E} %% Injecta a excepo E na thread T fun {Thread.this} %% Retorna a referencia thread proc{Thread.setPriority T P} %% P pode ser high, medium ou low proc{Thread.setThisPriority P} %% o mesmo que na anterior mas sobre a thread corrente fun{Property.get priorities} %% devolve as racios das prioridades proc{Property.put priorities(high:H medium:M)} %% especifica as racios das prioridades

O procedimento:
{Property.put priorities(high:X medium:Y)}

Ir colocar o racio temporal do processador em X:1 entre threads de alta prioridade e mdia prioridade, e ir tambm colocar o racio Y:1 entre as threads de mdia prioridade e baixa prioridade. X e Y so valores inteiros. Por exemplo:
{Property.put priorities(high:10 medium:10)}

Aplicando isto ao programa produtor-consumidor, poderemos resolver o problema da utilizao dos buffers. Esta expresso estabelece que as racios so 10:1 entre a alta prioridade a mdia e 10:1 entre a mdia e a baixa.

60

Vejamos a nova verso do programa produtor consumidor.


local L in {Property.put threads priorities(high:+10 medium:+10)} thread {Thread.setThisPriority low} L = {Producer 5000000} end thread {Thread.setThisPriority high} {Consumer L} end end

4.8.4 Execuo orientada ao pedido


Outra maneira de resolver o problema da sobreutilizao de memria no problema produtor-consumidor atravs de execuo orientada ao pedido (lazy), isto , o produtor apenas produz um elemento se for requerido pelo consumidor. Para isso o consumidor vai construindo o stream com variveis no instanciadas. O produtor aguarda o aparecimento destas variveis no stream, para depois lhes atribuir valores. O produtor ser algo semelhante a:
proc {Producer Xs} case Xs of X|Xr then I in Produce I X=I ... {Producer Xr} end end

E consumidor seria:
proc {Consumer ... Xs} X Xr in ... Xs = X|Xr Consume X ... {Consumer ... Xr} end

Vejamos agora o exemplo do Volvo completo:


local proc {Producer L} case L of X|Xs then X = volvo|{Producer Xs} [] nil then {Browse 'end of line'} end end proc {Consumer N L} if N==0 then L = nil else X|Xs = L in case X of volvo then if N mod 1000 == 0 then {Browse 'riding a new volvo'} end {Consumer N-1 Xs}

61

else {Consumer N Xs} end end end in {Consumer 10000000 thread {Producer $} end} end

Existe ainda outra maneira de resolver o problema. Existe outra forma de executar computaes orientadas ao pedido, atravs do uso de futures e da primitiva ByNeed. Uma future consiste na propriedade de leitura de uma varivel. Para criar a future da varivel X usa-se o smbolo !! e atribuda a Y.
Y = !!X

Uma thread quando tenta usar o valor da future, suspende at que a varivel da future seja instanciada. Pode-se executar um procedimento orientado ao pedido atravs da operao {ByNeed +P ?F}. Esta operao recebe um procedimento e retorna uma future F. Quando uma thread tenta aceder ao valor de F, o procedimento {P X} chamado e o seu resultado X atribudo a F. Por exemplo:
declare Y {ByNeed proc($ X) X = 1 end Y} {Browse Y}

O Y passa a ser uma future e ir ser instanciada com o valor 1. Outra maneira de aceder a Y, esperando que Y tenha um valor atravs da operao {Wait Y}. Vejamos o exemplo do Volvo usando ByNeed:
local proc {Producer Xs} Xr in Xs = volvo|{ByNeed {Producer Xr} $} end end proc {Consumer N Xs} if N>0 then case Xs of X|Xr then if X==valvo then if N mod 1000 == 0 then {Browse 'riding a new volvo'} end end {Consumer N-1 Xr} else {Consumer N Xr} end end end in {Consumer 10000000 thread {Producer $} end} end

4.8.5 Deteco de terminao de threads


Uma questo que pode surgir relativamente ao uso de threads , como se pode fazer uma thread principal aguardar que todas as threads lanadas a partir dela, terminem para s depois prosseguir a execuo.

62

Isto uma situao particular da deteco de terminao de threads e ter outra thread espera desse evento. Mais uma vez, sendo o Oz uma linguagem dependente do fluxo dos dados, isto pode ser feito de forma bastante simples.
thread T1 X1 = unit end thread T2 X2 = X1 end ... thread TN XN = XN-1 end {Wait XN} MainThread

Quando as threads terminam as variveis X1,X2,...,Xn so fundidas contendo o valor unit. A operao {Wait Xn} espera que a varivel Xn seja instanciada, ou seja indica que a ultima thread da sequncia foi terminada.

4.8.6 Composio concorrente


Em Oz existe uma forma de simplificar o problema anterior recorrendo ao composio concorrente. Para isso usa-se a primitiva da linguagem Conc, que recebe um nico parmetro que consiste numa lista de procedimentos sem argumentos. Quando o Conc executado, todos os procedimentos existentes na lista, so lanados de forma concorrente. S executada a prxima instruo depois de todos esses procedimentos terminarem.
{Conc [proc{$} S1 end proc{$} S2 end ... proc{$} Sn end]}

O Conc pode ser definido da seguinte forma:


local proc {Conc1 Ps I O} case Ps of P|Pr then M in thread {P} M = I end {Conc1 Pr M O} [] nil then O = I end end in proc {Conc Ps} {Wait {Conc1 Ps unit $}} end end

Vejamos um exemplo da utilizao do Conc:


declare proc {Ping N} if N==0 then {Browse 'ping terminated'} else {Delay 500} {Show ping} {Ping N-1} end end proc {Pong N} {For 1 N 1 proc {$ I} {Delay 600} {Show pong} end } end {Conc [proc{$} {Ping 500} end

63

proc{$} {Pong 500} end]} {Show pingPongFinished}

4.9 Tipos de dados baseados em estados


O Oz disponibiliza um conjunto de tipos de dados para representar estados. Esse tipos de dados so entre outros ports, objectos, arrays, e dicionrios (tabelas de hash). Este conjunto de dados abstracto porque caracterizam-se por apenas serem manipulados por um conjunto de operaes relativas a cada tipo. As suas implementaes so escondidas, e existem de facto vrias implementaes embora os seus comportamentos sejam os mesmos. Por exemplo, os objectos so implementados de forma diferente dependendo do nvel de optimizao do compilador. Cada membro sempre nico sendo-lhe atribudo um nome do Oz depois da sua criao. Um membro criado por uma operao explicita. Existe sempre a operao de teste de tipo. Os membros deixam de existir quando deixam de ser acessveis.

4.9.1 Ports
Os ports so canais de comunicao assncronos que podem ser partilhados por vrios emissores. Os ports tm sempre um stream associado. A operao {Port.new S ?P} cria um port P e associa-o a um stream S. A operao {Port.send P M} acrescenta no fim do stream associado a P a mensagem M. As inseres nos ports so sempre feitas no fim. A operao {Port.is P ?B} verifica se P um port. Para proteger o stream S de ser instanciado outra vez, S e uma future. A semntica a seguinte: As mensagens enviadas pela mesma thread chegam com a ordem de envio, mas mensagens enviadas por threads diferentes podem chegar com uma ordem arbitraria.

64

declare S P P = {Port.new S} {Browse S} {Port.send P 1} {Port.send P 2}

Executando este cdigo, verificamos que S est sempre a crescer. O resultado produzido ser:
S<Future> 1|_<Future> 1|2|_<Future>

Os ports so abstraces mais expressivas que a comunicao de streams pura, pois podem ser partilhados por vrias threads e podem ser embebidos noutras estruturas de dados. Os ports so o principal mecanismo para comunicao entre threads existente no Oz. Relativamente thread, a ordenao das mensagens do tipo FIFO. Vejamos o seguinte exemplo:
declare S P P = {Port.new S} {Browse S} thread ... {Port.send P a1} ... {Port.send P a2} end ... {Port.send P b1} ... {Port.send P b2}

Ordem possivel de chegada a1 a2 b1 b2 a1 b1 a2 b2 b1 a1 a2 b2


Ordem de chegada Impossvel a1 a2 b2 b1 b1 a2 a1 b2

4.9.2 Comunicao Cliente-Servidor


Os ports podem ser usados como pontos de entrada de comunicaes para servidores. Vejamos um exemplo de um servidor de uma fila FIFO: Usa dois ports, um para inserir elementos na fila atravs do procedimento put, e outro para retirar um elemento da fila usando get. O servidor insensvel ordem de relativa de chegada dos pedidos de get e put. Os pedidos de get podem chegar mesmo que a fila esteja vazia. Um servidor criado por {NewQueueServer ?Q}, que retorna um registo Q com duas propriedades put e get, tendo cada uma um procedimento com um argumento. Uma thread cliente pode aceder a estes servios, invocando esses procedimentos. Os valores so retornados para variveis lgicas.
declare fun {NewQueueServer}

65

Given GivePort={Port.new Given} Taken TakePort={Port.new Taken} in thread Given=Taken end queue(put:proc{$ X} {Port.send GivePort X} end get:proc{$ X} {Port.send TakePort X} end) end

Ou alternativamente:
declare functor NewQueueServer export put: proc {$ X} {Port.send GivePort X} end get: proc {$ X} {Port.send TakePort X} end define Given GivePort={Port.new Given} Taken TakePort={Port.new Taken} thread Given = Taken end end

E1 E2 E3 <Future 1> GivePort

= TakePort

X1=E1 X2=E2

X1 X2 <Future 2>

Figura 20 Funcionamento do servidor de fila FIFO

O programa funciona da seguinte maneira: {Q.put I0} {Q.put I1} ...{Q.put In} adiciona os elementos I0, I1, ..., In ao stream Given. O resultado I0|I1|...|In <future1>. {Q.get X0} {Q.get X1} ... {Q.get Xn} ir adicionar os elementos X0 , X1, ..., Xn ao stream Taken. O resultado o stream X0|X1|...|Xn <future2>. A igualdade Given = Taken ir instanciar os Xi com os Ii.

4.9.3 As clulas
As clulas (cells) referenciam uma clula de memria que tem um componente mutvel. Funcionam como as variveis nas linguagens convencionais. O procedimento {NewCell X ?C} cria uma clula com o valor inicial X. C instanciado com a Clula.

66

Podem efectuar-se as seguintes operaes com as clulas:


Operao {NewCell X ?C} {IsCell +C} {Exchange +C X Y} {Access +C X} {Assign +C Y} Descrio Cria uma clula com o contedo X Testa se C uma clula Troca contedo de C de X para Y Devolve o contedo de C em X Modifica o contedo de C para Y
Tabela 6 Operaes sobre clulas

O exemplo que se segue faz uso de clulas e iteradores high-order para acumular o valor de uma clula.
declare C = {NewCell 0} {For 1 10 1 proc {$ I} O N in {Exchange C O N} N = O+I end} {Browse {Access C}} %% o resultado ser 55

4.9.4 Chunks
Os chunks so estruturas semelhantes aos registos s que: O identificador do chunk um nome Oz e no existe operao de arity sobre os chunks. Isto permite que certos componentes do chunk possam ser escondidos se uma propriedade do chunk um nome Oz que apenas visvel atravs do lexical scoping, por operaes definidas pelo utilizador sobre o chunk. Um chunk pode ser criado da seguinte maneira:
{NewChunk Record ?Chunk}

Esta expresso cria um chunk com a mesma estrutura do registo mas com um identificador nico.
local X in {Browse X={NewChunk f(c:3 a:1 b:2)}} {Browse X.c} end

Ir mostrar no browser o seguinte:


<Ch>(a:1 b:2 c:3) 3

Os chunks e as clulas so tipos de dados primitivos do Oz, isto , todos os outros tipos de dados que representam estados existentes no Oz, podem ser construdos com base nestes dois tipos.

67

4.9.5 Locks
Os locks so os mecanismos existentes em Oz para garantir acesso exclusivo a seces criticas, que correspondem a uma zona de cdigo Oz. Quando uma thread entra numa seco critica, o lock activado e garantido o acesso exclusivo da thread ao recurso. Quando a execuo abandona a regio correspondente seco critica, quer na sequncia normal de execuo, que por interveno de uma excepo, o lock libertado. Uma thread que concorra para obter um lock que est ocupado, bloqueia at que o esse lock seja libertado. Os locks so reentrantes como no java. Os locks no so primitivos do Oz, isto , podem ser implementados atravs de clulas. Para aplicar um lock a uma seco critica S, faz-se o seguinte:
lock L then S end

O L a expresso associada ao lock. Assumindo que T uma thread que executa a expresso anterior, se L estiver livre, T executa S e L fica ocupado. Se L estiver ocupado, T bloqueia e fica no fim da fila aguardando a sua vez para ocupar L. Todas as outras threads bloqueiam enquanto uma thread executar S. Existem as seguintes operaes sobre locks:
{Lock.new ?L} cria um novo lock L . {Lock.is E} devolve true se E for um lock.

Para os locks simples a reentrncia no suportada. Isto , se a mesma thread tentar readquirir o lock bloqueia. O locks simples podem ser implementados atravs do seguinte cdigo:
proc {NewSimpleLock ?Lock} Cell = {NewCell unit} in proc {Lock Code} Old New in try {Exchange Cell Old New} {Wait Old} {Code} finally New=unit end end end

Code um procedimento sem argumentos que corresponde seco critica. O lock representado sob a forma dum procedimento, que quando aplicado ao code, tenta deter o lock esperando que a varivel Old seja instanciada com unit. Note-se que o lock libertado quando a execuo de code termina de forma normal ou por meio de uma excepo.

68

4.10 Classes e Objectos


As classes em Oz so chunks que contm : Uma coleco de mtodos numa tabela de mtodos; uma descrio dos atributos que sero possudos por cada instancia da classe, cada atributo representa estados e acedido pelo nome do atributo que consiste num tomo ou num nome Oz; descrio das propriedades que cada instancia da classe possui, as propriedades so componentes imutveis (variveis), que so acedidas pelo nome da propriedade que consiste num tomo ou um nome Oz. As classes em Oz no representam estados. Ao contrrio de linguagens como o Java e o Smalltalk, so apenas descries de como os objectos da classe se devem comportar.

4.10.1 Implementao de Classes utilizando estruturas mais primitivas


Vejamos um exemplo de uma classe counter. Esta classe tem um nico atributo que pode ser acedido pelo tomo val. Tem uma tabela de mtodos com trs mtodos acedidos pelas propriedades browse, init e inc do chunk. Um mtodo um procedimento que recebe uma mensagem que consiste num registo, um parmetro com o estado do objecto, e o prprio objecto representado internamente por self.
declare Counter local Attrs = [val] MethodTable = m(browse:MyBrowse init:Init inc:Inc) proc {Init M S Self} init(Value) = M in {Assign S.val Value} end proc {Inc M S Self} X inc(Value) = M in {Access S.val X} {Assign S.val X+Value} end end proc {MyBrowse M=browse S Self} {Browse {Access S.val}} end in Counter = {NewChunk c(methods:MethodTable attrs:Atts)} end

69

4.10.2 Implementao de Objectos utilizando estruturas mais primitivas


Existe um procedimento genrico que cria o objecto a partir da classe. O iterador Record.ForAll/2 itera sobre os campos de um registo. NewObject devolve um procedimento Object que identifica o objecto. O estado do objecto apenas visvel no interior de Object.
fun {NewObject Class InitialMethod} State = {MakeRecord state Class.attrs} proc{Object M} {Class.methods.{Label M} M State Object} end in {Record.forAll State proc{$ A} X in A={NewCell X} end} {Object InitialMethod} Object end

Este programa pode ser testado da seguinte maneira:


declare C {NewObject Counter init(0) C} {C inc(6)} {C inc(6)} {C browse}

4.10.3 As Classes em Oz
O Oz implementa programao orientada a objectos utilizando as metodologias vistas anteriormente, mas possui no seu sintaxe vocabulrio especifico e a nvel da implementao, possui certas optimizaes para que a invocao de mtodos em objectos, tenha o mesmo desempenho que a simples chamada a um procedimento. A classe Counter definida anteriormente poderia ser rescrita sobre a forma:
class Counter attr val meth browse {Browse @val} end meth inc(Value) val <- @val + Value end meth init(Value) val <- Value end end

70

Uma classe definida da seguinte forma:


class X ... end

Os atributos so definidos usando um declarador de atributos antes da declarao dos mtodos.


attr A1 A2 An

Depois declaram-se os mtodos usando a seguinte forma para cada um deles:


meth E S end

A expresso E um registo que especifica a cabea do mtodo e consiste num registo cujo identificador corresponde ao nome do mtodo.Para se aceder a um atributo A usa-se @A. Para atribuir um valor a um atributo A usa-se A <- E. Uma classe pode ser annima como os procedimentos. Define-se da seguinte forma:
X = class $ ... end

O exemplo a seguir mostra como um objecto pode ser criado a partir de uma classe usando o procedimento New/3, cujo primeiro argumento a classe, o segundo o mtodo inicial, e o terceiro o resultado, isto , o objecto. O procedimento New o procedimento genrico de criao de objectos a partir de classes.
declare C = {New Counter init(0)} {C browse} {C inc(1)} {C browse}

4.10.4 Chamadas estticas a mtodos


Assumindo que existe uma classe C e um mtodo cuja cabea seja m(...), uma chamada esttica a um mtodo tem a seguinte forma:
C, m(...)

Uma chamada esttica a um mtodo s pode ser usada no interior da definio da classe. A chamada ao mtodo recebe o prprio objecto denominado de self como argumento implcito. O mtodo m pode estar definido na classe C ou numa classe de hierarquia superior (super classe). Vejamos um exemplo:
declare functor ListF export append:Append member:MemberB length:Length define class ListClass from BaseObject meth append(Xs Ys $) case Xs of nil then Ys [] X|Xr then X|(ListClass , append(Xr Ys $))

71

end end meth member(X L $) end meth length(Xs $) end O = {New ListClass noop} fun {Append X Y} {O append(X Y $)} end fun {MemberB X L} {O member(X L $)} end fun {Length L} {O length(L $)} end end

Este exemplo bastante interessante, porque para alm de demonstrar a utilizao de chamadas estticas a mtodos, tambm demonstra como as classes podem ser usadas como especificaes de mdulos, isto , a classe ListC define procedimentos comuns de tratamento de listas como mtodos. Para alm disso tambm se pode ver um exemplo de herana de classe em Oz atravs da expresso :
class ListClass from BaseObject

4.10.5 Herana de classes


class Account attr balance:0 meth transfer(Amount) balance<- @balance+Amount end meth getBal(B) B = @balance end end A={New Account transfer(100)}

Extenso conservadora:
class VerboseAccount from Account meth verboseTransfer(Amount) {self transfer(Amount)} {Show @balance} end end

Extenso no conservadora:
class AccountWithFee from VerboseAccount attr fee:5 meth transfer(Amount) VerboseAccount,transfer(Amount-@fee) end end

72

As classes podem herdar de uma ou vrias classes definidas depois da palavra chave from. A classe B uma superclasse de A se: B aparece depois de from na declarao da classe A, ou B uma superclasse de uma classe que aparece depois da palavra chave from na declarao de A. Os mtodos (atributos e propriedades) disponveis (visveis) na classe C esto definidos segundo uma relao de precedncia sobre os mtodos sobre os mtodos que aparecem na hierarquia da classe: relao de sobrecarga: Um mtodo na classe C sobrecarrega um qualquer mtodo de uma superclasse, se esse mtodo tiver o mesmo identificador que o do mtodo da superclasse. Para a herana de classes ser vlida no pode haver relaes cclicas entre as classes.

Figura 21 Relao vlida de herana de classes

Figura 22 Relao de herana invalida (Relao cclica)

4.10.6 Propriedades
As propriedades so componentes das classes e so especificados da seguinte forma:
class C from feat a1 an end

Tal como nos registos, as propriedades dos objectos tm um campo associado. Esse campo consiste num varivel lgica que pode ser instanciada com qualquer valor Oz, isto , com clulas, registos, objectos, classes, etc... Para se aceder ao valor da propriedades usa-se o operador infixo ..
class ApartmentC from BaseObject meth init skip end end class AptC from ApartmentC feat streetName: york streetNumber:100 wallColor:white floorSurface:wood end Apt = {New AptC init} {Browse Apt.streetName}

73

As propriedades so inicializadas na altura da definio da classe: Todas as instancias da classe iro ter as propriedades com os valores definidos na classe. No exemplo que se segue ir ser escrito o nome york duas vezes.
declare Apt1 Apt2 Apt1 = {New AptC init} Apt2 = {New AptC init} {Browse Apt1.streetName} {Browse Apt2.streetName}

Propriedades no instanciadas:
class MyAptC1 from ApartmentC feat streetName end

Quando uma instancia de uma classe criada, o campo da propriedade no instanciada uma nova varivel. Exemplo:
declare Apt3 Apt4 Apt3 = {New MyAptC1 init} Apt4 = {New MyAptC1 init} Apt3.streetName = kungsgatan Apt4.streetName = sturegatan

Neste exemplo a propriedade streetName do objecto Apt3 instanciado com com o tomo kungsgatan e a propriedade streetName do objecto Apt4 instanciada com o tomo sturegatan. Quando as propriedades so instanciadas com um valor Oz, por exemplo uma varivel, todas as instancias iro partilhar a mesma varivel.
class MyAptC1 from ApartmentC feat streetName:f(_) end declare Apt1 Apt2 Apt1 = {New MyAptC1 init} Apt2 = {New MyAptC1 init} {Browse Apt1.streetName} {Browse Apt2.streetName} Apt1.streetName = f(york)

Neste exemplo a propriedade streetName de Apt2 ir ter o mesmo valor de streetName de Apt1.

74

4.10.7 Classes parametrizadas


A forma mais comum de tornar as classes genricas definindo uma classe com alguns mtodos definidos mas sem estarem especificados. Mais tarde esses mtodos sero especificados nas subclasses. Em Oz, existe uma forma muito natural de definir classes genricas. Como as classes so entidades first-class, pode criar-se uma funo que receba um determinado nmero de argumentos e retorne uma classe. O exemplo que se segue a funo SortClass recebe uma classe (que pode possui um operador de ordenao) como argumento e retorna uma classe de ordenao.
fun {SortClass Type} class $ from BaseObject meth qsort(Xs Ys) case Xs of nil then Ys = nil [] P|Xr then S L in {self partition(Xr P S L)} ListC, append({self qsort(S $)} P|{self qsort(L $)} Ys) end end meth partition(Xs P Ss Ls) case Xs of nil then Ss = nil Ls = nil [] X|Xr then Sr Lr in case Type,less(X P $) then Ss = X|Sr Lr = Ls else Ss = Sr Ls = X|Lr end {self partition(Xr P Sr Lr)} end end end end

Podemos criar uma classe com os operadores. Por exemplo para inteiros e racionais:
class Int meth less(X Y $) X<Y end end class Rat from Object meth less(X Y $) /(P Q) = X /(R S) = Y in P*S < Q*R end end

O exemplo pode ser usado da seguinte maneira:

75

Para inteiros:
{Browse {{New {SortClass Int} noop} qsort([1 2 5 3 4] $)}}

Para racionais:
{Browse {{New {SortClass Rat} noop} qsort([/(23 3) /(34 11) /(47 17)] $)}}

4.10.8 Mtodos pblicos e privados


Os mtodos podem ser identificados por variveis em vez de por literais. Caso sejam identificados por variveis, os mtodos so privados.
class C from ... meth A(X) ... end meth a(...) {self A(5)} ... end .... end

O mtodo A(X) apenas visvel do interior da classe. Esta notao representada de uma forma mais primitiva da seguinte forma:
local A = {NewName} in class C from meth !A(X) end meth a(){self A(5)} end end end

Muitas das linguagens de programao orientada a objectos tambm possuem o conceito de mtodos protegidos. Um mtodo protegido consiste num mtodo que apenas acessvel na classe onde est definido, ou nas classes descendentes. Como os atributos apenas so visveis dentro da prpria classe ou nas classes descendentes, para criar mtodos protegidos basta em primeiro lugar tornar os mtodos privados e depois guarda-los em atributos.
local ProtMeth in {NewName ProtMeth} class C2 attr protMeth:ProtMeth meth !ProtMeth ... end meth otherMethod {self @protMeth} end end end

76

Se for criada uma classe C2 descendente de C1, o mtodo protegido pode ser chamado da seguinte forma:
class C1 from C meth b(...) L=@protMeth in {self L(5)} ... end ... end

4.10.9 Valores por omisso dos argumentos


Um mtodo pode ter valores por omisso para os seus argumentos. Por exemplo:
meth m(X Y d1:Z<=0 d2:W<=0) ... end

Para este exemplo, se as propriedades d1 e d2 no forem especificadas estes argumentos iro ter o valor 0. Vejamos um exemplo que ilustra alguns dos conceitos aprendidos at agora sobre objectos:
class BoundedPoint from Point attr xbounds: 0#0 ybounds: 0#0 boundConstraint: BoundConstraint meth init(X Y xbounds:XB <= 0#10 ybounds:YB <= 0#10) Point,init(X Y) % call your super xbounds <- XB ybounds <- YB end meth move(X Y) if {self BoundConstraint(X Y $)} then Point,move(X Y) end end meth BoundConstraint(X Y $) (X >= @xbounds.1 andthen X =< @xbounds.2 andthen Y >= @ybounds.1 andthen Y =< @ybounds.2 ) end meth display Point,display {self DisplayBounds} end meth DisplayBounds X0#X1 = @xbounds Y0#Y1 = @ybounds S = "xbounds=("#X0#","#X1#"),ybounds=(" #Y0#","#Y1#")" in {Browse S} end end

77

4.11 Objectos e Concorrncia


As threads podem comunicar atravs da passagem de mensagens ou atravs de um espao partilhado (exemplo: objectos partilhados). A comunicao atravs de objectos partilhados requer a capacidade de construir objectos com operaes concorrentes, de forma que o estado do objecto se mantenha coerente depois de efectuadas essas operaes. Em Oz o acesso exclusivo a um objecto est separado do sistema do objecto. Isto permite-nos fazer operaes atmicas sobre conjuntos de objectos, o que bastante importante por exemplo em bases de dados distribudas.

4.11.1 Trocas atmicas em atributos


Um lock simples pode ser implementado usando a seguinte expresso:
Old = lck <- New

Esta operao semelhante operao Exchange aplicada s clulas, isto , efectua trocas atmicas em atributos de objectos.
class SimpleLock attr lck: unit meth init skip end meth lock( Code) Old New in try Old = lck <- New {Wait Old} {Code} finally New= unit end end end

4.11.2 Locks de threads reentrantes


Como j foi visto, a unidade computacional em Oz a thread. Consequentemente ter de existir um mecanismo de excluso mtua que garanta o acesso exclusivo de uma thread a um recuso. Um lock de thread reentrante permite mesma thread de reentrar numa seco critica dinmica aninhada (nested) sob o mesmo lock. Este lock apenas pode ser adquirido por uma thread de cada vez. Threads que tentem adquirir o mesmo lock concorrentemente ficam em fila de espera. Quando o lock libertado, a thread que est em primeiro lugar na fila ser a prxima a adquiri-lo. Vejamos um exemplo:

78

class ReentrantLock from SimpleLock attr Current: unit meth lck( Code) ThisThread = {Thread. this} in case ThisThread == @Current then {Code} else try Code1 = proc {$} Current <- ThisThread {Code} end in SimpleLock, lck{ Code1} finally Current <- unit end end end end

4.11.3 Aplicando locks a objectos

Podemos declarar na classe que os seus objectos possuem um lock por omisso quando estes so criados. Uma classe com a declarao implcita de um lock feita da seguinte forma:
class C from .... prop locking .... end

Isto no fecha automaticamente o objecto quando um dos seus mtodos invocado, tem de ser usada a expresso
lock S end

dentro do mtodo para seja garantido o acesso exclusivo quando S executado. importante relembrar que os locks so de threads reentrantes, isto significa que: Se em todos os objectos que construmos colocarmos lock S end em todos os mtodos e executarmos o programa com apenas uma thread, ter de ter o mesmo comportamento que antes. Vejamos agora um exemplo de como pode refinar uma classe para poder ser usada de forma concorrente:
class Counter attr val meth browse {Browse @val} end meth inc(Value) val <- @val + Value end meth init(Value) val <- Value end

79

end

class CCounter from Counter prop locking meth inc(Value) lock Counter,inc(Value) end end meth init(Value) lock Counter,init(Value) end end end

4.11.4 Canal FIFO concorrente


Trata-se de um exemplo de concorrncia com objectos, em que existe um canal concorrente que partilhado por um nmero arbitrrio de threads e funciona da seguinte maneira: Qualquer thread produtora pode colocar informao no canal assincronamente; As threads consumidoras tm que aguardar que exista informao no canal; As threads que esperam so servidas de forma fair (justa). A sincronizao feita atravs de variveis lgicas. Os mtodos put/1 e get/1 respectivamente inserem e esperam que exista um elemento no canal. Se existirem mltiplas threads a concorrerem pelo canal iro reservar o seu lugar no canal, garantido justia no acesso ao canal. A expresso {Wait I} feita fora da seco critica para evitar deadlocks.
class Channel from BaseObject prop locking attr f r meth init X in f <- X r <- X end meth put( I) X in lock @r= I|X r<- X end end meth get(? I) X in lock @f= I|X f<- X end {Wait I} end end

4.11.6 Eventos
A classe seguinte define a noo e operaes de evento (notify(Event) e wait(Event) ) atravs da classe Channel.
class Event from Channel meth wait Channel , get(_) end meth notify Channel , put(unit) end end

80

Vejamos o exemplo do unit buffer. O comportamento bastante semelhante ao do canal FIFO relativamente s threads consumidoras. Cada thread consumidora espera que o buffer esteja cheio. Relativamente s threads produtoras, apenas uma pode inserir um item no buffer vazio. As outras threads produtoras ficam suspensas, at que, o elemento seja consumido. Veja-se que no necessrio utilizar locks directamente neste programa. A combinao de variveis lgicas com objectos tornam bastante simples e clara, a programao de problemas deste tipo.
class UnitBuffer from BaseObject attr prodq buffer meth init buffer <- {New Channel init} prodq <- {New Event init} {@ prodq notify} % Buffer est vazio end meth put(I) {@ prodq wait} {@ buffer put(I)} end meth get(?I) {@ buffer get(I)} {@ prodq notify} end end

Se quisermos um tamanho varivel do buffer podemos pass-lo como argumento ao mtodo init e modificar da seguinte maneira este mtodo: Criar um ciclo que mande N notificaes de buffer livre.
class BoundedBuffer from UnitBuffer attr prodq buffer meth init( N) buffer <- {New Channel init} prodq <- {New Event init} {For 1 N 1 proc {$ _} {@ prodq notify} end} end end

4.11.7 Objectos Activos


Um objecto activo uma thread (processo) cujo comportamento descrito por uma classe. A comunicao com objectos activos feita atravs de mensagens. Um objecto activo reage s mensagens executando o mtodo correspondente mensagem recebida. Estes objectos executam um mtodo de cada vez, portanto o lock no necessrio para os mtodos executados pelo objecto activo. A interface feita usando ports de Oz. Os cliente enviam mensagens para o objecto activo, enviando mensagens para o port que lhe est associado. Vejamos como pode ser construda esta abstraco: Tendo em conta que, os objectos activos podem ser usados como servidores recebendo mensagens de clientes atravs de uma rede, chamamos a esta abstraco de abstraco de servidor. Para criar um servidor S com a classe Class executa-se o seguinte S={Newserver Class init}, em que init o construtor. Para se ficar com uma ideia mais clara, a funo a seguir, consiste numa primeira aproximao criao dum servidor. Esta funo cria um port chamado Port, um objecto chamado Object, e

81

por ultimo uma thread que trata as mensagens recebidas no port, chamando o mtodo correspondente.
fun {NewServer Class init} S % O stream do port Port = {NewPort S} Object = {New Class init} in thread {ForAll S proc{$ M} {Object M} end} end Port End

No prximo exemplo adicionou-se a possibilidade de terminar a thread construindo um mtodo protegido close que acessvel aos mtodos da classe. Para saltar fora do ciclo de recepo de mensagens, usa-se um mecanismo baseado em tratamento de excepes.
local CloseException = {NewName} class Server attr close: Close meth Close raise CloseException end end end in fun {NewServer Class init} S % O stream do port Port = {NewPort S} Object = {New class $ from Server Class end init} in thread try {ForAll S proc{$ M} {Object M} end} catch !CloseException then skip end end Port end end

82

4.12 Arrays e Dicionrios


Os arrays em Oz podem ser criados da seguinte maneira:
Arr={NewArray 1 100 nil}

Cria um array de 100 elementos, indexado de 1 a 100 e inicializado a nil. Para se inserir um elemento no array basta fazer:
{Array.put Arr 1 a} ou Arr.1:=a

Para se ler um valor do array faz-se o seguinte:


X={Array.get Arr 1} or X=Arr.1

A expresso seguinte cria um registo (tuplo) que a cpia sem ser baseada em estados do array.
R={Array.toRecord +Label +Array}

Um dicionrio uma estrutura de dados parecida aos arrays onde existe uma associao entre chaves e entradas. Pode ser criado da seguinte forma:
Dict={NewDict}

A expresso seguinte mostra como adicionar uma entrada no dicionrio:


{Dictionary.put Dict Chave Entrada} ou Dict.Chave:=Entrada

Para ler um elemento a partir da chave pode fazer-se:


X={Dictionary.get Dict Chave} ou X=Dict.Chave

A prxima expresso idntica anterior, caso exista uma entrada para essa chave, caso contrrio devolve um valor por omisso.
X={Dictionary.condGet Dict Chave Default}

A expresso seguinte devolve uma lista com todas as chaves:


X={Dictionary.keys Dict}

A expresso seguinte devolve uma lista na forma Chave#Entrada:


X={Dictionary.entries Dict}

Finalmente a expresso seguinte cria uma cpia do dicionario sob a forma de tuplo:
R={Dictionary.toRecord +Label +Dict}

83

4.13 Programao Lgica


Existem muitos tipos de problemas, nomeadamente no campo da inteligncia artificial, que so resolvidos recorrendo a mecanismos de busca e propagao de restries. Para facilitar a programao deste tipo de problemas, fundamental que a linguagem possa abstraccionar os detalhes da busca, atravs de no determinismo. O Prolog a linguagem mais conhecida desenvolvida para resolver este tipo de problemas. Iremos ver que o Oz tambm possui excelentes caractersticas para a programao lgica e para a programao baseada em restries.

4.13.1 Armazenamento de restries


As threads partilham um espao de armazenamento onde as variveis so instanciadas sob a forma de igualdades: X1=U1, ..., Xn=Un onde Xi so as variveis e Ui so entidades Oz ou variveis. O armazenamento de restries contm valores Oz correspondentes a registos, nmeros, nomes ou variveis, nomes nicos que identificam os procedimentos, clulas e vrios tipos de chunks (classes, objectos, functors, etc). Conceptualmente o armazenamento uma formula conjuntiva do tipo: Y1...Yn: X1=U1 e ... e Xn=Un, onde Xi so as variveis, Ui so os valores Oz, e Yi so todas as unies entre as variveis Xi e os valores Oz Yi. A rea de armazenagem de computao do Oz constituda pela rea de armazenagem de restries, de procedimentos e de clulas.

4.13.2 Espaos computacionais


Um espao computacional consiste num espao de armazenamento de computaes e um conjunto de threads. At agora, apenas foi usado um s espao computacional. Em programao lgica surgem estruturas mais elaboradas devido a mltiplas computaes embebidas. Existem as seguintes regras para a estrutura dos espaos computacionais: Existe sempre um espao computacional de topo, onde as threads interagem com o exterior. Uma thread que tente adicionar restries (instanciaes) inconsistentes neste espao ser levantada uma excepo, garantindo a consistncia deste espao. Uma thread pode criar directa ou indirectamente um espao computacional. O novo espao computacional ser filho e tendo como pai o espao computacional corrente, isto , criada uma hierarquia de espaos computacionais. As threads e as variveis apenas pertencem a um espao computacional. Uma thread num espao computacional pode aceder as suas variveis e s dos seus antecedentes. Mas uma thread num espao computacional pai no pode ver as variveis de um espao computacional filho. Uma thread num espao filho pode adicionar restries (instanciaes) a variveis que lhe so visveis, isto , pode instanciar variveis no espao corrente e

84

nos seus antecessores.

4.13.3 Vinculao e desvinculao de restries

Uma condio C vinculada ao armazenamento se C, for uma formula lgica e estiver implcita no armazenamento . Isto , adicionando C ao armazenamento no acrescenta informao ao que j l existe. Uma condio C desvinculada ao armazenamento se uma contradio de C estiver existir no armazenamento . Uma restrio desvinculada inconsistente com a informao que j existe no armazenamento. Sendo o armazenamento de restries uma formula lgica, pode tambm falar-se em vincular um armazenamento de restries a outro. Por exemplo, se considerarmos o armazenamento = X=1 e ... e Y=f(X Z) e as seguintes condies: X=1 vinculado porque esta atribuio no acrescenta informao ao espao . Existe pelo menos um U tal que Y=f(1 U), esta condio tambm vinculada porque no adiciona informao ao armazenamento. J existe uma condio X=1 e independentemente do valor que Z venha a ter a condio satisfeita. Y=f(1 2) no vinculado porque esta condio ir aumentar informao existente no armazenamento, nomeadamente a informao Z=1. X=2 e Y=f(3 U) ambas so desvinculadas do armazenamento , porque contradizem a informao l existente. Isto , X=2 contradiz X=1 e Y=f(3 U) tambm contradiz X=1. Ser levantada uma excepo, que no nvel superior aparecer ao utilizador sobre a forma de mensagem de erro.

4.13.4 Disjunes
Em Oz as disjunes so especificada usando a instruo or. As expresses disjuntivas so expressas na forma de clausulas. Uma clausula composta por um guarda G e por corpo S1.
or G1 then S1 [] G2 then S2 . [] GN then SN end

Assumindo que executa numa thread num espao SP, a semntica da instruo or a seguinte: A thread bloqueia. So criados N espaos SP1, SP2, ..., SPn com uma thread associada a cada executando cada um dos guardas G1, G2, ..., Gn. A execuo da thread principal fica bloqueada at que pelo menos um dos espaos computacionais filhos no falhe. Se todos os espaos filhos falharem a thread pai levantada uma

85

condio de falha no seu espao. Se o espao da thread for o topo da hierarquia de espaos, levantada uma excepo. Caso no seja o topo, simplesmente fica um espao falhado. Se restar um espao que no tenha falhado fundido com o espao pai. A execuo da thread inicial retomada executando o corpo da clausula correspondente ao guarda associado ao espao que no falhou. Pode abreviar-se a seguinte expresso:
or ... [] Gi then skip ... end

da seguinte forma:
or ... [] Gi ... end

A instruo or no introduz o conceito de no determinismo. No prolog no existe uma instruo correspondente ao or. O disjuntor P ; Q cria um ponto de escolha sujeito a backtraking.

4.13.6 Execuo orientada determinao


Este processo consiste em sincronizar computaes, baseando-se na indeciso sobre qual a disjuno a seguir, tendo a thread que aguardar que certas variveis existentes nos guardas dessas disjunes sejam conhecidas.
proc {Ints N Xs} or N = 0 Xs = nil [] Xr in N > 0 = true Xs = N|Xr {Inst N-1 Xr} end end local proc {Sum3 Xs N R} or Xs = nil R = N [] X|Xr = Xs in {Sum3 Xr X+N R} end end in proc {Sum Xs R} {Sum3 Xs 0 R} end end local N S R in thread {Ints N S} end thread {Sum S {Browse}} end N = 1000 end

Neste exemplo a thread que executa Ints, suspende at que N seja

86

conhecido, porque no consegue escolher uma disjuno. De forma similar Sum3 ir esperar que S seja conhecido. Como o S criado incrementalmente o comportamento de Sum3 ser uma secesso de suspenses e execues. O programa comea realmente a funcionar quando N instanciado com o valor 1000.

4.13.7 Lgica Condicional

Lgica condicional em Oz exprimida pela expresso:


cond X1 ... XN in S0 then S1 else S2 end

Assumindo que a thread executa num espao SP, esta instruo tem a seguinte semntica: A thread fica bloqueada. Um espao computacional SP1 criado com uma s thread que executa o guarda cond X1 ... XN in S. A execuo da thread pai fica suspensa at que SP1 seja vinculado ou desvinculado. Estas condies podem nunca ocorrer, por exemplo, se existir uma thread suspensa ou se existir uma thread em execuo permanente. Se a SP1 for desvinculado a thread pai continua com a execuo de S2. Se SP1 for vinculado, fundido com com SP e a thread pai continua com a execuo de S1. A instruo cond parecida com as expresses condicionais do Prolog ( P->Q ; R ). O Oz um pouco mais cuidadoso no espao de abrangncia das variveis, tendo que ser declaradas explicitamente.

4.13.8 Expresses condicionais paralelas


Uma expresso condicional paralela expressa da seguinte forma:
cond G1 then S1 [] G2 then S2 ... else SN end

Uma expresso condicional paralela e executada avaliando todas as condies G1, G2, ..., G(N-1), de forma arbitraria e concorrente, em que cada thread tem o seu prprio espao. Se o espao Gi for vinculado, a instruo Si escolhida pela thread pai. Se todas os espaos falharem, a instruo SN escolhida. Em caso contrrio a thread fica suspensa.

87

Um exemplo tipico de programao lgica concorrente a fuso binria. A fuso binria consiste um fundir dois streams, em que a ordem de chegada dos elementos dos dois streams determinam a ordem em que estaro na lista resultante.
proc {Merge Xs Ys Zs} cond Xs = nil then Zs = Ys [] Ys = nil then Zs = Xr [] X Xr in Xs = X|Xr then Zs = X|Zr {Merge Xr [] Y Yr in Ys = Y|Yr then Zs = Y|Zr {Merge Xs end end

Zr Ys Zr Yr

in Zr} in Zr}

Normalmente a fuso binria ineficiente. Uma maneira mais eficiente usando clulas e streams.
proc {MMerge STs L} C = {NewCell L} proc {MM STs S E} case STs of ST|STr then M in thread {ForAll ST proc{$ X} ST1 in {Exchange C X|ST1 ST1} end} M=S end {MM STr M E} [] nil then skip [] merge(STs1 STs2) then M in thread {MM STs1 S M} end {MM STs2 M E} end end in thread {MM STs unit E} end thread if E==unit then L = nil end end end

Para obter fuso binria basta fazer {MMerge [X Y] Z}.

4.13.9 Programas no deterministas e busca


O Oz permite a programao no deterministica e de busca ao mesmo nvel que o Prolog, embora haja algumas diferenas. Por exemplo o Prolog, por omisso j implementa uma estratgia de busca conhecida por backtraking. O Oz vai mais longe pois permite ao programador desenvolver a sua prpria estratgia de busca, de forma a separar da programao ortogonal a programao no deterministica. O Oz possui uma instruo que define um ponto de escolha, a estratgia de busca pode ser programada separadamente. A instruo dis permite definir um ponto de escolha.

88

proc {Append Xs Ys Zs} dis Xs = nil Ys = Zs then skip [] X Xr Zr in Xs = X|Xr Zs = X|Zr then {Append Xr Ys Zr} end end

idntico ao predicado append/3 do Prolog.


append([], Ys, Ys). append([X|Xr], Ys, [X|Zr]) :- append(Xr, Yr, Zr).

Tal como o or, o dis pode ser abreviado da seguinte maneira:


proc {Append Xs Ys Zs} dis Xs = nil Ys = Zs [] X Xr Zr in Xs = X|Xr Zs = X|Zr then {Append Xr Ys Zr} end end

Se este procedimento for chamado da seguinte maneira:


local X in {Append [1 2 3] [a b c] X} {Browse X} end

Neste caso o comportamento ser igual ao da instruo or, isto , X ser instanciado deterministicamente com a lista [1 2 3 a b c]. Mas se for chamado na forma:
local X Y in {Append X Y [1 2 3 a b c]} {Browse X#Y} end

O comportamento semelhante instruo or. A thread bloqueia quando tenta executar {Append X Y [1 2 3 a b c]}. No entanto existe um diferena que reside criar um ponto de escolha com duas alternativas: X = nil Y = [1 2 3 a b c] then skip Xr Xr in X = 1|Xr Zr = [2 3 a b c] then {Append Xr Y Zr}

89

4.13.10 Exemplos
Definio de uma gramtica:
Sentence(P) --> NounPhrase(X P1 P) VerbPhrase(X P1) NounPhrase(X P1 P) --> Determiner(X P2 P1 P) Noun(X P3) RelClause(X P3 P2) NounPhrase(X P P) --> Name(X) VerbPhrase(X P) --> TransVerb(X Y P1) NounPhrase(Y P1 P) | InstransVerb(X P) RelClause(X P1 and(P1 P2)) --> [that] VerbPhrase(X P2) RelClause(_ P P) --> [] Determiner(X P1 P2 all(X imp(P1 P2))) --> [every] Determiner(X P1 P2 exits(X and(P1 P2))) --> [a] Noun(X man(X)) --> [man] Noun(X woman(X)) --> [woman] name(john) --> [john] name(jan) --> [jan] TransVerb(X Y loves(X Y)) --> [loves] IntransVerb(X lives(X)) --> [lives]

proc {Sentence P S0#S} X P1 S1 in {NounPhrase X P1 P S0#S1} {VerbPhrase X P1 S1#S} end proc {NounPhrase X P1 P S0#S} choice P2 P3 S1 S2 in {Determiner X P2 P1 P S0#S1} {Noun X P3 S1#S2} {RelClause X P3 P2 S2#S} [] {Name X S0#S} P1 = P end end proc {VerbPhrase X P S0#S} choice Y P1 S1 in {TransVerb X Y P1 S0#S1} {NounPhrase Y P1 P S1#S} [] {IntransVerb X P S0#S} end end proc {TransVerb X Y Z S0#S} S0 = loves|S Z = loves(X Y) End proc {IntransVerb X Y S0#S} S0 = lives|S Y = lives(X) End proc {Name X S0#S} S0 = X|S

90

choice X = john [] X = jan end end proc {Noun X Y S0#S} choice S0 = man|S Y = man(X) [] S0 = woman|S Y = woman(X) end end proc {Determiner X P1 P2 P S0#S} choice S0 = every|S P = all(X imp(P1 P2)) [] S0 = a|S P = exists(X and(P1 P2)) end end proc {RelClause X P1 P S0#S} P2 in choice S1 in S0 = that|S1 P = and(P1 P2) {VerbPhrase X P2 S1#S} [] S0 = S P = P1 end end declare proc {Main P} {Sentence P [every man that lives loves a woman]#nil} end

Instruo dis:
declare Edge proc {Connected X Y} dis {Edge X Y} [] Z in {Edge X Z} {Connected Z Y} end end proc {Edge X Y} dis X = 1 Y = [] X = 2 Y = [] X = 2 Y = [] X = 3 Y = [] X = 2 Y = [] X = 5 Y = [] X = 4 Y = [] X = 6 Y =

2 1 3 4 5 6 6 7

91

[] X = 6 Y = 8 [] X = 1 Y = 5 [] X = 5 Y = 1 end end {ExploreOne proc {$ L} X Y in X#Y = L {Connected X Y} end } {Browse {SearchAll proc {$ L} X Y in X#Y = L {Connected X Y} end } }

Negao:
proc {NotP P} {SearchOne proc {$ L} {P} L=unit end $} = nil end proc {ConnectedEnh X Y Visited} dis {Edge X Y} [] Z in {Edge X Z} {NotP proc{$} {Member Z Visited} end} {ConnectedEnh Z Y Z|Visited} end end

92

5. Tutorial de Oz Distribudo
O Mozart-Oz possui um modelo de programao distribuda que consegue separar a funcionalidade da aplicao da sua estrutura distribuda. Possui tambm mecanismos para tolerncia a falhas, computao aberta, e algum suporte para segurana. Para as prximas verses ir ser dada uma maior ateno aos aspectos ainda pouco desenvolvidos deste sistema de programao, como por exemplo, a segurana. Neste tutorial podemos encontrar informao relativa programao distribuda em Mozart-Oz, nomeadamente, sobre as abstraces de programao distribuda em Oz, objectos estacionrios, agentes mveis, tolerncia a falhas, etc ..., sempre acompanhando os novos conceitos que so introduzidos, com exemplos prticos da sua aplicao. A distribuio nesta linguagem garantida por quatro mdulos: Connection Disponibiliza o mecanismo bsico (conhecidos por tickets) para a conexo entre duas aplicaes Remote Permite que uma aplicao possa criar um site e ligar-se a ele. Um site pode ser criado na mesma mquina ou num mquina remota. Pickle Permite guardar ou ler informao de URLs e ficheiros. Fault Possui mecanismos de tratamento e deteco de falhas. Para uma melhor compreenso dos assuntos referidos neste captulo recomenda-se a leitura do captulo 3, onde abordado o Oz distribudo de uma forma terica.

5.1 Nomes Globais


Existem dois tipos de nomes globais em OZ: Referncias internas So referncias que existem dentro dum espao computacional do Oz. Todas as estruturas de dados do Oz so endereadas por estas referncias. Estas referncias correspondem as apontadores e apontadores de rede em outras linguagens de programao, embora se encontrem protegidos do programador como por exemplo no Java. Referncias externas Podem existir em qualquer sitio, isto , quer no interior ou exterior de espaos computacionais do Oz. Possuem nomes globais externos conhecidos. So representados por strings, logo podem ser comunicados e guardados de diferentes formas, por exemplo: pginas Web, espaos computacionais do Oz, etc ... So necessrios para que uma aplicao comunique com o exterior.

93

Existem trs tipos de nomes globais externos: Ticket uma string que referencia uma qualquer entidade da linguagem numa aplicao em execuo. Os tickets so criados no interior de uma aplicao Oz em execuo e podem ser usados para conectar aplicaes. URL uma string referencia um ficheiro numa rede. Em Mozart um ficheiro pode ser um pickle. Um pickle pode conter procedimentos, classes, functors, etc ... Hostname uma string que segue o sintaxe do DNS, que identifica o nome de uma mquina. O hostname pode ser usado para iniciar um processo noutra mquina.

5.1.1 Conectar aplicaes atravs de tickets

Se por exemplo tivermos um stream e desejarmos que outras aplicaes o possam partilhar, basta associa-lo a um ticket. Se as outras aplicaes conhecerem o ticket podem comunicar e aceder ao stream. Existem a seguintes operaes sobre tickets:
{Connection.offer X T} Cria um ticket T para X. X pode ser uma qualquer entidade Oz. O ticket s pode ser utilizado uma vez, caso se tente usar mais de uma vez gerada uma excepo. {Connection.offerUnlimited X T} Cria um ticket T para a entidade X. O ticket pode ser usado um nmero de vezes ilimitado. {Connection.take T X} Cria uma referncia de uma entidade Oz para o ticket T. Isto quer dizer que, se a aplicao que criar a referncia estiver noutra mquina iro haver comunicaes pela rede.

No exemplo seguinte criado um ticket para um stream:


declare Stream Tkt in {Connection.offerUnlimited Stream Tkt} {Show Tkt}

O ticket fica com um valor semelhante a : x-ozticket://193.10.66.30:9002:SpGK0:U4v/y:s:f:xl Vejamos agora um exemplo de uma outra aplicao, que sendo executada noutra localizao referencia o mesmo stream:
declare Stream in {Connection.take x-ozticket://193.10.66.30:9002:SpGK0:U4v/y:s:f:xl Stream} {Browse Stream}

94

5.1.2 Estruturas de dados persistentes usando pickes


Uma aplicao pode guardar e ler qualquer estrutura de dados que no seja baseada em estados num ficheiro. Os ficheiros podem estar em localizaes remotas, sendo acedidos na forma de URLs. O modulo Pickle implementa estas funcionalidades. O exemplo seguinte ilustra como pode ser criada uma funo e guardada num ficheiro:
declare fun {Fact N} if N=<1 then 1 else N*{Fact N-1} end end {Pickle.save Fact "~pvr/public_html/fact"}

Como o ficheiro est localizado numa rea publica de html, pode ser acedido da seguinte forma:
declare Fact={Pickle.load "http://www.info.ucl.ac.be/~pvr/fact"} {Browse {Fact 10}}

5.1.3 Computaes Remotas e Functors

Uma aplicao pode iniciar um computao numa mquina remota usando os recursos dessa mquina e continuar a interagir com a aplicao. As computaes so definidas como functors, pois o functor permite especificar computaes e recursos. O functor a especificao dum mdulo e da definio dos recursos que esse mdulo necessita de forma explicita.
R={New Remote.manager init(host:"sinuhe.sics.se")} F=functor export x:X define X={Fact 30} end M={R apply(F $)} {Browse M.x}

Em primeiro lugar criada uma referencia mquina remota. Depois constroi-se a computao propriamente dita, que consiste num functor que atribudo a F. O resultado X devolvido ao lado cliente no mdulo M. Outra forma usando o functor com uma referencia externa:
declare F M X in F=functor define {Fact 30 X} end M={R apply(F $)} {Browse X}

95

5.1.4 Servidores
Servidores so computaes que esto em permanente execuo e garantem servios a clientes. Vejamos partindo dum exemplo simples, como podem ser criados servidores com o Mozart-Oz.
% Cria um servidor

declare Str Prt Srv in {NewPort Str Prt} thread {ForAll Str proc {$ S} S="Ol mundo" end} end proc {Srv X} {Send Prt X} end
% O servidor fica disponivel atravs de URL: % (passando o nome do ficheiro que tambm acessvel por URL) {Pickle.save {Connection.offerUnlimited Srv} "/usr/staff/pvr/public_html/hw"}

Um cliente poderia aceder a este servidor da seguinte maneira:


declare Srv in Srv={Connection.take {Pickle.load "http://www.info.ucl.ac.be/~pvr/hw"}} local X in {Srv X} {Browse X} end

Ir mostrar Ol mundo na janela do browser. O cliente conectando-se recebe uma referencia do servidor. Isto faz com que os espaos computacionais do servidor e do cliente sejam fundidos num s, o que faz com que o cliente e o servidor possam comunicar como se estivessem no mesmo processo. No exemplo anterior utilizou-se um port para recolher as mensagens dos clientes. Vejamos agora o mesmo exemplo utilizando objectos estacionrios. O servidor seria :
declare class HelloWorld meth hw(X) X="Hello world" end end Srv={NewStat HelloWorld hw(_)} % Requer um mtodo inicial

O cliente poderia chamar o servidor atravs de {Srv hw(X)}. As entidades estacionrias so muito importantes. O Oz possui duas formas de criar entidades estacionrias. A primeira :

96

Declare Object={NewStat Class init}

Quando executado num site, o NewStat, recebe uma classe e um mtodo inicial e cria um objecto que estacionrio nesse site.

5.1.5 Servidor de computaes

Um servidor de computaes pode ser escrito da seguinte forma, usando objecto estacionrios:
declare class ComputeServer meth init skip end meth run(P) {P} end end C={NewStat ComputeServer init}

O servidor pode ser disponibilizado atravs de uma URL como nos exemplos anteriores. Vejamos agora como um cliente pode usar o servidor de computaes:
declare fun {Fibo N} if N<2 then 1 else {Fibo N-1}+{Fibo N-2} end end % Executa a primeira computao remotamente local F in {C run(proc {$} F={Fibo 30} end)} {Browse F} end % Executa a segunda computao localmente local F in F={Fibo 30} {Browse F} end

Para executar uma computao remota basta usar {C run(P)}. Sendo o Oz completamente transparente o procedimento P pode ter qualquer instruo de Oz, mas se usar recursos deve ser executado remotamente atravs de funtors como j foi visto anteriormente.

5.1.6 Servidor de computaes com funtors


Vejamos como um cliente pode executar um servidor de computaes num site remoto.

97

declare R={New Remote.manager init(host:"rainbow.info.ucl.ac.be")} declare F C F=functor export cs:CS define class ComputeServer meth init skip end meth run(P) {P} end end CS={NewStat ComputeServer init} end C={R apply(F $)}.cs % Arranca o servidor

O cliente pode usar o servidor da seguinte forma:


local F in {C run(proc {$} F={Fibo 30} end)} {Browse F} end

5.1.7 Servidor dinamicamente extensvel


O Oz permite fazer actualizaes num servidor, sem que se tenha de o parar. Em Java no possvel fazer actualizaes deste tipo. Em Mozart-Oz esta actualizao pode at ser feita interactivamente. Um servidor destes pode ser desenvolvido da seguinte forma:
declare proc {NewUpgradableStat Class Init ?Upg ?Srv} Obj={New Class Init} C={NewCell Obj} in Srv={MakeStat proc{$ M} {{Access C} M} end} Upg={MakeStat proc{$ Class2#Init2} {Assign C {New Class2 Init2}}end} end

Retorna um servidor Srv e um procedimento estacionrio Upg que serve para actualizar o servidor. O servidor actualizvel porque todas as chamadas ao objecto so feitas indirectamente atravs de uma clula C. Para criar o servidor basta fazer:
declare Srv Upg in Srv={NewUpgradableStat ComputeServer init Upg}

Vejamos agora do lado cliente, como se pode actualizar o servidor sem o parar. No exemplo cria-se um novo objecto partindo de uma classe nova.

98

declare class CComputeServer from ComputeServer meth run(P Prio<=medium) thread {Thread.setThisPriority Prio} ComputeServer,run(P) end end end Srv2={Upg CComputeServer#init}

O mtodo run sobrecarregado por um novo mtodo que recebe dois parmetros : P que a computao a executar remotamente e Prio que a prioridade da thread que executa a computao P.

5.2 Agentes Mveis


Um Agente uma computao distribuda que est organizada como um conjunto de tarefas. Uma tarefa uma computao que usa recursos de um determinado site. Recursos so por exemplo: sistemas de ficheiros, perifricos, acessos ao sistema operativo, etc... Uma tarefa pode iniciar tarefas noutros sites com os recursos a ser usados bem especificados. O comportamento distribuido do agente decidido pelo prprio agente. Por exemplo, um agente A delega concorrentemente 10 tarefas a sites remotos e espera que todas sejam completadas para continuar. Os servidores de agentes so representado por AS0 a AS9 e o seu trabalho representado por functors contento um procedimento (P0 a P9) de um argumento que ir ser instanciado com o resultado.
declare A=functor define X0 ... X9 {AS0 functor define {P0 X0} end} ... {AS9 functor define {P9 X9} end} {Wait X0} ... {Wait X9} ... end

5.2.1 Instalao de Agentes


Vejamos um exemplo simples dum agente que se desloca a um sitio, interroga o sistema operativo e depois regressa. Em primeiro lugar necessitamos de saber como instalar o servidor de agentes para que o agente se possa deslocar. S depois podemos programar o agente.

99

Para que o agente se possa deslocar a um determinado site, tem de haver nesse site algo que o possa receber. Esse algo so os servidores de agentes. Estes servidores aceitam functors (que representam agentes ou partes de agentes) e aplicam-nos ao site. Para instalar um servidor de agentes num site usamos o functor AgentServer. Quando o AgentServer instalado num site, cria o mdulo AS nesse site com as seguintes operaes: O servidor de agentes acedido por AS.server. Um calculo iniciado assincronamente chamando {AS.server F} onde F o functor onde est especificado o calculo e os recursos que usa. A operao {AS.publishserver FN} cria um ficheiro FN com o ticket do servidor de agentes. A operao {AS.getserver UFN ?AS} quando lhe passada uma URL ou o nome de um ficheiro UFN retorna uma referncia ao respectivo servidor de agentes. Se assumirmos que a seguinte URL conhecida:
http://www.info.ucl.ac.be/~pvr/agents.ozf

e que referencia o funtor AgentServer. O cdigo seguinte cria um servidor de agentes local e torna-o acessvel atravs da URL:
http://www.info.ucl.ac.be/~pvr/as1

Vejamos:
declare GetServer in local % le o AgentServer: AgentServer={Pickle.load http://www.info.ucl.ac.be/~pvr/agents.ozf} % Instala o AgentServer localmente: (cria um servidor de agentes) [AS1]={Module.apply [AgentServer]} % Publica o servidor de agentes {AS1.publishserver /usr/staff/pvr/public_html/as1} in GetServer=AS1.getserver End

Criemos agora um segundo agente. Este ser remoto e acessvel atravs da URL
http://www.info.ucl.ac.be/~pvr/as2 local RF=functor import Pickle Module export done:D define AgentServer={Pickle.load http://www.info.ucl.ac.be/~pvr/agents.ozf} [AS2]={Module.apply [AgentServer]} {AS2.publishserver /usr/staff/pvr/public_html/as2} end RM={New Remote.manager init} M={RM apply(RF $)} in skip end

100

Podemos aceder aos servidores de agentes da seguinte maneira:


declare Server1={GetServer http://www.info.ucl.ac.be/~pvr/as1} Server2={GetServer http://www.info.ucl.ac.be/~pvr/as2}

5.2.2 Programao de agentes

Agora que j esto instalados os servidores podemos comear a programar o agente. O agente ir interrogar o sistema operativo e ir retornar as horas.
declare D1 in {Server2 functor import OS define D1={OS.time} end} {Browse D1}

Primeiro cria a varivel D1 e depois executa um agente num site remoto. O agente apenas necessita de um recurso (OS), que consiste num mdulo que disponibiliza acesso a funes do sistema operativo. Como o agente criado assincronamente, quando o Browse for executado, a varivel D1 pode ainda no estar instanciada. Isto no necessariamente um problema pois no Oz as operaes suspendem enquanto as variveis que necessitam no estiverem instanciadas. Para garantir que a varivel D1 mesmo instanciada pode usar-se {Wait D1}. Modifiquemos agora o primeiro exemplo de forma a obter um agente isolado, isto , o agente um functor que pode ser compilado e executado isoladamente ou por outra aplicao. Assumimos que o functor AgentServer est numa localizao standard.
functor import System AgentServer define GetServer=AgentServer.getserver Server2={GetServer "http://www.info.ucl.ac.be/~pvr/as2"} D1 {Server2 functor import OS define D1={OS.time} end} {System.show D1} end

A nica diferena do exemplo anterior que neste exemplo usa o GetServer para aceder ao servidor de agentes. Se o functor AgentServer no estiver numa localizao standard, o agente isolado tm que explicitamente referenciar a sua localizao.

101

functor import System Pickle Module define AgentServer={Pickle.load "http://www.info.ucl.ac.be/~pvr/agents.ozf"} [AS]={Module.apply [AgentServer]} GetServer=AS.getserver Server2={GetServer "http://www.info.ucl.ac.be/~pvr/as2"} D1 {Server2 functor import OS define D1={OS.time} end} {System.show D1} end

5.2.3 A Definio do servidor de agentes


A ferramenta que atribui funcionalidade aos agentes o AgentServer. Quando instalado, este functor cria um servidor de agentes, com um procedimento de publicao, um procedimento de acesso a qualquer servidor de agentes publicado.
functor import Module Connection Pickle export server:AS publishserver:PublishServer getserver:GetServer define S P={NewPort S} proc {InstallFunctors S} case S of F|S2 then try [_] = {Module.apply [F]} catch _ then skip end {InstallFunctors S2} else skip end end thread {InstallFunctors S} end proc {AS F} {Send P F} end T={Connection.offerUnlimited AS} % O servidor fica disponivel pelo ficheiro FN: % O servidor de agentes assincrono proc {PublishServer FN} {Pickle.save T FN} end % Acesso ao servidor que est no ficheiro/URL UFN: proc {GetServer UFN AS} try T={Pickle.load UFN} in AS={Connection.take T} catch _ then raise serverUnavailable end end end end

102

Existem duas formas de usar o AgentServer. Ou se compila com o compilador e se copia o ficheiro .ozf para uma rea publica (public_html), ou se quisermos usar o AgentServer interactivamente no OPI, temos de alterar o cdigo da seguinte maneira:
declare AgentServer= functor ... (corpo do functor) end {Pickle.save AgentServer "/usr/staff/pvr/public_html/agents.ozf"}

5.3 Tolerncia a falhas


Iremos ver agora como se podem desenvolver aplicaes robustas com tolerncia a falhas.

5.3.1 O servidor de Ol Mundo com tolerncia a falhas


O servidor deve continuar a funcionar mesmo que haja uma falha num dos seus clientes. O cliente deve ser informado de uma falha no servidor em tempo finito atravs de uma excepo serverError. Em seguida iremos ver como se pode construir o servidor de Ol mundo com atravs do modelo bsico de falhas. Neste modelo o sistema gera excepes quando existem tentativas de fazer operaes sobre entidades que tm problemas relacionado com a distribuio. Estas excepes assumem a forma de system(dp(conditions:FS ...) ...), onde FS a lista de estados das falhas. Por omisso o sistema gera excepes se os estados das falhas forem tempFail ou permFail. Vejamos as seguintes abstraces:
{SafeSend Prt X} envia para um port e gera uma excepo serverError se permanentemente impossivel. {SafeWait X T} espera que X seja instanciado e gera uma excepo caso isso seja permanentemente impossvel ou o tempo T seja ultrapassado.

A vantagem de criar estas abstraces que possibilita-nos ter um programa quase igual ao programa sem ser tolerante a falhas. Vejamos o servidor com tolerncia a falhas:

103

declare Str Prt Srv in {NewPort Str Prt} thread {ForAll Str proc {$ S} try S="Hello world" catch system(dp(...) ...) then skip end end} end proc {Srv X} {SafeSend Prt X} end
{Pickle.save {Connection.offerUnlimited Srv} "/usr/staff/pvr/public_html/hw"}

A instanciao de S est na expresso try ... catch para que possa tolerar falhas dos clientes. Vejamos um cliente:
declare Srv try X in try Srv={Connection.take {Pickle.load "http://www.info.ucl.ac.be/~pvr/hw"}} catch _ then raise serverError end end {Srv X} {SafeWait X infinity} {Browse X} catch serverError then {Browse Server down} end

O cliente efectua duas operaes distribudas, o send, que substitudo pelo safeSend dentro de Srv, e o wait que substitudo pelo SafeWait. Se houver problemas no envio ou na recepo de uma mensagem gerada uma excepo serverError. Tambm so geradas excepes se existirem falhas na altura do estabelecimento de conexo, isto , na execuo das operaes Connection.take e Pickle.load. Vejamos agora a definio das operaes SafeSend e SafeWait. As funes FOneOf e FSomeOf sero definidas depois.
declare proc {SafeSend Prt X} try {Send Prt X} catch system(dp(conditions:FS ...) ...) then if {FOneOf permFail FS} then raise serverError end elseif {FOneOf tempFail FS} then {Delay 100} {SafeSend Prt X} else skip end end end

104

Gera uma excepo serverError quando existe uma falha permanente no servidor e quando existe uma falha temporria volta a tentar de 100 em 100 ms.
declare local proc {InnerSafeWait X Time} try cond {Wait X} then skip [] {Wait Time} then raise serverError end end catch system(dp(conditions:FS ...) ...) then if {FSomeOf [permFail remoteProblem(permSome)] FS} then raise serverError end if {FSomeOf [tempFail remoteProblem(tempSome)] FS} then {Delay 100} {InnerSafeWait X Time} else skip end end end in proc {SafeWait X TimeOut} Time in if TimeOut\=infinity then thread {Delay TimeOut} Time=done end end {Fault.enable X thread(this) [permFail remoteProblem(permSome) tempFail remoteProblem(tempSome)] _} {InnerSafeWait X Time} end end

Gera uma excepo serverError quando h uma falha permanente no servidor e tenta de 100 em 100 ms quando a falha temporria. A varivel X existe apenas no cliente e no servidor. Quando sucede remoteProblem(permFail:_ ...) significa que o servidor avariou. Para impedir que o cliente fique a funcionar indefinidamente introduz-se um timeout. As funes FOneOf e FsomeOf foram criadas para simplificar a verificao dos estados das falhas. A funo {FOneOf permFail AFS} true se o estado da falha permFail no conjunto de falhas AFS.
declare fun {FOneOf F AFS} case AFS of nil then false [] AF2|AFS2 then case F#AF2 of permFail#permFail(...) then true [] tempFail#tempFail(...) then true [] remoteProblem(I)#remoteProblem(I ...) then true else {FOneOf F AFS2} end end end

105

A funo {FSomeOf [permFail remoteProblem(permSome)] AFS} retorna true se permFail ou remoteProblem(permSome) ou ambos ocorrem no conjunto AFS.
declare fun {FSomeOf FS AFS} case FS of nil then false [] F2|FS2 then {FOneOf F2 AFS} orelse {FSomeOf FS2 AFS} end end

5.3.2 Tolerncia a falhas em objectos estacionrios


Os objectos estacionrios tm de ter comportamentos bem definidos quando existem falhas. A chamada C={NewSafeStat Class Init} cria um novo servidor C. Se no existirem problemas com a distribuio, a semntica da chamada {C Msg} igual execuo centralizada do objecto incluindo as excepes geradas. Se houver um problema na distribuio que impea de completar a chamada {C Msg} ser gerada a excepo remoteObjectError. Se existir um problema na comunicao com o cliente, o servidor tenta a comunicao por um curto perodo de tempo e depois desiste. Isto no ir afectar a execuo do servidor. Em primeiro lugar vamos criar um objecto estacionrio remoto. A classe seguinte define um contador.
declare class Counter attr i meth init i<-0 end meth get(X) X=@i end meth inc i<-@i+1 end end

O functor definido a seguir cria uma instancia da classe usando o NewSafeStat, Note-se que o objecto ainda no criado.
declare F=functor import Fault export statObj:StatObj define {Fault.defaultEnable nil _} StatObj={NewSafeStat Counter init} End

O mdulo Fault importado porque queremos garantir que ir ser usado o Fault do site onde o functor instalado.

106

De seguida iremos criar um site remoto e uma instancia da classe Counter chamada StatObj. A classe Remote.manager d-nos a possibilidade de criar de vrias formas um site remoto. Neste exemplo usamos a opo fork:sh, que cria um processo novo na mesma mquina. O processo acessvel atravs do gestor de mdulo MM, que permite instalar functors no site remoto.
declare MM={New Remote.manager init(fork:sh)} StatObj={MM apply(F $)}.statObj

Finalmente podemos chamar o objecto. A chamada feita dentro de try catch para demonstrar a tolerncia a falhas. Se matarmos o processo remoto, veremos que continua a funcionar.
try {StatObj inc} {StatObj inc} {Show {StatObj get($)}} catch X then {Show X} end

5.3.3 Tolerncia a falhas usando guardas


A melhor maneira mais simples de implementar tolerncia a falhas em objecto estacionrios atravs de guardas (Guards). Um guarda vigia uma computao. Se existir uma falha de distribuio o guarda termina essa computao. O procedimento {Guard E FS S1 S2} guarda a entidade para os estados de falhas FS dentro da instruo S1, substituindo S1 por S2 caso uma falha ocorra. A definio de NewSafeStat idntica definio de NewStat s que a primeira usa guardas em todas as suas operaes distribudas. O exemplo seguinte mostra um objecto estacionrio baseado em guardas.
proc {MakeStat PO ?StatP} S P={NewPort S} N={NewName} in % Interface com o Servidor: % o cliente gera uma excepo se houver um problema % no servidor proc {StatP M} R in {Fault.enable R thread(this) nil _} {Guard P [permFail] proc {$} {Send P M#R} if R==N then skip else raise R end end end proc {$} raise remoteObjectError end end} end % Implementao do Servidor % O servidor termina o pedido do cliente se % se houver um problema com o cliente thread {ForAll S proc{$ M#R} thread RL in try {PO M} RL=N catch X then RL=X end {Guard R [permFail

107

remoteProblem(permSome)] proc {$} R=RL end proc {$} skip end} end end} end end proc {NewSafeStat Class Init Object} Object={MakeStat {New Class Init}} end

Vejamos agora a definio de Guarda. O guarda permite-nos substituir uma instruo S1 por outra S2 caso exista uma falha. O procedimento {Guard E FS S1 S2} primeiro desliga todas as excepes em E. Depois executa S1 com um watcher local W. Se o watcher for chamado durante a execuo de S1, ento S1 interrompido e gerada uma excepo N. Consequentemente S2 executado.
declare proc {Guard E FS S1 S2} N={NewName} T={Thread.this} proc {W E FS} {Thread.injectException T N} end in {Fault.enable E thread(T) nil _} try {LocalWatcher E FS W S1} catch X then if X==N then {S2} else raise X end end end end

Um watcher local um watcher que instalado apenas durante a execuo de uma instruo. Quando a instruo termina ou gerada uma excepo o watcher removido. O procedimento {LocalWatcher E FS W S} vigia a entidade E para os estados de falhas FS com o watcher W durante a execuo de S.
declare proc {LocalWatcher E FS W S} {Fault.installWatcher E FS W _} try {S} finally {Fault.deInstallWatcher E W _} end end

108

5.3.4 Tolerncia a falhas baseada em excepes


Iremos ver como implementar o NewSafeStat usando tolerncia a falhas baseada em excepes. Este modelo de tolerncia a falhas usado por outras linguagens de programao com por exemplo o Java.
declare proc {MakeStat PO ?StatP} S P={NewPort S} N={NewName} EndLoop TryToBind in % Interface do cliente com o servidor % O cliente chama o servidor % Sincronizao do cliente com o servidor % Implementao do servidor % Ciclo principal do servidor % Sincronizao com o cliente end

proc {NewSafeStat Class Init ?Object} Object={MakeStat {New Class Init}} end

Primeiro o cliente envia uma mensagem ao servidor com uma varivel de sincronizao. Esta varivel usada para sinalizar que o servidor terminou a chamada ao objecto. Se foram geradas excepes, estas so passadas ao cliente. Se existir uma falha permanente no envio, gerada a excepo remoteObjectError, caso exista uma falha temporria o envio e retentado de 100 em 100 ms.
% O cliente chama o servidor proc {StatP M} R in try {Send P M#R} catch system(dp(conditions:FS ...) ...) then if {FOneOf permFail FS} then raise remoteObjectError end elseif {FOneOf tempFail FS} then {Delay 100} {StatP M} else skip end end {EndLoop R} end

O cliente espera que o servidor instancie uma varivel de sincronizao. No caso de existir uma falha permanente gerada uma excepo, caso exista uma falha temporria espera 100 ms e tenta de novo.

109

% Sincronizao do cliente com o servidor proc {EndLoop R} {Fault.enable R thread(this) [permFail remoteProblem(permSome) tempFail remoteProblem(tempSome)] _} try if R==N then skip else raise R end end catch system(dp(conditions:FS ...) ...) then if {FSomeOf [permFail remoteProblem(permSome)] FS} then raise remoteObjectError end elseif {FSomeOf [tempFail remoteProblem(tempSome)] FS} then {Delay 100} {EndLoop R} else skip end end end

O servidor cria uma nova thread para cada cliente.


% Ciclo principal do servidor thread {ForAll S proc {$ M#R} thread try {PO M} {TryToBind 10 R N} catch X then try {TryToBind 10 R X} catch Y then skip end end end end } end % Sincronizao com o cliente proc {TryToBind Count R N} if Count==0 then skip else try R=N catch system(dp(conditions:FS ...) ...) then if {FOneOf tempFail FS} then {Delay 2000} {TryToBind Count-1 R N} else skip end end end end

110

6 Programao Grfica
Em Mozart a programao do interfaces grficas baseada em Widgets que consistem em objectos que representam entidades graficas, como por exemplo janelas, menus, botes, etc... As janelas so descritas composicionalmente atravs de hierarquias de objectos e esto sujeitas modificaes dinmicas e interactivas. Este tipo de programao consiste num interface orientado a objectos para o Tk. Todas as caractersticas do Oz so aplicveis a este tipo de programao, tais como concorrncia e entidades first-class. Do Tk o interface herda um conjunto de abstraces bastante poderosas. O objectivo deste capitulo apenas de introduzir uma ideia geral de como pode ser construdo um interface grfico em Mozart-Oz.

6.1 Toplevel e Objectos Widget


O objecto TopLevel consiste num contentor de outros objectos e pode ser inicializado da seguinte maneira:
W={New Tk.toplevel tkInit(width:150 height:50)}

Podem enviar-se mensagens para um widget para poder alterar uma das suas propriedades, por exemplo para mudar a cor do fundo do TopLevel faramos:
{W tk(configure background:purple)}

Por exemplo a mensagem tkClose serve para fechar o widget, removendoo consequentemente do ecr.

6.1.1 Frames
Os frames tm caractersticas idnticas ao toplevel, a diferena que os frames so contentores dentro de outros widgets. As opes relief e border permitem dar um aspecto tridimensional ao frame. A opo relief pode ter os seguintes valores: groove, ridge, flat, sunken, e raised.
Fs={Map [groove ridge flat sunken raised] fun {$ R} {New Tk.frame tkInit(parent:W width:2#c height:1#c relief:R borderwidth:4)} end} {{Nth Fs 3} tk(configure background:black)} {Tk.send pack(b(Fs) side:left padx:4 pady:4)}

Este exemplo cria um frame para cada tipo de relief.

111

Todos os widgets necessitam uma referencia a um widget pai com a excepo do toplevel.

6.1.2 Labels
Um label serve para escrever texto ou uma imagem (bitmap) numa janela. Vejamos um exemplo em que criado um label com uma imagem e outro com texto.
L1={New Tk.label tkInit(parent:W bitmap:info)} L2={New Tk.label tkInit(parent:W text:Labels: bitmaps and text)} {Tk.send pack(L1 L2 side:left padx:2#m pady:2#m)}

Existem alguns bitmaps predefinidos, vejamos este exemplo:


{List.forAllInd [error gray75 gray50 gray25 gray12 hourglass info questhead question warning] proc {$ I D} R=(I-1) div 5 C=(I-1) mod 5 in {Tk.batch [grid(row:R*2 column:C {New Tk.label tkInit(parent:W bitmap:D)}) grid(row:R*2+1 column:C {New Tk.label tkInit(parent:W text:D)})]} end}

As fonts podem ser especificadas de uma forma dependente da plataforma, por exemplo se a plataforma for unix as fonts podem ser especificadas por um dos nomes que o comando xlsfonts devolve. No exemplo seguinte podemos ver um outro mtodo de especificar fonts independentemente da plataforma.
{ForAll [times helvetica courier] proc {$ Family} {ForAll [normal bold] proc {$ Weight} F={New Tk.font tkInit(family: Family weight: Weight size: 12)} L={New Tk.label tkInit(parent: W text: A #Weight# #Family# font. font: F)} in {Tk.send pack(L)} end} end

112

6.1.3 Imagens
A diferena entre as imagens e os bitmaps que as imagens podem ter mais de duas cores. Podemos usar os labels para mostrar imagens numa janela.
D ={Property.get oz.home}#/doc/wp/ I ={New Tk.image tkInit(type:photo format:ppm file:D#truck-left.ppm)} L1={New Tk.label tkInit(parent:W image:I)} L2={New Tk.label tkInit(parent:W image:I)} L3={New Tk.label tkInit(parent:W image:I)} {Tk.send pack(L1 L2 L3 padx:1#m pady:1#m side:left)}

As imagens podem ser de dois tipos : photo pode mostrar imagens com dois formatos: gif e ppm. bitmap o ficheiro referenciado tem que estar no formato bitmap. As imagens podem em vez de serem lidas de ficheiros, podem ser lidas a partir de uma URL da seguinte maneira:
{New Tk.image tkInit(type:photo format:gif url:http://foo.com/bar.gif}

6.1.4 Mensagens
As mensagens servem para mostrar texto em vrias linhas. O proximo exemplo mostra deferentes combinaes de justificaes e do aspecto condicionado pela opo aspect.
S =Text extending over several lines. Ms={Map [left#200 center#100 right#50] fun {$ J#A} {New Tk.message tkInit(parent:W text:S justify:J aspect:A)} end} {Tk.send pack(b(Ms) side:left padx:2#m pady:2#m)}

6.2 Gestores de Geometria


Os gestores de geometria servem para determinar quanto espao os widgets ocupam e em que posio aparecem no ecr.

6.2.1 O Packer
O Packer suporta uma organizao de widgets em linhas e colunas. Vejamos um exemplo de aplicao do packer:

113

fun {NewLabels} W={New Tk.toplevel tkInit(background:white)} in {Map [label Second label widget 3rd label] fun {$ A} {New Tk.label tkInit(parent:W text:A)} end} end

Para os labels aparecerem no toplevel o packer pode ser invocado da seguinte maneira:
[L1 L2 L3] = {NewLabels} {Tk.send pack(L1 L2 L3)}

O packer tambm pode ser chamado usando um batch tickle. Um batch tickle um tuplo com um identificador b e argumento uma lista de tickles. Os tickles so mensagens enviadas ao motor grfico.
{Tk.send pack(b({NewLabels}))}

Existe uma opo (side) no packer que permite escolher a orientao dos widgets. O valor por omisso top. No exemplo seguinte os labels em vez de serem desenhados de cima para baixo, so desenhados da esquerda para a direita.
{Tk.send pack(b({NewLabels}) side:left)}

As reas ocupadas com os widgets podem ser expandidas de duas formas: interna ou externa. Espao adicional externo pode ser especificado com as opes padx e pady, e corresponde ao espao disponibilizado pela janela pai em redor dos widgets. Os valores usado nestas opes devem ser distncias de ecr. Distncias de ecr so distancias em pixeis, a no ser que antes tenham um c, m, i ou um p, que significam que as distancias so respectivamente em centmetros, milmetros, polegadas e pontos de impresso. As distancias de ecr podem ser especificadas com strings virtuais. Para especificar espao adicional interno, ou seja, o espao que cada cada um expande nas suas 4 bordas, usa-se o ipadx e o ipady.
{Tk.send pack(b({NewLabels}) padx:1#m pady:1#m)} {Tk.send pack(b({NewLabels}) ipadx:2#m ipady:2#m)}

A opo anchor em que posio dentro da parcela do packer o widget ir ser colocado. As posies podem ser center, n, s, w, e, nw, ne, sw, and se. Por omisso o widget colocado na posio central. O espao expandido pode ser preenchido totalidade do espao da parcela para isso usa-se a opo fill. Os valores possiveis so x,y e both.
{Tk.send pack(b({NewLabels}) anchor:w padx:1#m pady:1#m)} {Tk.send pack(b({NewLabels}) fill:x)}

114

6.2.2 Grids

As grids organizam os widgets na forma de grelhas. Para cada widget poder ser posicionado na grelha necessrio passar-lhe o nmero de linha e colunas desejadas. Todas as posio na mesma coluna tm o mesmo tamanho e na mesma linha tm a mesma altura. As grids tambm suportam as opo que vimos anteriormente para o packer.
proc {GL W R C S} L={New Tk.label tkInit(parent:W text:S)} in {Tk.send grid(L row:R column:C padx:4 pady:4)} end {GL W 1 1 nw} {GL W 1 2 north} {GL W 1 3 ne} {GL W 2 1 west} {GL W 2 3 east} {GL W 3 1 sw} {GL W 3 2 south} {GL W 3 3 sw}

A opo columnspan e rowspan permite que o widget ocupe mais de uma coluna ou mais de uma linha. Vejamos um exemplo:
{Tk.send grid({New Tk.label tkInit(parent:W text:Upper left)} row:1 rowspan:2 column:1 columnspan:2 padx:4 pady:4)} {GL W 1 3 ne} {GL W 2 3 east} {GL W 3 1 sw} {GL W 3 2 south} {GL W 3 3 sw}

A opo sticky especifica simultaneamente o widget ocupa todo o espao da parcela e a sua posiao dentro desta.
{Tk.send grid({New Tk.label tkInit(parent:W text:Upper left)} row:1 rowspan:2 column:1 columnspan:2 sticky: nse padx:4 pady:4)}

6.3 Outros Widgets


A seguir iremos ver alguns dos widgets interactivos disponibilizados pelo mdulo Tk do Mozart-Oz.

115

6.3.1Botes e Aces
Os botes so bastante similares aos labels, a grande diferena que os botes desencadeiam aces: quando se carrega no boto do rato por cima do boto desencadeada uma aco que consiste ou num procedimento ou num par objecto e mensagem. As aces podem ser alteradas ou apagadas com o mtodo tkAction. Vejamos um exemplo em que a aco do boto B1 eliminada, e a aco do boto B2 alterada.
{B1 tkAction} {B2 tkAction(action: B1 # tkClose)}

Vejamos um exemplo de botes:


B1={New Tk.button tkInit(parent: W text: Press me! action: proc {$} {Browse pressed} end)} B2={New Tk.button tkInit(parent: W bitmap: error action: W#tkClose)} {Tk.send pack(B1 B2 fill:x padx:1#m pady:1#m)}

6.3.2 CheckButtons, RadioButtons e variveis


Os checkbuttons so usados para escolhas binrias, isto , podem estar activados ou desactivados. O estado deste tipo de botes dado por uma varivel de tickle. Uma varivel de tickle um objecto tickle que disponibiliza mtodos para interrogar e modificar o estado do boto. Os radiobuttons so usados para escolhas no binrias, isto , vrios botes so agrupados e escolhendo um dos botes do grupo todos os outros sero desactivados. Usam uma varivel de tickle para representar o estado do grupo de botes. Vejamos um exemplo de uma interrogao o estado dos checkbuttons ou dos radiobuttons:
{Browse state(bold: {V1 tkReturnInt($)}==1 family: {V2 tkReturnAtom($)})}

Vejamos um exemplo de checkbuttons e de radiobuttons:


V1={New Tk.variable tkInit(false)} C ={New Tk.checkbutton tkInit(parent:W variable:V1 text:Bold anchor:w)} V2={New Tk.variable tkInit(Helvetica)} Rs={Map [Times Helvetica Courier] fun {$ F} {New Tk.radiobutton tkInit(parent:W variable:V2 value:F text:F anchor:w)} end} {Tk.batch [grid(C padx:2#m columnspan:3) grid(b(Rs) padx:2#m)]}

116

As aces nestes tipos de botes podem ser usadas da mesma forma que nos botes convencionais. Como j foi visto anteriormente a forma de aceder ao valor das variveis de tickle atravs dos mtodos tkReturnInt e tkReturnAtom. Vejamos um exemplo:
fun {GetWeight} if {V1 tkReturnInt($)}==1 then bold else normal end end F={New Tk.font tkInit(size:24 family: {V2 tkReturn($)} weight: {GetWeight})} L={New Tk.label tkInit(parent:W text:A test text. font:F)} {C tkAction(action: proc {$} {F tk(configure weight:{GetWeight})} {L tk(configure font:F)} end)} {List.forAllInd [Times Helvetica Courier] proc {$ I Family} {{Nth Rs I} tkAction(action: proc {$} {F tk(configure family:Family)} {L tk(configure font:F)} end)} end}

As opes dos widgets tambm podem ser interrogadas. Para isso usa-se o seguinte:
{T tkReturnListAtom(configure bg:unit $)}

Neste exemplo estamos a interrogar a opo bg do widget T. Vejamos que fazer para interrogar os parmetros de um widget. O exemplo seguinte interroga a posio e a geometria de um widget T.
{Browse {Map [rootx width rooty height] fun {$ A} {Tk.returnInt winfo(A T)} end}}

6.3.3 Menus

As entradas de menus no so widgets. No so geridos pelo gestor de geometria. Quando uma entrada menu criado imediatamente visualizado num menu pai. O prximo programa cria dois widgets M1 e M2. A primeira entrada de menu est configurada para mostrar M2 em cascata. A opo tearoff:false impede que o menu seja cria numa janela prpria.
Cs =[Wheat Firebrick Navy Darkorange] M1 ={New Tk.menu tkInit(parent:W tearoff:false)}

117

M2 ={New Tk.menu tkInit(parent:M1 tearoff:false)} E1 ={New Tk.menuentry.cascade tkInit(parent:M1 label:Background Color menu:M2)} E2 ={New Tk.menuentry.separator tkInit(parent:M1)} E3 ={New Tk.menuentry.command tkInit(parent:M1 label:Quit action: W#tkClose)} V ={New Tk.variable tkInit(Cs.1)} CEs={Map Cs fun {$ C} {New Tk.menuentry.radiobutton tkInit(parent:M2 label:C var:V val:C action: W#tk(configure bg:C))} end}

Normalmente os menus no so visveis. Para que o menu seja por exemplo visvel no canto superior esquerdo faz-se:
{M1 tk(post 0 0)}

No mdulo Tktools existem abstraces para criar de forma bastante simples barras de menus. Para criar menus popup usa-se o comando tk_popup. Recebe como parmetros o widget do menu e as coordenadas onde suposto aparecer. De seguida iremos ver como se associam eventos a widgets, por exemplo, para fazer aparecer o menu quando se pressiona o boto direito do rato.

6.3.4 Eventos
Para associar uma aco a um widget usa-se o comando tkbind. A aco invocada quando um determinado evento sucede. Vejamos o que teramos de fazer para que, no exemplo anterior, o menu seja visualizado quando se pressionar o boto do rato sobre o toplevel.
{W tkBind(event: <Button-1> args: [int(x) int(y)] action: proc {$ X Y} TX={Tk.returnInt winfo(rootx W)} TY={Tk.returnInt winfo(rooty W)} in {Tk.send tk_popup(M1 X+TX Y+TY)} end)}

A seguir opo event especificado o tipo de evento chamado de padro de evento, na opo args especificada uma lista com os paramentos do evento. Os padres tm a seguinte forma:
< Um caracter > Significa que a tecla correspondente a esse caracter foi primida <#Modifier#-#Modifier#-#Type#-#Detail#> ou <#Type#> ou apenas <#Detail#>

118

Exemplos de modifiers:
Control Shift Lock Meta Alt Button1, B1 Button2, B2 Button3, B3 Button4, B4 Button5, B5 Double Triple

Exemplos de tipos de eventos:


Key, KeyPress Tecla primida KeyRelease Tecla libertada Button, ButtonPress Boto do rato primido ButtonRelease mouse Boto do rato libertado Enter mouse pointer O ponteiro do rato entrou num widget Leave mouse pointer O ponteiro do rato sau de um widger Motion mouse pointer O ponteiro do rato foi movido no interior de um windget

Se no for especificada nenhuma aco na respectiva opo, a aco que anteriormente estava associada a esse evento ser eliminada. Para adicionar aces a um evento usa-se a opo append, que significa que a nova aco especificada no ir ser sobreposta anterior. Os Listeners garantem que as mensagem das aces so processadas pela ordem que so invocadas. Podem ser criados derivando uma classe da classe Tk.listener.
L ={New class $ from Tk.listener meth b1 {Browse b1} end meth b2 {Browse b2} end end tkInit} B1={New Tk.button tkInit(parent:W text:One action: L#b1)} B2={New Tk.button tkInit(parent:W text:Two action: L#b2)} {Tk.send pack(B1 B2 side:left)}

Neste exemplo garantimos que os mtodos b1 e b2 so executados com a mesma ordem que os botes correspondentes so primidos.

6.3.5 Entries
Uma entry permite-nos inserir uma linha de texto. Vejamos um exemplo:
L={New Tk.label tkInit(parent:W text:File name:)} E={New Tk.entry tkInit(parent:W width:20)} {Tk.batch [pack(L E side:left pady:1#m padx:1#m) focus(E)]}

O comando focus serve para atribuir o foco entry para que o texto digitado aparea nesse widget. Para ler o contedo da entry usa-se o comando get da seguinte forma:
{Browse {E tkReturnAtom(get $)}}

119

6.3.6 Scales

O scale widget, permite seleccionar um determinado valor movendo um slider que desliza sobre uma barra. Cada vez que o slider movido, um aco associada invocada com um nico argumento, que o valor para o qual o slider aponta. Vejamos um exemplo que cria trs barras com as corres RGB.
L ={New class $ from Tk.listener attr red:0 green:0 blue:0 meth bg(C I) C <- I {F tk(configure bg:c(@red @green @blue))} end end tkInit} F ={New Tk.frame tkInit(parent:W height:2#c)} Ss={Map [red green blue] fun {$ C} {New Tk.scale tkInit(parent:W orient:horizontal length:8#c label: C from:0 to:255 action: L # bg(C) args: [int])} end} {Tk.send pack(b(Ss) F fill:x)}

6.3.7 Listboxes
Uma listbox mostra uma lista de strings e permite o utilizador seleccionar uma delas. Para associar a uma listbox um elevador, usa-se o procedimento Tk.addYScrollbar. Este procedimento associa os eventos do elevador s strings visveis na listbox. Vejamos um exemplo:
L={New Tk.listbox tkInit(parent:W height:6)} {L tkBind(event: <1> action: proc {$} I={L tkReturn(curselection $)} C={L tkReturn(get(I) $)} in {L tk(configure bg:C)} end)} S={New Tk.scrollbar tkInit(parent:W)} {ForAll NomesDeCores proc {$ C} {L tk(insert end C)} end} {Tk.addYScrollbar L S} {Tk.send pack(L S fill:y side:left)}

120

6.3.8 Manipular o toplevel

Existem vrios comandos para manipular a janela toplevel. Por exemplo o comando seguinte:
{Tk.send wm(iconify T)}

Inconifica a janela toplevel T. Para desiconificar faz-se:


{Tk.send wm(iconify T)}

Estes comando correspondem a opes quando o toplevel criado. Por exemplo:


W={New Tk.toplevel tkInit(title:Meu Titulo)}

Cria uma janela toplevel com o titulo Meu Titulo. Para se poder criar um toplevel que no aparea imediatamente quando criado no ecr, e s apareca depois de todos os widgets que ir conter serem criados, faz-se da seguinte maneira:
W={New Tk.toplevel tkInit(withdraw:true)}

Para que o toplevel seja visualizado faz-se:


{Tk.send wm(deiconify W)}

6.3.9 DialogBoxes predefinidas


O tk proporciona algumas DialogBoxes predefinidas para algumas operaes, por exemplo para seleccionar um ficheiro, pode ser utilizado o commando tk_getOpenFile, para gravar um ficheiro, pode ser utilizado o comando tk_getSaveFile. Vejamos um exemplo:
case {Tk.return tk_getOpenFile} of nil then skip elseof S then {Browse file({String.toAtom S})} end

6.4 O Canvas
Os widgets Canvas servem para manipular entidades grficas, tais como linhas, arcos, pontos, etc...

121

6.4.1 Criar um grfico de barras

Para ser mais fcil entender como funciona o canvas iremos partir de um exemplo prtico. Antes de um elemento ser criado pelo mtodo bars, o canvas configurado de forma a que o grfico de barras caiba na zona definida com elevadores. O mtodo drawBars cria para cada elemento da lista Ys um rectngulo e um texto correspondendo a um determinado valor. O valor de O usado como opo na criao do rectngulo. Este valor depende de Tk.isColor que true se o ecr for colorido e false se no for colorido. Isto serve para determinar as corres dos rectngulos. Se o ecr for a preto e branco os rectngulos so preenchidos com listas. Cada canvas identificado por um valor inteiro que pode ser obtido pelo mtodo tkReturnInt. Para ser mais fcil manipular elementos ou grupos de elementos do canvas existe o conceito de etiquetas (Tags) que podem ser manipuladas como objectos. Uma etiqueta pode ser criada atravs da classe Tk.canvasTag. Por exemplo:
R={New Tk.canvasTag tkInit(parent:C)} {C tk(create rectangle 10 10 40 40 fill:red tags:R)}

Se quisermos adicionar outro elemento etiqueta podemos fazer:


{C tk(create oval 20 20 40 40 tags:R)}

Para manipular simultaneamente todos os elementos de uma etiqueta podemos fazer por exemplo:
{R tk(move 40 0)}

Para configurar um elemento do canvas pode usar-se o comando itemconfigure que semelhante ou configure para os widgets. As cores da oval e do rectngulo do exemplo anterior podem ser modificadas usando:
{R tk(itemconfigure fill:wheat)} % preto e branco com listas {O tk(itemconfigure fill:blue)} % cor azul

Para eliminar um determinado elemento do canvas pode fazer-se:


{O tk(delete)}

Apaga todas as ovais associadas etiqueta O. Vejamos agora o exemplo do programa do grfico de barras:

122

local O=if Tk.isColor then o(fill:wheat) else o(stipple:gray50 fill:black) end D=10 D2=2*D B=10 in class BarCanvas from Tk.canvas meth DrawBars(Ys H X) case Ys of nil then skip [] Y|Yr then {self tk(create rectangle X H X+D H-Y*D2 O)} {self tk(create text X H+D text:Y anchor:w)} {self DrawBars(Yr H X+D2)} end end meth configure(SX SY) {self tk(configure scrollregion:q(B ~B SX+B SY+B))} end meth bars(Ys) WY=D2*({Length Ys}+1) HY=D2*({FoldL Ys Max 0}+1) in {self configure(WY HY)} {self DrawBars(Ys HY D)} end end end

Pode ser utilizado da seguinte forma:


C={New BarCanvas tkInit(parent:W bg:white width:300 height:120)} H={New Tk.scrollbar tkInit(parent:W orient:horizontal)} V={New Tk.scrollbar tkInit(parent:W orient:vertical)} {Tk.addXScrollbar C H} {Tk.addYScrollbar C V} {Tk.batch [grid(C row:0 column:0) grid(H row:1 column:0 sticky:we) grid(V row:0 column:1 sticky:ns)]} {C bars([1 3 4 5 3 4 2 1 7 2 3 4 2 4 5 6 7 7 8 4 3 5 6 7 7 8 4 3])}

6.4.2 Eventos

De modo similar aos widgets, tambm se podem utilizar eventos associados etiquetas. Um evento associado a uma etiqueta significa que fica associado a todos o elementos que ela referencia. Vejamos a atribuio de eventos a uma oval:
Colors={New class $ from BaseObject attr cs:(Cs=red|green|blue|yellow|orange|Cs in Cs) meth get(?C) Cr in C|Cr = (cs <- Cr) end end noop} {O tkBind(event: <3> action: proc {$} {O tk(itemconfigure fill:{Colors get($)})} end)}

123

6.5 Caixas de Texto


As caixas de texto, ao contrario das entries, suportam texto em mais de uma linha. Existem vrios comando para a manipulao do texto.

6.5.1 Manipulao do texto


Para inserir texto numa caixa de texto usa-se o seguinte:
T={New Tk.text tkInit(parent:W width:28 height:5 bg:white)} {T tk(insert end "Este texto ir aparecer na caixa de texto.")}

Para no serem tomados em conta os espaos entre as palavras, mas preservando os limites das palavras, faz-se o seguinte:
{T tk(configure wrap:word)}

Pores do texto podem ser associadas a tickles p(L C), em que L a linha e C a coluna. As pores texto pode ser acedidas usando o seguinte:
{T tkReturnAtom(get p(1 4) p(1 9) $)}

As pores tambm podem ser usadas para determinar onde iremos inserir texto.
{T tk(insert p(1 4) "texto")}

Da mesma forma podemos eliminar texto. No exemplo o texto ente as posies p(1 4) e p(1 14) eliminado.
{T tk(delete p(1 4) p(1 14))}

Para se impedir que o utilizador insira texto faz-se:


{T tk(configure state:disabled)}

6.5.2 Etiquetas de texto e marcas


Tal como os canvas as caixas de texto suportam etiquetas. A diferena que as etiquetas, em vez de referenciarem elementos, referenciam conjuntos de caracteres. Vejamos como podem ser criadas as etiquetas:

124

B={New Tk.textTag tkInit(parent:T foreground:brown)}

Todos os caracteres associados a esta etiqueta iro ser castanhos. Pode-se adicionar texto a uma etiqueta da seguinte maneira:
{B tk(add p(1 10) p(1 15))}

O prximo exemplo mostra como se pode alterar a configurao de uma etiqueta.


{B tk(configure font:{New Tk.font tkInit(size:18)})}

O comando insert tambm pode ser utilizado para inserir ou adicionar texto directamente a uma etiqueta.
{T tk(insert end "\nTexto1 ")} {T tk(insert end "Texto2" B)} {T tk(insert end " Texto3.")}

As marcas so idnticas s etiquetas, s que em vez de referenciarem caracteres, referenciam posies no texto. A classe que suporta as marcas Tk.textMark.

6.6 Ferramentas para o Tk


O mdulo TkTools possui diversas abstraces que facilitam a implementao de interfaces grficos.

6.6.1 Dialogs
Dialogs ou caixas de dialogo consistem em janelas que possuem alguma informao grfica e botes. Para criar este tipo de janelas usa-se a classe TkTools.dialog. A criao da dialog contem uma opo tiltle para o titulo. Os botes so especificados em listas de pares, onde o primeiro par o boto mais direita. Estes pares so constitudos pelo identificador do boto e a aco correspondente. A aco pode ser um procedimento ou por exemplo o tomo tkClose que envia uma mensagem para fechar a dialog. Pode tambm ser um tuplo com o identificador tkClose, que significa que antes de ser fechada a dialog executado o argumento do tuplo, que pode ser um procedimento. A opo default especifica qual o boto escolhido por omisso. Vejamos um exemplo de uma dialog para apagar um ficheiro.
D={New TkTools.dialog tkInit(title: Eliminar um Ficheiro buttons: [Ok # proc {$} try

125

{OS.unlink {E tkReturn(get $)}} {D tkClose} catch _ then skip end end Cancelar # tkClose] default: 1)} L={New Tk.label tkInit(parent:D text:Nome do Ficheiro:)} E={New Tk.entry tkInit(parent:D bg:wheat width:20)} {Tk.batch [pack(L E side:left pady:2#m) focus(E)]}

6.6.2 Mensagens de Erro


Para produzir mensagens de erro existe a classe TkTools.error que uma subclasse de TkTools.dialog. Vejamos um exemplo da utilizao desta classe:

E={New TkTools.error tkInit(master:W text: Erro na configurao do sistema: # demasiada memria.)}

6.6.3 Barras de Menus


Uma barra de menus um frame com vrios widgets botes de menus. Cada widget botes de menu tm associados um menu. O menu contm items de menus ou entradas, que podem ser radiobuttons, checkbutton, comandos, separadores, ou outros menus associados em cascata. Podem ser usados aceleradores de teclado que podem ser usados para invocar opes do menu. Para criar barras de menus, o mdulo TkTools tem o procedimento TkTools.menubar, onde so especificados os menus e os aceleradores de teclado. Vejamos um exemplo:
V={New Tk.variable tkInit(0)} B={TkTools.menubar W W [menubutton(text:Test underline:0 menu: [command(label: About test action: Browse#about key: alt(a) feature: about) separator command(label: Quit action: W#tkClose key: ctrl(c) feature: quit)] feature: test) menubutton(text:Options underline:0 menu: [checkbutton(label: Incremental var: {New Tk.variable tkInit(false)}) separator cascade(label: Size menu: [radiobutton(label:Small

126

var:V value:0) radiobutton(label:Middle var:V value:1) radiobutton(label:Large var:V value: 2)])])] nil} F={New Tk.frame tkInit(parent:W width:10#c height:5#c bg:ivory)} {Tk.send pack(B F fill:x)}

A opo feature associa propriedades aos botes de menu e aos items de menu, de forma a que seja acessveis. Podem ser usadas por exemplo para desactivar uma opo:
{B.test.about tk(entryconfigure state:disabled)}

A propriedade menu permite aceder a submenus:


{B.test.menu tk(configure tearoff:false)}

Um menu pode ser expandido da seguinte forma:


A={New Tk.menuentry.command tkInit(parent:B.test.menu before:B.test.quit label: Exit)}

A opo pode ser removida com:


{A tkClose}

6.6.4 Listas de Imagens


Uma maneira de criar imagens usando TkTool.images. Recebe uma lista de URLs e retorna uma registo de imagens, onde os campos so tomos derivados das URLs. O tipo das imagens depende da extenso das URLs. Vejamos um exemplo:
U=http://www.mozart-oz.org/home-1.1.0/doc/wp/ I={TkTools.images [U#wp.gif U#queen.xbm U#truck-left.ppm]}

127

7. Concluso
Penso que este tutorial uma boa ferramenta para introduzir o sistema de programao Mozart-Oz. Obviamente, devido vastido de assuntos que so includos neste tutorial, no possvel detalh-los a todos com maior profundidade. No entanto, escolhi focar as questes que me pareceram mais fundamentais, quer para entender como se programa com esta poderosa linguagem de programao, que o Oz, quer para compreender os modelos subjacentes a essa programao. Tive algumas duvidas em incluir o capitulo dedicado programao grfica, pois no me pareceu um aspecto fundamental da introduo programao neste sistema. Decidi inclu-lo porque hoje em dia, quando se estuda uma linguagem de programao e no se foca os aspecto grficos dessa linguagem normalmente desmotivador. Para alm disso, relativamente fcil programar interfaces grficos em Mozart, porque as abstraces de programao so simples de compreender e so baseadas no Tk. O modelo de programao distribuda na minha opinio aquilo que torna este sistema de programao realmente brilhante. Isto obviamente possvel graas s extraordinrias caractersticas que a linguagem de programao Oz possui, nomeadamente o facto de tudo em Oz ser first-class, o que possibilita que aplicaes distribudas sejam praticamente iguais a aplicaes centralizadas, isto , separa-se completamente as questes da distribuio da funcionalidade dos programas. Eu penso que o Oz marcar certamente uma nova era na programao. As linguagens de programao convencionais no conseguem dar respostas a todos os problemas. Existe nos dias de hoje uma tendncia para a programao de computaes mveis e programao de agentes que possam existir em qualquer site e movimentarem-se numa rede local ou na internet. Creio que o Oz, apesar de algumas lacunas que ainda tem, nomeadamente em questes de segurana, a ferramenta mais apropriada para o desenvolvimento deste tipo de programas. Vimos tambm neste tutorial a comparao entre o desempenho do Oz e do Java, numa aplicao produtor / consumidor. Os resultados demonstram a ineficincia do Java para este tipo de problemas, e a ineficincia ainda maior quando a aplicao distribuda. O Oz vem provar que o java uma linguagem desactualizada e inadequada aos problemas que a computao distribuda prope nos dias de hoje. Foi para mim um grande prazer desenvolver este tutorial. Nunca tinha tido contacto com este sistema de programao antes. Espero que seja tambm do agrado das pessoas que o vierem a ler e a utilizar como ferramenta. Espero tambm que o Mozart tenha um excelente futuro. Parabns aos professores Seif Haridi, Peter Van Roy, Per Brand e a tantos outros que trabalharam neste excelente projecto.

128

Apndice A
Os programas dos testes de performance entre o Oz e o Java
A.1 Produtor Consumidor Centralizado em Java
import java.util.*; /** This is where the example is run. */ public class ProdCons { public static void main(String[] args) { long starttime = (new Date()).getTime(); Buffer buffer=new Buffer(); Consumer c=new Consumer(buffer); Producer p=new Producer(buffer,1000000); c.start(); p.start(); try { c.join(); } catch (InterruptedException e) {} try { p.join();import java.util.*; /** The buffer class acts as the glue between the Producer and the Consumer. Here the Producer puts his products and from here the consumer retrievs them */ public class Buffer { LinkedList list = new LinkedList(); public synchronized void put(Integer i) { list.addLast(i); notifyAll(); } public synchronized Integer get() { if (list.size() == 0) try { wait(); } catch (InterruptedException e) {} return (Integer) list.removeFirst(); } } } catch (InterruptedException e) {} long endtime = (new Date()).getTime(); System.out.println("Time (milliseconds): "+ Long.toString(endtime-starttime));

129

System.out.println("The sum is: "+c.getSum()); } } public class Producer extends Thread { Buffer buffer; int times; Producer(Buffer buffer, int times) { this.buffer = buffer; this.times = times; } public void run() { for (int i=times;i>0;i--) { Integer n = new Integer(i); buffer.put(n); } buffer.put(null); // Tells the consumer that we are done. } } public class Consumer extends Thread { Buffer buffer; long sum; Consumer(Buffer buffer) { this.buffer = buffer; sum = 0; } public void run() { Integer i; while(true) { i = buffer.get(); if (i==null) break; else sum += i.intValue(); } } public long getSum() { return sum; } }

130

A.2 Produtor Consumidor Centralizado em Oz


declare S T1 T2 Res proc{Producer Stream Cur No} if No>0 then Stream1 in Stream=Cur|Stream1 {Producer Stream1 Cur+1 No-1} else Stream=nil end end fun{Consumer Stream Acc} case Stream of S1|Ss then {Consumer Ss Acc+S1} [] nil then Acc end end {Property.get time T1} thread Res={Consumer S 0} {Property.get time T2} {Show result(res:Res time:T2.total-T1.total)} end {Producer S 1 1000000}

A.3 Produtor Consumidor Distribudo em Java

import java.rmi.Remote; import java.rmi.RemoteException; public interface Buffer extends Remote { public void put(Integer i) throws RemoteException; public Integer get() throws RemoteException; public void waitForStart(Waiter w) throws RemoteException; public void start() throws RemoteException; } import import import import java.rmi.*; java.rmi.registry.*; java.rmi.server.*; java.util.*;

public class BufferImpl extends UnicastRemoteObject implements Buffer { LinkedList list = new LinkedList(); Waiter w; BufferImpl() throws RemoteException {

131

super(); } public void put(Integer i) { putSynch(i); } // Java does not allow remote methods to be synchronized, // therefore we have to synchronize locally. private synchronized void putSynch(Integer i) { list.addLast(i); notifyAll(); } public Integer get() { return getSynch(); } // Java does not allow remote methods to be synchronized, // therefore we have to synchronize locally. private synchronized Integer getSynch() { if (list.size() == 0) try { wait(); } catch (InterruptedException e) {} return (Integer) list.removeFirst(); } /////////////////////// // For timing purposes public void waitForStart (Waiter w) { this.w=w; } public void start() { try { w.startup(); } catch (RemoteException e) { System.err.println("Could not start "+w); } } ////////////////////// public static void main(String[] args) { if (System.getSecurityManager() == null) { System.setSecurityManager(new RMISecurityManager()); } String name = "//" + args[0] + "/Buffer"; try { // Create a "name-server" and store infomation of // this buffer there. LocateRegistry.createRegistry(1099); BufferImpl bufferImpl = new BufferImpl(); Naming.rebind(name, bufferImpl); } catch (Exception e) { System.err.println("BufferImpl exception: " + e.getMessage()); } } }

import java.rmi.*;

132

import java.util.*; public class Consumer extends Thread { Buffer buffer; long sum; Consumer(Buffer buffer) { this.buffer = buffer; sum = 0; } public void run() { Integer i; try { while(true) { i = buffer.get(); if (i==null) { break; } else sum += i.intValue(); } } catch (RemoteException e) { System.err.println("Consumer exception: " + e.getMessage()); } } public long getSum() { return sum; } // Now the consumer is a standalone application. public static void main(String[] args) { if (System.getSecurityManager() == null) { System.setSecurityManager(new RMISecurityManager()); } try { String name = "//" + args[0] + "/Buffer"; // Find the buffer from the "name-server" Buffer buffer = (Buffer) Naming.lookup(name); Consumer c = new Consumer(buffer); long starttime = (new Date()).getTime(); buffer.start(); c.start (); try { c.join(); } catch(InterruptedException e2) {} long endtime = (new Date()).getTime(); System.out.println("Time (milliseconds): "+ Long.toString(endtime-starttime)); System.out.println("The sum is: "+c.getSum()); } catch (Exception e) { System.err.println("Consumer exception: " + e.getMessage()); e.printStackTrace(); } } }

133

import java.rmi.*; public class Producer extends Thread { Buffer buffer; int times; Producer(Buffer buffer, int times) { this.buffer = buffer; this.times = times; } public void run() { try { for (int i=times;i>0;i--) { Integer n = new Integer(i); buffer.put(n); } buffer.put(null); } catch (RemoteException e) { System.err.println("Producer exception: " + e.getMessage()); } } // Now the producer is a standalone application. public static void main(String[] args) { if (System.getSecurityManager() == null) { System.setSecurityManager(new RMISecurityManager()); } try { String name = "//" + args[0] + "/Buffer"; // Find the buffer from the "name-server" Buffer buffer = (Buffer) Naming.lookup(name); Producer p = new Producer(buffer, 1000000); buffer.waitForStart(new ProdWaiter(p)); } catch (Exception e) { System.err.println("Producer exception: " + e.getMessage()); e.printStackTrace(); } } }

import java.rmi.*; import java.rmi.server.*; /** This is simply a construction to start the Producer at a given moment, to make it possible to time the process. */ public class ProdWaiter extends UnicastRemoteObject implements Waiter { Producer p; ProdWaiter(Producer p) throws RemoteException { this.p=p; } public void startup() { p.start(); }

134

} import java.rmi.Remote; import java.rmi.RemoteException; /** This is simply a construction to start the Producer at a given moment, to make it possible to time the process. */ public interface Waiter extends Remote { public void startup() throws RemoteException; } /* java.policy */ grant { permission java.net.SocketPermission *:1024-65535, connect,accept; permission java.net.SocketPermission *:80, connect; };

A.4 Produtor Consumidor distribudo em Oz

declare S Trig FileName='/home/perbrand/public_html/ticket' {Save {Connection.offer S#Trig} FileName} proc{Producer Stream Cur No} if No>0 then Stream1 in Stream=Cur|Stream1 {Producer Stream1 Cur+1 No-1} else Stream=nil end end {Wait Trig} {Producer S 1 1000000} /* Trig=unit */ functor import Connection Pickle Application System define FileName={Application.getArgs record('url'(single type:atom))}.url _#Trig ={Connection.take {Pickle.load FileName}} {System.show initiating} Trig=unit {System.show done} {Application.exit 0} end declare S T1 T2 Trig FileName='http://www.sics.se/~perbrand/ticket' S#Trig={Connection.take {Load FileName}} fun{Consumer Stream Acc} case Stream of S1|Ss then

135

{Consumer Ss Acc+S1} [] nil then Acc end end {Wait Trig} {Property.get time T1} Res={Consumer S 0} {Property.get time T2} {Show result(result:Res time:T2.user -T1.user)}

136

Apndice B
Nomes de Cores
___

[aliceblue azure black blueviolet cadetblue coral cyan darkgoldenrod darkgrey darkolivegreen darkred darkslateblue darkturquoise deepskyblue dodgerblue forestgreen gold green honeydew ivory lavenderblush lightblue lightgoldenrod lightgreen lightsalmon lightslateblue lightsteelblue linen mediumaquamarine mediumpurple mediumspringgreen midnightblue moccasin navyblue orange palegoldenrod palevioletred peru powderblue rosybrown salmon seashell slateblue snow tan turquoise wheat yellow

antiquewhite beige blanchedalmond brown chartreuse cornflowerblue darkblue darkgray darkkhaki darkorange darksalmon darkslategray darkviolet dimgray firebrick gainsboro goldenrod greenyellow hotpink khaki lawngreen lightcoral lightgoldenrodyellow lightgrey lightseagreen lightslategray lightyellow magenta mediumblue mediumseagreen mediumturquoise mintcream navajowhite oldlace orangered palegreen papayawhip pink purple royalblue sandybrown sienna slategray springgreen thistle violet white yellowgreen]

aquamarine bisque blue burlywood chocolate cornsilk darkcyan darkgreen darkmagenta darkorchid darkseagreen darkslategrey deeppink dimgrey floralwhite ghostwhite gray grey indianred lavender lemonchiffon lightcyan lightgray lightpink lightskyblue lightslategrey limegreen maroon mediumorchid mediumslateblue mediumvioletred mistyrose navy olivedrab orchid paleturquoise peachpuff plum red saddlebrown seagreen skyblue slategrey steelblue tomato violetred whitesmoke

137

Apndice C
Glossrio de alguns termos
Agentes Computaes que exibem pelo menos os seguintes comportamentos: autonomia, comportamento reactivo, comportamento pr-activo e comportamento social. Agentes Inteligentes Agentes que exibem comportamentos semelhantes inteligncia humana (raciocnio, aprendizagem). Agentes Mveis Agentes com a capacidade de se deslocarem entre localizaes e com autonomia para decidir a que localizao se deslocam. Aninhamento ou Embutimento de Instrues Consiste em embutir instrues dentro de outras instrues. Avaliao Eager A avaliao de alguns ou todos os parmetros de uma funo iniciada antes do seu valor ser requerido. Um exemplo tipico o call-byvalue em que todos os argumentos so passados depois de avaliados. Avaliao Lazy A avaliao de uma expresso apenas feita se for necessria para que a funo retorna um valor. Em situaes em que o valor de uma expresso necessrio mais do que uma vez, o resultado da primeira avaliao relembrado. Computao Aberta So computaes (aplicaes) que so desenvolvidas de forma a poderem comunicar com outras, independentemente da plataforma e da linguagem de implementao. A comunicao feita atravs de sockets ou ficheiros. Full Laziness a modificao de um programa tendo em vista a optimizao da avaliao lazy, de modo a que todas as subexpresses no corpo da funo que no dependem dos argumentos, sejam apenas avaliados uma vez. Handlers Mecanismos de deteco de falhas de distribuio que consistem em executar um procedimento quando uma instruo sobre uma determinada entidade distribuda falha. High Order As funes ou procedimentos podem receber como argumento outras funes ou procedimentos e podem tambm retornar outras funes ou procedimentos. Lambda-Calculus Um ramo da matemtica desenvolvido por Alonzo Church nos anos 30 e anos 40, relacionado com a aplicao das funes aos seus argumentos. O puro Lambda-Calculus no possui constantes, nem nmeros nem operadores. Consiste em abstraces lambda (funes), variveis e aplicao de uma funo noutras. Todas as entidades so representadas por funes. Por

138

exemplo um nmero natural pode ser representado como uma funo que aplica o seu primeiro argumento ao seu segundo N vezes (Inteiro de Church). As linguagens funcionais so extenses ao Lambda-Calculus introduzindo constantes e tipos. Linguagens Declarativas Computao directa (Linguagens de programao Funcional) e computao indirecta (Linguagens de programao em Lgica). Lgica Determinista Programao lgica em que a sequncia de execuo do algoritmo conhecida. Lgica no Determinista Programao lgica em que a sequncia de execuo do algoritmo desconhecida, isto , existem vrios caminhos alternativos partindo de um ponto de escolha. Multi-Agentes Vrios Agentes em cooperao e/ou competio. Objectos Estacionrios Objectos que permanecem numa localizao (site). Objectos Mveis Objectos que se movimentam entre sites. OPI Oz Programming Interface. o ambiente de desenvolvimento do Mazart-Oz. Consiste em vrios programas utilitrios e na integrao com o Emacs. Procedimentos Annimos uma forma de definir um bloco de instrues. Isto permite que estes blocos de instrues sejam embutidos dentro de chamadas a procedimentos. Servidores de Computao So aplicaes que aceitam pedidos de computao remota processam essas computaes e devolvem um resultado. Servem para tirar partido dos recursos da rede para melhorar o desempenho do sistema. Servidores Dinamicamente Extensvel So servidores que podem ser actualizados sem que tenham que ser desligados. Threads Unidades computacionais da programaes concorrente que consistem num conjunto de instrues executadas em concorrncia. Tickets So strings que servem para identificar uma determinada entidade distribuda. Valores First-Class Um valor first-class quando pode ser passado como parmetro ou devolvido por uma funo e pode tambm ser guardado em estruturas de dados. O conceito semelhante a high order. Watchers Mecanismos de deteco de falhas de distribuio que consistem em executar um determinado procedimento quando uma determinada entidade distribuda falha independentemente se executada ou no uma instruo sobre essa entidade.

139

Bibliografia
Site Oficial www.mozart-oz.org

Jason Cecil, Mike Reily, Jason Sutton, Mike Tolliver; A Comparative Report of Simple Procedural vs. Functional Programming Languages and Functional vs. Logic-Based Programming Languages; 24/4/2000 Constraint Logic Programming; Revista Byte; 2/1995 The Journal of Functional Programming; MIT press Gert Smolka; The Oz programming Model; DFKI; 1995 Seif Haridi, Peter Van Roy, Per Brand e Christian Schulte; Programming Languages for Distributed Applications; 1998 Seif Haridi, Peter Van Roy, Gert Smolka ; An Overview of the Design of Distributed Oz; 1997 Seif Haridi, Peter Van Roy, Per Brand e Christian Schulte, Denis Duchier, Martin Henz; Logic Programming in Oz and its relation to multiparadigm programming; 2000 Seif Haridi, Nils Fransn; Tutorial of Oz; 2/2000 Seif Haridi, Peter Van Roy, Per Brand; Distributed Programming in Mozart ATutorial Introduction; 2/2000 Christian Schulte; Window Programming in Mozart; 2/2000

140

141

Você também pode gostar