Resolvi escrever depois de ver a ideia de usar unidades junto dos valores no tutorial oficial do Erlang.
No exemplo, vamos converter centímetros em polegadas ou vice-versa. É fácil estender a ideia para converter outras unidades, como temperatura, pressão, moeda, etc.
O principal é só isso aqui:
(define (convert-length value) (match value [`(,(? number? v) centimeter) `(,(* v 2.54) inch)] [`(,(? number? v) inch) `(,(/ v 2.54) centimeter)]))
Ou seja, criamos uma função chamada convert-length que recebe um valor. O interessante é que esse valor não é um tipo numérico, mas sim uma estrutura de dados que contém um valor numérico e uma unidade.
Com isso, podemos ter uma única função de conversão, e sabemos sempre exatamente com que unidade estamos trabalhando.
O que é feito na função é usar casamento de padrões (pattern matching), uma funcionalidade padrão do Racket e bastante poderosa para trabalhar com estruturas de dados, para realizar a conversão apropriada de acordo com o valor de entrada.
[`(,(? number? v) centimeter) ; padrão `(,(* v 2.54) inch)] ; valor retornado
O primeiro padrão casa com uma lista formada por um número, armazenado na variável v, e o símbolo literal centimeter. A segunda parte da cláusula computa a conversão e cria uma estrutura com a nova unidade de medida.
O código completo:
#lang racket (require rackunit) ;; Idea from the Erlang Tutorial ;; http://www.erlang.org/doc/getting_started/seq_prog.html#id64621 (define (convert-length value) (match value [`(,(? number? v) centimeter) `(,(* v 2.54) inch)] [`(,(? number? v) inch) `(,(/ v 2.54) centimeter)] [_ (error "Wrong value format. Value must be a list of two elements: a number and an unit, inch or centimeter.")])) ;; From Matthias Felleisein ;; http://lists.racket-lang.org/users/archive/2011-November/049154.html (define (tee tag v) (displayln `(,tag ,v)) v) ;;---------------------------------------------------------------------- ;; Tests (check-equal? (tee 'test-1 (convert-length '(1 centimeter))) '(2.54 inch)) (check-equal? (tee 'test-2 (convert-length '(2.54 inch))) '(1.0 centimeter)) (check-equal? (tee 'test-3 (convert-length (convert-length '(1 centimeter)))) '(1.0 centimeter)) (check-exn exn:fail? (λ () (convert-length 3.4)))