stainless icon indicating copy to clipboard operation
stainless copied to clipboard

State capturing closure-like construct

Open vkuncak opened this issue 2 years ago • 4 comments

The following code works in Scala and is an explicit way of representing closures as objects that capture their environment as a trait parameter. It does not work in Stainless because trait parameters are not supported. However, the syntactic sugar capturing could be perhaps supported and directly translated in imperative phase elimination.

//import stainless.annotation.*
object Capture:

  trait FunEff[A, E, B](val e: E):
    def apply(a: A): B
  end FunEff

  case class Ref[T](var content: T)

  def capturing[A, E, B](e1: E)(f: E => A => B): FunEff[A,E,B] =
    new FunEff[A, E, B](e1) {
      override def apply(a: A) = f(e)(a)
    }

  val adder = capturing(Ref[Int](0))((e: Ref[Int]) =>
    (a: Int) => 
      e.content = e.content + a
      e.content      
  )

  val incrementer = capturing(Ref(()))((_:Ref[Unit]) => 
    (a: Int) => a + 1
  )

end Capture

What do you think @mario-bucev @samarion ?

vkuncak avatar Dec 19 '22 18:12 vkuncak

The most important thing is in fact to have something like FunEff that has a method that depends on a mutable trait parameter. How we construct instances of FunEff is less important.

When f : FunEff then f is an object with apply and a field storing a mutable object. So, a call

val b = f(a)

becomes something like

val (b,f1) = f.apply(f, a)

We can represent traits with mutable constructor fields by having each of the trait methods take that field as an extra argument, then applying the usual imperative transformation.

vkuncak avatar Dec 19 '22 21:12 vkuncak

Regis wrote up an example with an SFun case class which does something similar: https://github.com/epfl-lara/stainless/blob/a041c9790d24411f45c4329526adec334bc71337/frontends/benchmarks/imperative/valid/Tutorial.scala#L192-L194

samarion avatar Dec 20 '22 17:12 samarion

@samarion : thanks, that's it! I remembered, but could not find it in the benchmarks.

The question is if we want to make this more seamless, or just document it as a pattern.

On another note, the S in SFun or E in my example must be immutable. If you want to allow hierarchical state, then you would like to allow S/E to be mutable, but then perhaps no need to wrap it.

vkuncak avatar Dec 20 '22 21:12 vkuncak

In case it's not obvious, this question was partly inspired by the bug https://github.com/epfl-lara/stainless/issues/1365

vkuncak avatar Dec 20 '22 21:12 vkuncak