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.
Functional | ML