segunda-feira, 30 de abril de 2007

Python, Scheme e ML - Uma análise comparativa das linguagens de programação

Durante o FLISOL Salvador 2007 , evento em que participei apresentando uma palestra sobre Python, decidi que era hora de começar a escrever. Já fazia mais de um mês que eu havia decidido começar a escrever um blog sobre minhas experiências usando a linguagem Python, o servidor de aplicação Zope e CMS Plone. O nome já estava registrado: python-blog.blogspot.com, e o que mais estava faltando? Iniciativa. ;-)

Na primeira postagem estou publicando um artigo que escrevi juntamente com dois colegas na disciplina de Engenharia e Software I (2006.2). Escrevê-lo me ajudou enxergar o quão poderosas são as linguagens funcionais e o quanto Python herda desse paradigma. Além disso cheguei a conclusão que as linguagens funcionais deveriam ser mais ensinadas nos cursos de Ciência da Computação, talvez até com primeira linguagem! :-o

Python, Scheme e ML

Uma análise comparativa das linguagens de programação.

Faculdade Ruy Barbosa

Rudá Porto Filgueiras, Sergio Pacheco, Adrienne Nogueira


Disciplina de Engenharia de Software I – 2006.2

Curso de Bacharelado em Ciência da Computação – Faculdade Ruy Barbosa(FRB)

Rua Theodomiro Batista, 422 – Rio Vermelho – Cep: 41.940-320 – Salvador – BA


rudazz@gmail.com, sergiorpacheco@gmail.com, adrieness_eu@yahoo.com.br

1. Resumo

As linguagens de programação tem uma importância fundamental na nossa capacidade de expressar idéias e resolver problemas. A análise de diferentes linguagens possibilita o avaliação das escolhas realizadas em tempo de projeto, utilizando os critérios e conceitos da Engenharia de Software para entender as escolhas dos projetistas e o resultado desas escolhas em cada linguagem.

Palavras-chave: Linguagem de Programação, ML, Lisp, Scheme, Python, Projeto de Linguagem

2. Introdução

Esse artigo se propõe a comparar as características de três diferentes linguagens de programação. Scheme e ML são linguagens fundamentadas no paradigma funcional, de amplo uso, possuem diversas implementações e são muito utilizadas na área de matemática, lógica e inteligência artificial. A terceira e última linguagem é Python, que se diferencia por ser de propósito geral, multiparadigma e influenciada pelas lições de design de importantes linguagens, tais como: Ada, Scheme, SmalTalk, Módula 3.

Para realizar esse comparativo, selecionamos algumas características a serem estudadas e comparadas entre as linguagens. Isso nos permitirá avaliar as escolhas realizadas em tempo de projeto e seus benefícios e/ou contra-partidas utilizando os critérios da Engenharia de Software como parâmetro. As características abordadas são: simplicidade global, intruções de controle, suporte para abstração, manipulação de exceções e verificação de tipos.

3. Simplicidade global

A simplicidade global de uma linguagem afeta diretamente a sua legibilidade”, Sebesta (2005). As linguagens do paradigma funcional se fundamentam na simplicidade do conceito matemático de função, elementos e conjuntos. Em especial Scheme, que no seu projeto tem por objetivo ser minimalista, mantendo número de instruções básicas e palavras reservadas restritas ao mínimo necessário e ao mesmo tempo compatível com LISP.

Python se apoia nas idéias do projeto de Scheme para se manter simples e ao ser comparada com outras linguagens imperativas, em geral, possui menos instruções básicas e palavras reservadas, o que contribui para o seu fácil aprendizado. Assim como Scheme, Python procura implementar apenas um modo correto de se executar uma determinada operação, isso fortalece a simplicidade e consequentemente a legibilidade da linguagem.

ML não possui um grau de simplicidade global tão alto quanto Scheme, pois permite a programação imperativa e a declaração de tipos, embora não obrigue essa definição. Scheme não possui tipos de dados e apoia-se na conceito de átomos (elementos) e listas (conjuntos) para abstrair suas estruturas de dados. Todas estas linguagens são de alto nível, com alta capacidade de abstração o que torna essa simplicidade poderosa e expressiva.

4. Instruções de controle

As instruções de controle são utilizadas para construir desvios e organizar o fluxo da execução dos programas. Nas linguangens imperativas, existem estruturas de controle para realizar a repetição e a seleção de determinados trechos de código a serem executados.

Python possui um conjunto reduzido de estruturas de controle, de forma a reduzir a complexidade da linguagem. Além disso a forma de expressar um loop for para percorer elementos de uma lista ou dicionário ou qualquer objeto que implemente alguns métodos especiais é simples e poderosa. Como ML, Python possui o recurso de compreensão de listas.

Scheme e ML possuem instruções de seleção simples as repetições são implementadas através da recursividade. Em Scheme a pilha de recursão não aumenta de acordo com a sua execução, o que garante que o espaço utilizado será constante, tornando a recursão mais segura e eficiente.

ML também possui recursos do paradigma imperativo, e por isso, possui instruções de controle semelhantes a linguagem C++, como if, else, case. Além disso seu mecanismo de seleção baseados em encontro de padrões pode ser utlizado para selecionar parte da função que será executada a depender do valor recebido como parâmetro.

Exemplos:

Python – compreensão de listas:

lista = [1, 2, 3 ,4 ,5 ,6 ,7 ,8 ,9, 10]

lista = [x for x in lista if x % 2 = 0]

[2, ,4 ,6, 8 , 10]

Scheme – condicional e repetição com recursão

(DEFINE (factorial n)

(IF (= n 0)

1

(* n (fatorial (- n 1)))))

ML – encontro de padrões:

datatype shape =

point

| circle of real

| box of (real * real)

fun area (point) = 0.0

| area (circle r) = pi * sqr (r)

| area (box(w,h)) = w * h

5. Suporte para abstração

“A abstração é a capacidade de modelar um problema de maneira a se concentrar nos aspectos importantes para esse sistema ou trecho expecífico de código, omitindo detalhes e facilitando a manipulaçãos das entidades ao separar as definições do que um programa faz, dos detalhes sobre como ele é implementado.”, Watt, David A. (1989).

O suporte para abstração em uma linguagem está relacionado a capacidade de definição de novos tipos de dados abstratos, denominada abstração de dados. Além disso, também está relacionada com a possibilidade de se abstrair um processo utilizando funções e procedimentos no paradigma funcional e imperativo.

O paradigma orientado a objetos funde esses dois conceitos ao permitir a definição de classes, com suas propriedades e métodos. E isso possui um impacto importante na capacidade de escrita e na confiabilidade, facilitando o reuso de código e a legibilidade.

As linguagens ML, Scheme e Python tratam as funções como tipos de dados de primeira ordem, o que as permite passar uma função como parâmetro de outras funções, armazenar funções como atributos ou elementos de uma estrutura de dados ou lista e também retorná-las como resultado de uma expressão, o que permite a criação de abstrações mais poderosas.

Um exemplo dessa capacidade é o uso da função interna map que mapeia os elementos de um conjunto aos elementos de um novo conjunto utilizando uma função que é passada como parâmetro e que será aplicada a cada elemento do conjunto inicial para gerar o novo conjunto.

Exemplos:

ML


map(sqrt [1, 2, 3, 4])

Scheme


(MAP SQRT '(1 2 3 4))

Python


map(math.sqrt, [1,2,3,4])

Em todos os exemplos acima o resultado será uma nova lista contendo a raiz quadrada dos elementos da lista passada como parâmentro.

6. Verificação de Tipos

Tradicionalmente na Engenharia de Software o sistema de verificação de tipos de uma linguagem de programação é classificado em dois grupos principais: estaticamente (fortemente) tipadas ou dinâmicamente (fracamente) tipadas. A capacidade de verificação de tipos de uma linguagem afeta diretamente a sua confiabilidade.

Porém algumas linguagens, como Python, não se enquadram nesse modelo, pois são dinâmicamente tipadas e ao mesmo tempo fortemente tipadas. Surgindo então uma diferenciação da definição tradicional onde podemos definir as linguagens como estáticamente ou dinâmicamente tipadas e fortemente ou fracamente tipadas.

As linguagens estaticamente tipadas são aquelas no qual toda a variável, função e parâmentros tem um tipo bem definido, declarado pelo programador, e ela realiza a verficação de tipos em tempo de compilação.

Já nas linguagens dinamicamente tipadas, somente os valores tem tipos de dados definidos (tipos latentes), ou seja, uma variável ou parâmetro não tem um tipo definido e pode conter valores de diferentes tipos em diferentes momentos. Isso implica que a verificação de tipos deve ser realizada em tempo de execução.

ML é uma linguagem estaticamente tipada e que possui um sistema de inferência de tipos robusto, tornando opcional a declaração de tipos. Scheme e Python são dinamicamente tipadas e também possuem a inferência dos tipos. Porém em todas elas o sistema de verficação de tipos não realiza a conversão implícita de um tipo de dados para outro, leventando uma exceção quando tipos inconsistentes são utilizados.

Python realiza uma nova vinculação de nome e espaço a cada attribuição de valor a uma variável, inferindo o tipo de dados do valor recebido. Além disso ela não possui definição de tipos nos parâmetros de funções ou métodos, deixando a cargo no programador o tratamento dos tipos recebidos e retornados.

Exemplos:

Scheme

(+ 10 “16”)

;The object “16”, passed as the second argument to integer-add, is not the correct type.

Python

10 + “16”

TypeError: unsupported operand type(s) for +: 'int' and 'str'

ML

10 + “16”;

! Type clash: expression of type string cannot have type int

Podemos observar que não há a sobrecarga do operador “+” para concaternar uma string com um inteiro e a exceção levantada é resultado do sistema de verificação de tipos.

7. Manipulação de exceções

Durante o desenvolvimento de um programa, diferentes tipos de erros precisam ser previstos e tratados para que se alcance um grau de confiabiliade adequado a cada necessidade, evitanto que o programa seja finalizado de forma inesperada.

Porém, se a linguagem utilizada não suportar nativamente o recurso de manipulação de execeções, quanto mais verificações forem necessárias, mais complexo o código vai ficar, e isso terá um impacto direto na legibilidade e no custo de implementação e manutenção.

ML, Scheme e Pyhon tem o suporte nativo ao tratamento de exceções, incluindo a definição de novos tipos de execeção. Scheme tem o suporte a exceções integrado ao seu interpretador de comandos, permitindo que o programador decida o que fazer quando uma exceção não tratada é levantada. As exceções em ML sempre são de tipo e podem ter um valor e um efeito associado.

Em Python as exceções definidas pelo usuário utilizam o conceito de herança da orientação a objetos. Exception é a classe base na qual todas as outras exceções são derivadas. E como existe a unificação entre classes e tipos, e subclasses são subtipos, ao tratar uma exceção de um determinada classe todas as subclasses dessa exceção também serão tratadas.

8. Conclusão

Ao analisar as linguagens e comparar suas características percebemos que as escolhas em realizadas em tempo de projeto determinam as características das linguagens e os domínios para os quais elas serão mais indicadas.

ML e Scheme por serem fundamentadas no paradigma funcional, possuem uma excelente capacidade de escrita para o domínio da matemático, lógico e para sistemas simbólicos. Python foi desenvolvida para ser uma linguagem de propósito geral e consegue alcançar esse objetivo ao englobar o paradigma funcional, orientado a objetos e imperativo.

ML possui um alto grau de confiabilidade por ser estaticamente tipada e possuir um mecanismo de exceções nativo e um excelente suporte para abstração. Além disso ML permite o uso de estrturas das linguagens imperativas, aumentando a capacidade de escrita para domínios mistos que utilizem modelos formais e lógicas de negócios.

Python possui um grau de flexibilidade muito alto e isso tem impacto na sua confiabilidade, mas que é contrabalanceado com um modelo de objetos elegante, tratamento de exceções nativo e orientado a objetos e alta capacidade de abstração e recursos da programação funcional. A sua capacidade de escrita é alta para domínio de negócios e domínios mistos.

Segue abaixo um quadro resumo das características das linguagens:


ML

Scheme

Python

Simplicidade Global

paradigma imperativo

padronização

biblioteca padrão extensa e inconsistente entre versões

Minimalista

padronização

tipos de dados latentes

Minimalista

paradigma imperativo

unificação entre tipos e classes

Instruções de controle

estruturas semelhante as linguagens imperativas

encontro de padrões

Compreensão de listas

Recursividade para repetição

Recursividade eficiente

Estruturas de seleção simples e múltipla

Possui um conjunto reduzido de estruturas de controle: for, while, if e with.

Recursividade limitada

Compreensão de listas

Suporte a abstração

tratam as funções como tipos de dados de primeira ordem

programação funcional

estruturas e registros


tratam as funções como tipos de dados de primeira ordem

programação funcional

listas dinâmicas

Tudo é objeto e tipo de dado de primeira ordem

orientação a objetos elegante e consistente

Herança múltipla

Verificações de Tipos

Estaticamente tipada

inferência dos tipos

não realiza a conversão implícita de tipos

permite a declaração do tipo de uma variável

Dinâmicamente tipada

inferência dos tipos

não realiza a conversão implícita de tipos

tipos de dados latentes

Dinâmicamente tipada

inferência dos tipos

não realiza a conversão implícita de tipos

tipos de dados latentes


Manipulação de Exceções

suporte nativo ao tratamento de exceções.

Exceções podem conter valores e eventos associados

suporte nativo ao tratamento de exceções.

suporte implícito integrado ao interpretador de comandos.


9. Bibliografia

SEBESTA, Robert W. , Concepts of Programming Languages 7 ed.

WATT, David A. , Programming Language Concepts and Paradigms 1 ed.

DRAKE, Fred L., editor, Python Reference Manual - Release 2.5
19th September, 2006 http://docs.python.org/ref/ref.html

HARPER, Robert, Programming in Standard ML (WORKING DRAFT OF AUGUST 25, 2006.) - http://www.cs.cmu.edu/~rwh/smlbook/online.pdf

ABELSON, Harold, Structure and Interpretation of Computer Programs, http://mitpress.mit.edu/sicp/full-text/book/book.html

Wikipédia, a enciclopédia livre,

http://pt.wikipedia.org/wiki/Programa%C3%A7%C3%A3o_funcional

http://en.wikipedia.org/wiki/Python_%28programming_language%29

http://en.wikipedia.org/wiki/Scheme_%28programming_language%29

http://en.wikipedia.org/wiki/ML_language

acessado em 10/11/2006.