Criando funções em Lisp: o papel de defun e lambda, defn e fn

Posted in Uncategorized on 02/10/2014 by wiltonsilva

Como foi dito em posts anteriores, em linguagens da família Lisp, tudo são listas, ou em Clojure, sequencias… ;)
Acontece que declarações de função não são excessões! Tomemos como exemplo a declaração de funções em dois dialetos de Lisp: Common Lisp e Clojure:

Em Common Lisp, declara-se funções da seguinte maneira:

(defun square (x) (* x x))

Podemos notar que essa declaração nada mais é doque uma lista, na qual o primeiro elemento é a função defun, que tem como tarefa criar uma função e atribuí-la (bind) ao nome square.
Tente digitar no seu REPL:
(car '(defun square (x) (* x x)))
e veja o retorno!

Em Clojure não é muito diferente, sendo que a declaração de função é feita da seguinte maneira:

(defn square [x] (* x x))

podendo também ser declarada mais explicitamente:

(def square (fn [x] (* x x)))

A maior diferença é que Clojure não usa a palavra-chave lambda para definir funções, mas sim fn, que tem basicamente a mesma função.
Em Clojure, podemos ainda usar as funções doc e source para olhar ainda mais de perto como essas funções mágicas funcionam =D

Tente no seu REPL!

(source defn)
(source fn)
(doc defn)
(doc fn)

Saudações…

SLIME: usando Emacs para edição de código Common Lisp

Posted in Uncategorized on 11/18/2013 by wiltonsilva

São poucas as IDEs próprias para edição de código Lisp,  bom, a menos que a linguagem escolhida seja Clojure, que conta com um plugin muito bom para ser usada com Eclipse (o Counterclockwise) e a LightableIDE, que apresenta um conceito de feedback maravilhoso e que vale a pena ser explorado.

Pra quem quer trabalhar com Scheme existe o DrRacket, que é bem simples de usar também.

Já para trabalhar com Common Lisp as (boas) escolhas ficam um pouco mais limitadas, com a maior parte da comunidade recomendando o SLIME (“Superior Lisp Interaction Mode for Emacs”), que permite que você conecte seu Emacs a um servidor rodando alguma implementação de Common Lisp, como o SBCL ou CLISP por exemplo.

A configuração básica (suficiente pra rodar um hello-world) é relativamente simples, não sendo necessário nenhum conhecimento mais aprofundado de Emacs ( que não é exatamente o editor mais trivial do mundo, rsrs…).

Segue abaixo um passo a passo:

1. Para usar o SLIME, a maneira mais segura é baixar um snapshot direto do repositório corrente. Esse arquivo contém os fontes da última versão estável do SLIME.

2. Presume-se que você já tenha a versão mais recente do Emacs instalada. Se esse não é o caso, pode instalar com o comando:

sudo apt-get install emacs24

3. Você também deve ter instalada em sua máquina uma implementação de Common Lisp, como por exemplo o SBCL. Se não tiver, o comando é o seguinte:

sudo apt-get install sbcl

4. O próximo passo é configurar seu arquivo “.emacs“. Esse é o arquivo de inicialização do Emacs, e fica no seu diretório home “~/.emacs”. Abra o arquivo e insira o seguinte trecho de código:

(setq inferior-lisp-program “CAMINHO_DO_SBCL”)

(add-to-list ‘load-path “CAMINHO_DO_SLIME”)

(require ‘slime)

(slime-setup)

Lembre-se de substituir CAMINHO_DO_SBCL e CAMINHO_DO_SLIME pelo caminho real! =D

O caminho do SBCL você consegue com o comando

which sbcl

e o caminho do SLIME é o diretório onde você descompactou o snapshot do SLIME.

5. Feito isso, abra o seu Emacs e digite

Alt+x slime

Se a configuração foi feita corretamente, o Emacs vai se conectar ao servidor e você já pode criar um arquivo para edição =)

Para fazer isso, digite:

Ctrl+x Ctrl+f nome-do-arquivo.lisp

Se o arquivo não existir ele será criado.

A partir daí, você pode escrever suas expressões à vontade e compilar com o comando Ctrl+c Ctrl+c que vai avaliar seu arquivo e carrega-lo para ser usado no REPL logo abaixo da tela.

E essa é a configuração mais simples possível para começar com o SLIME e Common Lisp!

Emacs, juntamente com o Vim, é um dos editores mais sofisticados que existem, e as possibilidades são enormes. Vale dar uma estudada na documentação oficial e nessa cheat sheet legal que eu achei =)

 

Saudações…

 

Homoiconicidade: viabilizando a escrita de macros

Posted in Uncategorized on 11/04/2013 by wiltonsilva

Uma característica de linguagens Lisp em especial as distingue de qualquer outra linguagem, o uso que Lisp faz de Macros.

A proposta desse post, entretanto, não é analisar o uso, ou mesmo técnicas para construção de macros. Esse assunto é extenso e requer experiência e uma boa dose de bom gosto para ser bem explorado. O objetivo aqui é apresentar a característica da linguagem que torna possível a contrução de macros: a homoiconicidade.

Mas antes, uma breve explicação do que são macros.

Macros são funções que retornam funções!  É código que retorna código.

Em Lisp, funções são consideradas first class citizens, ou cidadãos de primeira classe. Isso significa que funções podem ser passadas como parâmetros para outras funções, armazenadas em listas, ou mesmo servir de retorno de outras funções.

O conceito que torna isso possível é homoiconicidade, quer dizer que a estrutura da linguagem é semelhante à sua sintaxe. Em Lisp, tudo são listas, a lista é a unidade de abstração primária da linguagem. Sendo assim, até mesmo as contruções mais simples, como, por exemplo a expressão

(+ 1 1)

nada mais são do que listas. Nesse caso temos uma lista na qual o primeiro elemento é uma função e os outros dois são primitivos inteiros. Por convenção, o primeiro elemento de uma lista é uma função, e os elementos seguintes são parâmetros recebidos por aquela função.

Agora, já que Lisp não faz distinção entre o código do seu programa e os dados manipulados por ele, e se tudo na linguagem é representado por listas, se uma função retorna uma lista, essa lista pode ser interpretada como uma função também!

Esse conceito pode parecer um pouco confuso à princípio, mas na realidade é muito simples. Vamos analisar um exemplo bem curto:

(defmacro make-function [nome]
(list 'defn nome []
(list 'println "Alô, mundo das macros!")))

Em Clojure, defmacro é o nome da função para montagem de macros. essa função recebe como parâmetros o nome da função a ser gerada e argumentos opcionais. No nosso caso ela só recebe o nome da função.

Logo em seguida é onde acontece a ‘mágica’, a função aqui vai retornar uma lista! Que, como foi visto, é código Lisp. Assim, como funções nada mais são do que listas, podemos dizer que essa função de fato retorna outra função.

Analizemos essa lista:

(list ‘defn nome [] (list ‘println “Alô, mundo das macros!”))

O primeiro elemento da lista é uma função ‘list’, que recebe n parâmetros e retorna uma lista composta por esses parâmetros.

O segundo elemento da lista é ‘defn’, que é o nome da função usada para declarar funções, como por exemplo: (defn soma [] (+ 1 1))

O terceiro elemento é o nome da função, que foi passado como parâmetro para defmacro.

O quarto elemento é um vetor vazio, que poderia conter quaisquer parâmetros requeridos pela nossa função a ser criada.

O quinto elemento é outra lista, que por sua vez contém o nome da função println e uma string a ser impressa.

Agora podemos chamar a nossa macro e verificar seu retorno:

user=> (make-function write)
#’user/write

Como retorno, obtivemos o nome da função criada pela nossa macro, no caso o nome é ‘write’.

Vamos agora usar nossa recém criada função:

user=> (write)
Alô, mundo das macros!
nil

O uso de macros constitui uma das maiores vantagens das linguagens da família Lisp, segue o link para download gratuito de um dos melhores textos de referência sobre o assunto (extenso, mas essencial):

http://www.paulgraham.com/onlisp.html

Saudações…

Concorrência em Clojure com STM

Posted in Uncategorized on 11/01/2013 by wiltonsilva

Diferente de linguagens funcionais puras, Clojure fornece meios para guardar o estado de execução de um programa quando é absolutamente necessário que isso aconteça.
Isso, entretanto, é feito de maneira segura e limpa, não sobrecarregando o programador com a responsabilidade pelo controle de acesso a esse estado.
Clojure usa o conceito de STM (Software Transactional Memory) para garantir que o acesso ao estado da aplicação é cuidadosamente sincronizado.
Abaixo está um exemplo simples de como isso pode ser feito.
O programa abaixo consiste em uma variável ‘balance’, que representa o saldo de uma conta bancária. Todo acesso a essa variável é feito por meio de ‘account’, que é declarado como uma referência a essa variável. Nesse ponto, todo o acesso é gerenciado via STM, as modificações são todas transacionais. Sendo thread safe, não há o risco de que, executando em múltiplos cores, o estado da variável balance seja corrompido, permitindo operações ilegais na conta do usuário.
As principais interfaces de uso do programa são as funções ‘sacar’,  ‘depositar’ e ‘get-balance-from’, podendo ser invocadas da seguinte maneira:
user=> (get-balance-from usuario)
0
user=> (depositar 4)
4
user=> (sacar 3)
1
user=> (sacar 5)
Saldo insuficiente

nil
user=> (get-balance-from usuario)
1

Segue o código fonte comentado do programa:

;define a mensagem para ser mostrada em caso de saldo insuficiente para saque.
(def saldo-insuficiente “Saldo insuficiente”)

;define um alias para o operador -, permitindo maior fluência na leitura do código.
(def saque -)

;idem.
(def deposito +)

;inicia a conta do cliente com saldo 0.
(def balance 0)

;define account como referência a balance.
(def account (ref balance))

;função genérica para operações na conta.
(defn perform [oper amount]
            (dosync (alter account oper amount)))

;definição de modelo de cliente.
(defrecord client [name client-account])

;define um novo usuario de acordo com o modelo definido anteriormente.
(def usuario (->client “nome-usuario” account))

;função para recuperar saldo do usuário.
(defn get-balance-from [client-name]
             (deref (:client-account client-name)))

;verificaçao de saldo suficiente.
(defn saldo-suficiente? [amount]
             (>= (get-balance-from usuario) amount))

;definição da operação de saque.
(defn sacar [amount]
             (if (saldo-suficiente? amount)
                         (perform saque amount)
                         (println saldo-insuficiente)))

;definição da operação de depósito.
(defn depositar [amount]
             (perform deposito amount))

Para informações mais detalhadas sobre concorrência em Clojure, segue o link…
http://clojure.org/concurrent_programming

Saudações…

Testes Unitários Nativos em Clojure

Posted in Uncategorized with tags , on 10/30/2013 by wiltonsilva

Testes unitários em Clojure chamam a atenção por serem elegantes, limpos e nativos (significando que não é necessário adicionar dependências ao seu projeto).

Abaixo está a definição de duas funções ilustrativas:

(defn square [x]  (* x x))

(defn sum-of-squares [x y] (+ (square x) (square y)))

E esses são seus respectivos testes:

(use ‘clojure.test)

(deftest test-square (is (= 4 (square 2))))

(deftest test-sum-of-squares (is (= 13 (sum-of-squares 2 3))))

Os testes são assim escritos de modo mais fluente do que é comumente encontrado em linguagens imperativas como, por exemplo, Java, podendo ser lidos quase naturalmente, como pode ser observado abaixo:

(deftest test-square (is (= 4 (square 2)))) -> “É igual a 4 o quadrado de 2?”

ou

(deftest test-sum-of-squares (is (= 13 (sum-of-squares 2 3)))) -> “É igual a 13 a soma dos quadrados de 2 e 3?”

Além dessa facilidade, também é simples compor uma suíte de testes para execução automática, para isso, cria-se um teste englobando os outros dois escritos previamente:

(deftest test-suite (test-square) (test-sum-of-squares))

Os testes podem assim ser executados por meio do tão conhecido REPL chamando a suíte:

user=> (test-suite)
nil

Sendo que eventuais falhas em testes são reportadas de forma clara e compreensível. Como exemplo, podemos intencionalmente “adulterar” nossa função square:

(defn square [x]  (* x 4))

e veremos como nossos testes passam a falhar:

user=> (test-suite)
FAIL in (test-suite test-square) (NO_SOURCE_FILE:6)
expected: (= 4 (square 2))
actual: (not (= 4 8))

FAIL in (test-suite test-sum-of-squares) (NO_SOURCE_FILE:8)
expected: (= 13 (sum-of-squares 2 3))
actual: (not (= 13 20))
nil

Essa foi uma exposição resumida, ainda há muito mais a ser explorado referente ao assunto. Pra quem se interessar em dar uma aprofundada no tópico, recomendo a especificação da própria API clojure.test:

http://richhickey.github.io/clojure/clojure.test-api.html

Saudações…

Seguir

Obtenha todo post novo entregue na sua caixa de entrada.