Modula-3 - Modula-3
Paradigmas | imperativo , estruturado , procedimental , modular , simultâneo |
---|---|
Família | Wirth Modula |
Projetado por | Luca Cardelli , James Donahue, Lucille Glassman, Mick Jordan; Bill Kalsow, Greg Nelson |
Desenvolvedores |
DEC Olivetti elego Software Solutions GmbH |
Apareceu pela primeira vez | 1988 |
Versão estável | 5.8.6 / 14 de julho de 2010
|
Versão de visualização | 5.8.6 / 14 de julho de 2010
|
Disciplina de digitação | forte , estático , seguro ou se inseguro explicitamente seguro isolado |
Alcance | Lexical |
Plataforma | IA-32 , x86-64 , PowerPC , SPARC |
SO | Plataforma cruzada : FreeBSD , Linux , Darwin , SunOS |
Local na rede Internet | www |
Implementações principais | |
SRC Modula-3, CM3, PM3, EZM3, M3 / PC Klagenfurt | |
Influenciado por | |
ALGOL , Euclid , Mesa , Modula-2 , Modula-2 + , Oberon , Pascal | |
Influenciado | |
C # , Java , Nim , OCaml , Python |
Modula-3 é uma linguagem de programação concebida como sucessora de uma versão atualizada do Modula-2 conhecida como Modula-2 + . Embora tenha sido influente nos círculos de pesquisa (influenciando os projetos de linguagens como Java , C # e Python ), não foi amplamente adotado na indústria. Foi projetado por Luca Cardelli , James Donahue, Lucille Glassman, Mick Jordan (antes no Laboratório de Tecnologia de Software da Olivetti ), Bill Kalsow e Greg Nelson no Centro de Pesquisa de Sistemas (SRC) da Digital Equipment Corporation (DEC ) e no Centro de Pesquisa Olivetti ( ORC) no final dos anos 1980.
As principais características do Modula-3 são simplicidade e segurança, preservando o poder de uma linguagem de programação de sistemas. Modula-3 teve como objetivo continuar a tradição Pascal de segurança de tipo, ao introduzir novas construções para a programação prática do mundo real. Em particular, o Modula-3 adicionou suporte para programação genérica (semelhante a modelos ), multithreading , tratamento de exceções , coleta de lixo , programação orientada a objetos , revelação parcial e marcação explícita de código inseguro. O objetivo de design do Modula-3 era uma linguagem que implementasse os recursos mais importantes das linguagens de programação imperativas modernas em formas bastante básicas. Assim, recursos supostamente perigosos e complicadores, como herança múltipla e sobrecarga de operador, foram omitidos.
Desenvolvimento histórico
O projeto Modula-3 começou em novembro de 1986, quando Maurice Wilkes escreveu a Niklaus Wirth com algumas idéias para uma nova versão do Modula. Wilkes estava trabalhando na DEC pouco antes deste ponto e voltou para a Inglaterra e ingressou no Conselho de Estratégia de Pesquisa da Olivetti. Wirth já havia se mudado para Oberon , mas não teve problemas com o desenvolvimento contínuo da equipe de Wilkes com o nome Modula. A definição da linguagem foi concluída em agosto de 1988 e uma versão atualizada em janeiro de 1989. Compiladores de DEC e Olivetti logo seguiram, e implementações de terceiros depois disso.
Seu design foi fortemente influenciado pelo trabalho na linguagem Modula-2 + em uso no SRC e no Acorn Computers Research Center (ARC, mais tarde ORC quando a Olivetti adquiriu a Acorn) na época, que era a linguagem em que o sistema operacional para o A estação de trabalho VAX com multiprocessador DEC Firefly foi escrita e na qual o Acorn Compiler for Acorn C e Modula Execution Library (CAMEL) no ARC para o projeto de sistema operacional ARX da gama de computadores Acorn Archimedes baseada em ARM foi escrita. Como afirma o Relatório Modula-3 revisado, a linguagem foi influenciada por outras línguas, como Mesa , Cedar , Object Pascal , Oberon e Euclid .
Durante a década de 1990, o Modula-3 ganhou uma aceitação considerável como língua de ensino, mas nunca foi amplamente adotado para uso industrial. Contribuir para isso pode ter sido o fim do DEC, um dos principais apoiadores do Modula-3 (especialmente quando ele deixou de mantê-lo efetivamente antes que o DEC fosse vendido para a Compaq em 1998). Em qualquer caso, apesar da simplicidade e poder do Modula-3, parece que havia pouca demanda por uma linguagem compilada procedural com implementação restrita de programação orientada a objetos . Por um tempo, um compilador comercial chamado CM3 mantido por um dos principais implementadores antes da DEC SRC, que foi contratado antes da DEC ser vendida para a Compaq , um ambiente de desenvolvimento integrado (IDE) chamado Reactor e uma máquina virtual Java extensível (licenciada em código binário e formatos de código-fonte e compiláveis com o Reactor) foram oferecidos pela Critical Mass, Inc., mas essa empresa interrompeu suas operações ativas em 2000 e deu parte do código-fonte de seus produtos à elego Software Solutions GmbH. Modula-3 agora é ensinado em universidades principalmente em cursos de linguagem de programação comparativa, e seus livros estão esgotados. Essencialmente, o único apoiador corporativo do Modula-3 é o elego, que herdou as fontes do Critical Mass e desde então fez vários lançamentos do sistema CM3 em código fonte e binário. O Reactor IDE foi lançado em código aberto depois de vários anos sem, com o novo nome CM3-IDE. Em março de 2002, elego também assumiu o repositório de outra distribuição Modula-3 ativa, PM3, até então mantida na École Polytechnique de Montréal, mas que mais tarde continuou com o trabalho no HM3, melhorou ao longo dos anos até que se tornou obsoleto.
Sintaxe
Um exemplo comum de sintaxe de uma linguagem é "Hello, World!" programa .
MODULE Main;
IMPORT IO;
BEGIN
IO.Put("Hello World\n")
END Main.
Todos os programas em Modula-3 têm pelo menos um arquivo de módulo, enquanto a maioria também inclui um arquivo de interface que é usado por clientes para acessar dados do módulo. Como em algumas outras linguagens, um programa Modula-3 deve exportar um módulo Principal, que pode ser um arquivo denominado Main.m3 ou um arquivo pode ser chamado EXPORT
para exportar o módulo Principal.
MODULE Foo EXPORTS Main
Recomenda-se que os nomes dos arquivos de módulo sejam iguais ao nome no código-fonte. Se forem diferentes, o compilador apenas emitirá um aviso.
Outras convenções na sintaxe incluem nomear o tipo exportado de uma interface T
, uma vez que os tipos são geralmente qualificados por seus nomes completos, portanto, um tipo T
dentro de um módulo denominado Foo será nomeado Foo.T
. Isso ajuda na legibilidade. Outra convenção semelhante é nomear um objeto público Public
como nos exemplos OOP acima.
Características da linguagem
Modularidade
Em primeiro lugar, todas as unidades compiladas são INTERFACE
ou implementações MODULE
, de um tipo ou outro. Uma unidade de interface compilada, começando com a palavra-chave INTERFACE
, define constantes, tipos, variáveis, exceções e procedimentos. O módulo de implementação, começando com a palavra-chave MODULE
, fornece o código e quaisquer constantes, tipos ou variáveis adicionais necessários para implementar a interface. Por padrão, um módulo de implementação implementará a interface com o mesmo nome, mas um módulo pode explicitamente EXPORT
para um módulo que não tem o mesmo nome. Por exemplo, o programa principal exporta um módulo de implementação para a interface Principal.
MODULE HelloWorld EXPORTS Main;
IMPORT IO;
BEGIN
IO.Put("Hello World\n")
END HelloWorld.
Qualquer unidade compilada pode ter IMPORT
outras interfaces, embora as importações circulares sejam proibidas. Isso pode ser resolvido fazendo a importação do MÓDULO de implementação. As entidades dentro do módulo importado podem ser importadas, em vez de apenas o nome do módulo, usando a FROM Module IMPORT Item [, Item]*
sintaxe:
MODULE HelloWorld EXPORTS Main;
FROM IO IMPORT Put;
BEGIN
Put("Hello World\n")
END HelloWorld.
Normalmente, apenas se importa a interface e usa a notação de 'ponto' para acessar os itens dentro da interface (semelhante a acessar os campos dentro de um registro). Um uso típico é definir uma estrutura de dados (registro ou objeto) por interface junto com quaisquer procedimentos de suporte. Aqui, o tipo principal receberá o nome 'T' e será usado como em MyModule.T
.
No caso de uma colisão de nome entre um módulo importado e outra entidade dentro do módulo, a palavra reservada
AS
pode ser usada como emIMPORT CollidingModule AS X;
Seguro vs inseguro
Alguma habilidade é considerada insegura, onde o compilador não pode mais garantir que os resultados serão consistentes; por exemplo, ao fazer interface com a linguagem C. A palavra-chave UNSAFE
prefixada antes de INTERFACE
ou MODULE
, pode ser usada para dizer ao compilador para habilitar certos recursos de baixo nível da linguagem. Por exemplo, uma operação insegura está contornando o sistema de tipos usando LOOPHOLE
para copiar os bits de um inteiro em um REAL
número de ponto flutuante .
Uma interface que importa um módulo não seguro também deve ser insegura. Uma interface segura pode ser exportada por um módulo de implementação não seguro. Este é o uso típico ao fazer interface com bibliotecas externas , onde duas interfaces são construídas: uma não segura, a outra segura.
Genéricos
Uma interface genérica e seu módulo genérico correspondente, prefixam a palavra-chave INTERFACE
ou MODULE
com GENERIC
e tomam como argumentos formais outras interfaces. Assim (como os modelos C ++ ), pode-se facilmente definir e usar tipos de dados abstratos, mas ao contrário do C ++ , a granularidade está no nível do módulo. Uma interface é passada para a interface genérica e módulos de implementação como argumentos, e o compilador irá gerar módulos concretos.
Por exemplo, pode-se definir um GenericStack e, em seguida, instanciá-lo com interfaces como IntegerElem
, ou RealElem
, ou mesmo interfaces para objetos, desde que cada uma dessas interfaces defina as propriedades necessárias para os módulos genéricos.
Os tipos simples INTEGER
, ou REAL
não podem ser usados, porque não são módulos, e o sistema de genéricos é baseado no uso de módulos como argumentos. Por comparação, em um modelo C ++, um tipo simples seria usado.
ARQUIVO: IntegerElem.i3
INTERFACE IntegerElem;
CONST Name = "Integer";
TYPE T = INTEGER;
PROCEDURE Format(x: T): TEXT;
PROCEDURE Scan(txt: TEXT; VAR x: T): BOOLEAN;
END IntegerElem.
ARQUIVO: GenericStack.ig
GENERIC INTERFACE GenericStack(Element);
(* Here Element.T is the type to be stored in the generic stack. *)
TYPE
T = Public OBJECT;
Public = OBJECT
METHODS
init(): TStack;
format(): TEXT;
isEmpty(): BOOLEAN;
count(): INTEGER;
push(elm: Element.T);
pop(VAR elem: Element.T): BOOLEAN;
END;
END GenericStack.
ARQUIVO: GenericStack.mg
GENERIC MODULE GenericStack(Element);
< ... generic implementation details... >
PROCEDURE Format(self: T): TEXT =
VAR
str: TEXT;
BEGIN
str := Element.Name & "Stack{";
FOR k := 0 TO self.n -1 DO
IF k > 0 THEN str := str & ", "; END;
str := str & Element.Format(self.arr[k]);
END;
str := str & "};";
RETURN str;
END Format;
< ... more generic implementation details... >
END GenericStack.
ARQUIVO: IntegerStack.i3
INTERFACE IntegerStack = GenericStack(IntegerElem) END IntegerStack.
ARQUIVO: IntegerStack.m3
MODULE IntegerStack = GenericStack(IntegerElem) END IntegerStack.
Rastreabilidade
Qualquer identificador pode ser rastreado de volta ao local de origem, ao contrário do recurso 'incluir' de outras linguagens. Uma unidade compilada deve importar identificadores de outras unidades compiladas, usando uma IMPORT
instrução. Mesmo as enumerações usam a mesma notação de 'ponto' usada ao acessar um campo de um registro.
INTERFACE A;
TYPE Color = {Black, Brown, Red, Orange, Yellow, Green, Blue, Violet, Gray, White};
END A;
MODULE B;
IMPORT A;
FROM A IMPORT Color;
VAR
aColor: A.Color; (* Uses the module name as a prefix *)
theColor: Color; (* Does not have the module name as a prefix *)
anotherColor: A.Color;
BEGIN
aColor := A.Color.Brown;
theColor := Color.Red;
anotherColor := Color.Orange; (* Can't simply use Orange *)
END B.
Alocação dinâmica
Modula-3 suporta a alocação de dados em tempo de execução . Existem dois tipos de memória que podem ser alocados TRACED
e UNTRACED
, a diferença é se o coletor de lixo pode vê-la ou não. NEW()
é usado para alocar dados de qualquer uma dessas classes de memória. Em um UNSAFE
módulo, DISPOSE
está disponível para liberar memória não rastreada.
Orientado a Objeto
Técnicas de programação orientada a objetos podem ser usadas no Modula-3, mas seu uso não é necessário. Muitos dos outros recursos fornecidos no Modula-3 (módulos, genéricos) geralmente podem substituir a orientação a objetos.
O suporte a objetos é mantido intencionalmente em seus termos mais simples. Um tipo de objeto (denominado "classe" em outras linguagens orientadas a objetos) é introduzido com a OBJECT
declaração, que tem essencialmente a mesma sintaxe de uma RECORD
declaração, embora um tipo de objeto seja um tipo de referência, enquanto RECORDs em Modula-3 não são ( semelhantes às estruturas em C). Os tipos exportados geralmente são chamados de T por convenção e criam um tipo "Público" separado para expor os métodos e dados. Por exemplo:
INTERFACE Person;
TYPE T <: Public;
Public = OBJECT
METHODS
getAge(): INTEGER;
init(name: TEXT; age: INTEGER): T;
END;
END Person.
Isso define uma interface Person
com dois tipos, T
e Public
, que é definida como um objeto com dois métodos, getAge()
e init()
. T
é definido como um subtipo de Public
pelo uso do <:
operador.
Para criar um novo Person.T
objeto, use o procedimento interno NEW
com o método init()
como
VAR jim := NEW(Person.T).init("Jim", 25);
A REVEAL
construção do Modula-3 fornece um mecanismo conceitualmente simples e limpo, mas muito poderoso para ocultar os detalhes de implementação dos clientes, com muitos níveis de facilidade arbitrariamente . Use REVEAL
para mostrar a implementação completa da Person
interface de cima.
MODULE Person;
REVEAL T = Public BRANDED
OBJECT
name: TEXT; (* These two variables *)
age: INTEGER; (* are private. *)
OVERRIDES
getAge := Age;
init := Init;
END;
PROCEDURE Age(self: T): INTEGER =
BEGIN
RETURN self.age;
END Age;
PROCEDURE Init(self: T; name: TEXT; age: INTEGER): T =
BEGIN
self.name := name;
self.age := age;
RETURN self;
END Init;
BEGIN
END Person.
Observe o uso da BRANDED
palavra - chave, que "marca" os objetos para torná-los únicos, evitando a equivalência estrutural. BRANDED
também pode aceitar uma string como argumento, mas quando omitido, uma string exclusiva é gerada para você.
Modula-3 é uma das poucas linguagens de programação que requer referências externas de um módulo para ser estritamente qualificado. Ou seja, uma referência no módulo A
ao objeto x
exportado do módulo B
deve assumir a forma B.x
. No Modula-3, é impossível importar todos os nomes exportados de um módulo.
Por causa dos requisitos da linguagem sobre qualificação de nome e sobreposição de método , é impossível quebrar um programa de trabalho simplesmente adicionando novas declarações a uma interface (qualquer interface). Isso possibilita que programas grandes sejam editados simultaneamente por muitos programadores sem se preocupar com conflitos de nomenclatura; e também torna possível editar bibliotecas de linguagens centrais com o firme conhecimento de que nenhum programa existente será quebrado no processo.
Exceções
O tratamento de exceções é baseado em um sistema de TRY
... EXCEPT
bloco, que desde então se tornou comum. Um recurso que não foi adotado em outras linguagens, com as notáveis exceções de Delphi , Python [1] , Scala [2] e Visual Basic.NET , é que a EXCEPT
construção definiu uma forma de instrução switch com cada possível exceção como um caso em sua própria cláusula EXCEPT. Modula-3 também suporta uma construção LOOP
... EXIT
... END
que faz um loop até que EXIT
ocorra, uma estrutura equivalente a um loop simples dentro de uma cláusula
TRY
....EXCEPT
Multi-threaded
A linguagem suporta o uso de multi-threading e sincronização entre threads. Há um módulo padrão na biblioteca de tempo de execução ( m3core ) denominado Thread, que oferece suporte ao uso de aplicativos multithread. O tempo de execução do Modula-3 pode fazer uso de um thread separado para tarefas internas, como coleta de lixo.
Uma estrutura de dados embutida MUTEX
é usada para sincronizar vários threads e proteger as estruturas de dados de acesso simultâneo com possível corrupção ou condições de corrida. A LOCK
declaração introduz um bloco no qual o mutex está bloqueado. O desbloqueio de a MUTEX
está implícito na saída do bloco de execução do código. O MUTEX
é um objeto e, como tal, outros objetos podem ser derivados dele.
Por exemplo, na seção de entrada / saída (I / O) da biblioteca libm3 , leitores e gravadores (Rd.T e Wr.T) são derivados de MUTEX e se bloqueiam antes de acessar ou modificar quaisquer dados internos, como buffers.
Resumo
Em resumo, o idioma apresenta:
- Módulos e interfaces
- Marcação explícita de código inseguro
- Genéricos
- Coleta de lixo automática
- Tipagem forte , equivalência estrutural de tipos
- Objetos
- Exceções
- Tópicos
Modula-3 é uma das raras linguagens cuja evolução de recursos é documentada.
Em Programação de Sistemas com Modula-3 , quatro pontos essenciais do projeto da linguagem são intensamente discutidos. Esses tópicos são: equivalência estrutural vs. nome, regras de subtipagem, módulos genéricos e modos de parâmetro como READONLY
.
Recursos de biblioteca padrão
Continuando uma tendência iniciada com a linguagem C , muitos dos recursos necessários para escrever programas reais foram deixados de fora da definição da linguagem e, em vez disso, fornecidos por meio de um conjunto de bibliotecas padrão . A maioria das interfaces abaixo são descritas em detalhes em
Bibliotecas padrão que fornecem os seguintes recursos. Elas são chamadas de interfaces padrão e são obrigatórias (devem ser fornecidas) no idioma.
- Texto: operações em referências de string imutáveis, chamadas
TEXT
s - Thread: Operações relacionadas a threading, incluindo
MUTEX
variável de condição e pausa de thread. A biblioteca de threading fornece troca de threads preventiva - Word: operações bit a bit em inteiros sem sinal (ou palavras de máquina). Normalmente implementado diretamente pelo compilador
- Interfaces de ponto flutuante
Algumas interfaces recomendadas implementadas nas implementações disponíveis, mas não são necessárias
- Lex: Para analisar o número e outros dados
- Fmt: formatação de vários tipos de dados para impressão
- Pkl (ou Pickle): serialização de objetos de quaisquer tipos de referência acessíveis pelo coletor de lixo
- Tabela: Módulos genéricos para mapas
Como em C, I / O também é fornecido por meio de bibliotecas, em Modula-3 chamado Rd
e Wr
. O projeto orientado a objetos das bibliotecas Rd (leitores) e Wr (escritores) é abordado em detalhes no livro de Greg Nelson. Um aspecto interessante do Modula-3 é que ele é uma das poucas linguagens de programação cujas bibliotecas padrão foram formalmente verificadas para não conter vários tipos de bugs, incluindo bugs de bloqueio. Isso foi feito sob os auspícios do Larch / Modula-3 (ver família Larch ) e projetos de verificação estática estendida no DEC Systems Research Center .
Implementações
Vários compiladores estão disponíveis, a maioria deles de código aberto .
- DEC-SRC M3, o original.
- O kit de ferramentas Modula-3 do Olivetti Research Center (ORC), originalmente um compilador, agora está disponível como uma biblioteca para análise sintática, lexical e semântica de programas Modula-3.
- Massa crítica CM3, um sucessor diferente do DEC-SRC M3
- Polytechnique Montreal Modula-3 PM3, um sucessor do DEC-SRC M3, atualmente se fundindo com o CM3
- EzM3, uma implementação independente leve e facilmente portável, desenvolvida em conexão com o CVSup
- HM3, um sucessor do lançamento pm3-1.1.15 do PM3, com suporte para threading nativo usando NPTL
- CM3, o sucessor do Critical Mass CM3. Esta é a única implementação atualizada, mantida e desenvolvida. As versões estão disponíveis em http://www.opencm3.net/releng/ .
Como o único aspecto das estruturas de dados C que está faltando no Modula-3 é o tipo de união, todas as implementações do Modula-3 existentes são capazes de fornecer boa compatibilidade de código binário com as declarações de tipo de linguagem C de matrizes e estruturas .
Livros
Nenhum desses livros ainda está sendo impresso, embora cópias usadas possam ser obtidas e alguns sejam digitalizados, parcial ou totalmente, e alguns capítulos de um deles tenham versões anteriores ou posteriores obtidas como relatórios de pesquisa na web.
- Greg Nelson, ed., Systems Programming with Modula-3 A referência definitiva sobre a linguagem Modula-3 com artigos interessantes sobre construção de software de sistemas orientados a objetos e uma documentação da discussão conduzindo aos recursos finais da linguagem. Existem alguns anteriormente (ver para o capítulo dois, para o capítulo quatro, para o capítulo cinco, para o capítulo seis) e alguns posteriormente (ver para o capítulo um e mais atualizados dois, portanto, de ambas as versões anteriores da definição de linguagem e, para o capítulo três e para capítulo sete) de publicar versões da maioria de seus oito capítulos individualmente disponíveis no DEC Systems Research Center (SRC) anterior como relatórios de pesquisa para download.
- Samuel P. Harbison, Modula-3 Livro de aula fácil de usar.
- Robert Sedgewick , Algorithms in Modula-3
- Laszlo Boszormenyi & Carsten Weich, Programming in Modula-3: An Introduction in Programming with Style
- Renzo Orsini, Agostino Cortesi Programmare in Modula-3: introduzione alla programmazione imperativa e a oggetti um livro italiano da língua explicando suas principais características.
Projetos usando Modula-3
O software que é programado Modula-3 inclui:
- O sistema operacional SPIN
- O programa de sincronização do repositório de software CVSup
- A linguagem Obliq , que usa objetos de rede Modula-3, tem a capacidade de migrar objetos em redes locais de forma transparente, permitindo uma capacidade distribuída para o paradigma de programação orientado a objetos Modula-3. Ele tem sido usado para construir aplicativos distribuídos, animações de computador e aplicativos de programação web na forma de extensão de script para Modula-3.
Influências em outras linguagens de programação
Embora o Modula-3 não tenha ganhado o status de mainstream, várias partes da distribuição do DEC-SRC M3 o fizeram. Provavelmente, a parte mais influente foi a biblioteca de objetos de rede, que formou a base para a primeira implementação de Remote Method Invocation (RMI) do Java, incluindo o protocolo de rede. Somente quando a Sun mudou do padrão Common Object Request Broker Architecture (CORBA) para o protocolo baseado em IIOP, ela foi abandonada. A documentação Java sobre coleta de lixo de objetos remotos ainda se refere ao trabalho pioneiro feito para Objetos de Rede Modula-3. A implementação de classes em Python também foi inspirada no mecanismo de classe encontrado em C ++ e Modula-3. Além disso, a linguagem Nim faz uso de alguns aspectos do Modula-3, como ponteiros rastreados e não rastreados .
Referências
links externos
- Website oficial
- Modula3 no GitHub
- Site de implementação CM3
- Página inicial da Modula-3 (agora morta há muito tempo, espelho )
- Modula-3: definição de linguagem
- Elego Software Solutions
- Grupo de notícias Modula-3 , quase todo deserto
- Modula-3 Development Mailing List , ativo
- Notas da classe CS2 da Caltech, ministrada em Modula-3 em 2002 e 2003
- Classe CS3 da Caltech 2009 na Wayback Machine (arquivado em 23 de maio de 2013)
- Programação espelho em Modula-3 : exemplos de programas
- Construindo aplicações distribuídas OO: Modula-3 Objects at Work . Michel R. Dagenais. Versão de rascunho (janeiro de 1997)
- Modula-3: Linguagem, bibliotecas e ferramentas . Apresentação em Modula-3 com mais de 120 slides. Michael R. Dagenais , morto
- Abstração de dados orientada a objetos no Modula-3 . Joseph Bergin (1997)
- Entrevista do Computerworld com Luca Cardelli no Modula-3