[MLton] Monadic MLton.Vector.create with update

Daniel C. Wang danwang@CS.Princeton.EDU
Wed, 29 Mar 2006 23:52:51 -0800


Perhaps, vector create is too ambitious. I'd be happier with an unfold 
operation. It's a relatively simple generalization of Vector.tabulate, 
that lets you do a lot more. (Not random access but I think for a lot of 
situations  that's okay). It also avoids the need for a dummy initial 
value too. I'd like to see this in addition to any MLton.Vector.create

functor Test (val unfold_vec : ('b -> ('b * 'a)) -> 'b -> int -> 'a  
vector) =
  struct
    fun fib_unfold {fibsub2,fibsub1,n} =
      if n < 2 then
        ({fibsub2=fibsub2,
          fibsub1=fibsub1,
          n=n+1},fibsub1)
        else let
          val fibn = fibsub1 + fibsub2
        in ({fibsub2=fibsub1,
             fibsub1=fibn,
             n=n+1},fibn)
        end
     val fibv = unfold_vec fib_unfold {fibsub1=1,fibsub2=1,n=0} 35
     fun tabulate (len,f) =
       unfold_vec (fn n => (n+1,f n)) 0 len
  end

fun unfold_vec gen seed len = let
  val array = Array.array (len, NONE)
  fun loop (i,seed) =
    if i < len then let
      val (seed,v) = gen seed
    in Array.update(array,i,SOME v);
      loop(i+1,seed)
    end
  else Vector.tabulate(len,(fn i => Option.valOf (Array.sub(array,i))))
in loop(0,seed)
end
structure T = Test(val unfold_vec = unfold_vec);


Vesa Karvonen wrote:
> Quoting Stephen Weeks <sweeks@sweeks.com>:
> [...]
>   
>> A subtle difference between this monadic code and Vesa's is the bounds
>> checks on subscript and update operations.  This code (as well as the
>> non-monadic version) dynamically changes the limit as the tabulator
>> fills in more elements in the array.  Vesa's bakes in the limit to
>> each monadic operation, allowing manipulation only of lower-indexed
>> vector elements.  A baked-in limit makes perfect sense, if the entire
>> computation for the index is going to be done right then, as the
>> monadic approach guarantees.  However, it also hints at a weakness of the
>> monadic approach -- it doesn't allow subscripts and updates to occur
>> after the tabulator is finished.  This seems weaker than the
>> non-monadic approach.
>>
>> As far as I can tell, the monadic approach treats subscripts and
>> updates the same.  That is, just as it disallows updates after the
>> vector has been created, it also disallows subscripts (in the
>> tabulator functions, not via Vector.sub, obviously).  This doesn't
>> work so well in the situation I mentioned earlier, where one wants to
>> create a vector of promises, where the promises can refer to other
>> elements in the vector.
>>     
> [...]
>
> The following is just a quick comment.  I haven't yet read your entire
> article carefully and might not have the time to do so until much later
> today.
>
> I think that the kind of computations that you are describing are handled
> in Haskell by using the implicit recursion that lazy evaluation allows.
> In a lazy ML you could simply write
>
>  val rec fib = V.tabulate (n,
>                            fn i => if i <= 1 then i
>                                    else V.sub (fib, i-1) + V.sub (fib, i-2))
>
> What I'm thinking is that perhaps one shouldn't attempt to make the create
> interface too "general".  Using a suitable fixpoint combinator and explicit
> lazy evaluation, one should be able to do similar computations even in a
> strict ML.  Allowing imperative updates to occure during the initial
> construction of an immutable vector already makes it seem a little ad hoc to
> me.  The monadic approach (even without updates) already allows "complete
> induction" to be used and that seems powerful enough to me.
>
> -Vesa Karvonen
>
> _______________________________________________
> MLton mailing list
> MLton@mlton.org
> http://mlton.org/mailman/listinfo/mlton
>