From: "Pierre-Evariste Dagand" <pedagand@gmail.com>
> I'm looking for advices about a "clean way" of doing something which,
> by design, isn't. So, let states the problem.
>
> I'm doing a combinator library based on Arrows for a Yampa-like system :
>
> type ('a,'b) arrow = Arrow of ( 'a -> 'b )
[...]
> But now, I would like to be able to "copy" a built arrow and to be
> able to execute the copy without side-effecting on the first one.
> Obviously, I cannot do that in this implementation.
Here is yet another solution, using objects, which actually combines
Zheng's and Oleg's ideas. That is, it separates state and function,
but provides only access to state through a cloning method, so that it
is completely type safe. This is just what objects are good at!
class ['a,'b] arrow (f : 'a -> 'b) =
object (self) method call = f method copy = self end
let (>>>) rf rg : ('a,'b) arrow =
object
val rf : ('a,'c) arrow = rf
val rg : ('c,'b) arrow = rg
method call x = rg#call (rf#call x)
method copy = {< rf = rf#copy; rg = rg#copy >}
end
let loop init rf : ('b,'c) arrow =
object
val mutable state = init
val rf : ('a*'b,'a*'c) arrow = rf
method call x =
let state', y = rf#call (state, x) in
state <- state'; y
method copy = {< rf = rf#copy >}
end
let arr = new arrow
let arr_counter = loop 0 (arr (fun (counter,x) -> counter+1, counter+x))
let arr_double = arr (fun x -> 2*x)
let arr_my_small_arrow = arr_counter >>> arr_double
The key here is the {< ... >} construct, which creates a shallow copy
of an object, eventually with some fields changed. As a result, in
loop there is no need to handle the state explicitly: the state field
in the copied object will be distinct from the state field in the
original object. On the other hand, you must explicitly update fields
holding arrows, since the copy is shallow. Note that the explicit
"val" fields are needed to allow updating their contents when copying.
One difference with Oleg's approach is that we take a copy of the
original object, rather than creating a completely new record. In this
case, this doesn't mean much, since there is no extra computation
involved. Still, the state after copying is not the original but the
current one. And this may matter more if the construction is more
complicated.
Jacques Garrigue