Kodumaro :: Standard ML – continuação

Publicado em 17 de Março, 2016
As sombras da programação.

No artigo anterior vimos como criar funções que retornem uma sequência de inteiros. Porém, para estendê-las para gerarem uma sequência de caracteres, por exemplo, teríamos de reescrever o código.

Neste artigo mudaremos o código para torná-lo estensível a outros tipos usando um functor.

Módulos em Standard ML são chamados structures e podem ser instanciados sob demanda com o uso de functors. A primeira coisa que precisamos é da assinatura dos módulos gerados pelo functor.

Os módulos gerados precisam ter o tipo de dado (t), uma função para construir o gerador (build) e outra para converter o gerador em lista (genToList). A assinatura fica assim então:

signature GENERATOR =
sig
  eqtype t
  val genToList : (unit -> t option) -> t list
  val build : (t * t) -> unit -> t option
end

Também precisamos da assinatura da structure usada como parâmetro do functor. Essa structure deve gerenciar a reiteração dos dados. Para tanto, deve conhecer o tipo de dado (t), ter uma função para fazer o passo, ou seja, incrementar o dado (increment), e outra para testar a condição de parada (checkFinish).

A assinatura fica assim então:

signature GENERATOR' =
sig
  eqtype t
  val increment : t ref -> unit
  val checkFinish : t ref -> t -> bool
end

O functor terá praticamente o mesmo código de nossas funções originais, mas agora será extensível.

Vamos então trazer o código para o functor:

functor Generator (G : GENERATOR') : GENERATOR =
struct
  type t = G.t

  val genToList =
    let
      fun genToList' (acc : t list) (gen : unit -> t option) : t list =
        case gen () of NONE       => rev acc
                     | SOME value => genToList' (value::acc) gen
    in
      genToList' nil
    end

  local
    fun build' i finish () =
      if G.checkFinish i finish then
        NONE
      else
        let
          val r = !i
        in
          G.increment i;
          SOME r
        end
  in
    fun build (start, finish) = build' (ref start) finish
  end
end

Repare que agora em vez de int estamos usando t (que é igual a G.t), e delegamos a verificação do fim da reiteração e o incremento da referência para G.

Resta agora recriar nossas funções originais. Como só nos interessam xrange e range, podemos criar a structure como local:

local
  structure IntGenerator = Generator (struct
    type t = int

    fun increment v : unit = v := !v + 1
    and checkFinish i finish = !i >= finish
  end)
in
  val xrange = IntGenerator.build
  val range = IntGenerator.genToList o xrange
end

E para criar funções equivalentes para caracteres? A lógica é a mesma:

local
  structure StringGenerator = Generator (struct
    type t = char

    fun increment v : unit = v := chr (ord (!v) + 1)
    and checkFinish (i : char ref) (finish : char) = !i > finish
  end)
in
  val xcharseq = StringGenerator.build
  val strseq = implode o StringGenerator.genToList o StringGenerator.build
end

Veja que agora podemos com facilidade estender o lógica de criação de geradores para virtualmente qualquer tipo, com a ajuda do functor.