This page is no longer maintained — Please continue to the home page at www.scala-lang.org

Re: zipper

1 reply
rytz
Joined: 2008-07-01,
User offline. Last seen 45 weeks 5 days ago.
Unfortunately it's not possible to implement it as a compiler plugin.The reason is that the code needs to be generated before type-checking(otherwise the ".loc" calls won't type-check). But it's not possible to generate it directly after parsing either, because we need to know thingslike "is the parameter type another case class with @zip?".
So the code generation happens "somewhere" during naming / typing.
Actually, many people are fighting with that problem (generate codebefore type-checking, but still require some symbol / type informationto generate it, e.g. Kevin Wright), it would be good to solve it at some point. I see two solutions: - type-checking a first time without reporting errors, then generate the   code, then type-check again. - providing some plugin-infrastructure for the namer/typer to generate    code.The first approach does not work well because type-checking changesthe trees in many situations ("adapt").
Having it in 2.8 is certainly not possible, there won't be any new features.
Lukas

On Wed, Jun 9, 2010 at 13:39, Daniel Kröni <daniel.kroeni@gmail.com> wrote:
This is just amazing! I also read Greg's blog post and realized how
cool this is but it didn't look comfortable for my use case (modifying
ASTs). Now I got exactly what I was looking for! Thank you very much.
Any chance you turn this into a compiler plugin or even put it into
the 2.8 release (I know I'm dreaming)

Cheers Daniel

On Wed, Jun 9, 2010 at 11:20 AM, Lukas Rytz <lukas.rytz@epfl.ch> wrote:
> Hi,
> inspired by Greg Meredith's blog post and Jesper Nordenberg's comment
> i implemented some code generation which allows navigating trees of case
> classes and performing functional updates on them.
> Here's an example. Suppose we model the state of a pacman game the
> following way:
>
> scala> @zip case class Pacman(lives: Int = 3, superMode: Boolean = false)
> scala> @zip case class Game(state: Symbol = 'pause, pacman: Pacman =
> Pacman())
> scala> val g = Game()
> g: Game = Game('pause,Pacman(3,false))
>
> Changing the game state to 'run is simple using the copy method:
>
> scala> val g1 = g.copy(state = 'run)
> g1: Game = Game('run,Pacman(3,false))
>
> However, changing pacman's super mode is much more cumbersome
> (and it gets worse for deeper structures):
>
> scala> val g2 = g1.copy(pacman = g1.pacman.copy(superMode = true))
> g2: Game = Game('run,Pacman(3,true))
>
> Using the compiler-generated location classes this gets much easier:
>
> scala> val g3 = g1.loc.pacman.superMode set true
> g3: Game = Game('run,Pacman(3,true))
>
>
> What happens? For a case class "@zip case class C(x: A, y: B)", suppose
> T is another case class annotated with @zip, U is any other type, the
> compiler
> generates:
>
> class CLoc[T, P <: Loc[T, _, _]](v: C, f: C => Option[P]) extends Loc[T, C,
> P](v, f) { self =>
>   override def copy(v: C) = new CLoc[T, P](v, f)
>   def x: ALoc[T, CLoc[T, P]] = new ALoc[T, CLoc[T, P]](v.x, (x: A) => {
>     Some(self.copy(v.copy(x = x)))
>   })
>   def y: Loc[T, B, CLoc[T, P]] = new Loc[T, B, CLoc[T, P]](v.y, (y: B) => {
>     Some(self.copy(v.copy(y = y)))
>   })
> }
>
> in the class C
>
> def loc = new CLoc[C, Nothing](this, v => None)
>
> The class Loc is defined in the library as
>
> /**
>  * T: top type
>  * E: element type of this location
>  * P: parent location type
>  */
> class Loc[T, E, P <: Loc[T, _, _]](v: E, f: E => Option[P]) {
>   def up: Option[P] = f(v)
>   def top: T = up match {
>     case Some(p) => p.top
>     case _ => v.asInstanceOf[T]
>   }
>   def copy(v: E): Loc[T, E, P] = new Loc[T, E, P](v, f)
>   def set(v: E) = copy(v).top
> }
>
>
> The prototype is on github, many things are not worked out yet e.g. generic
> case classes, case classes without copy method or name clashes.
> Comments welcome :)
> Lukas

Kevin Wright 2
Joined: 2010-05-30,
User offline. Last seen 26 weeks 4 days ago.
Re: zipper
(sending again, the mailing list bounced me first time...)
Interestingly, I'm taking both approaches.  The ultimate goal is to provide a framework for code generation based on the double-typecheck approach.

I'm trying to resist it, but it looks more and more like the best way to achieve this would be spawning a separate instance of Global for the first round of typechecking.  My only main concern here is that this will cause the compiler to duplicate some work and take a performance hit.

Right now, Martin is currently 10mins down the road from me teaching a Scala course.  I'll be speaking to him tonight, and my hope is that he can give me a some more insight on the compiler internals.  With any luck, that should help me refine the process a bit, and optimize around any compiler slow-downs.
Failing that, it's going to have to be sorted directly in the compiler (not as a plugin), which means we'll all have to wait a bit longer :(At least we know that approach is definitely viable.  After all, it already works for @BeanProperty

On 9 June 2010 13:25, Lukas Rytz <lukas.rytz@epfl.ch> wrote:
Unfortunately it's not possible to implement it as a compiler plugin.The reason is that the code needs to be generated before type-checking(otherwise the ".loc" calls won't type-check). But it's not possible to generate it directly after parsing either, because we need to know thingslike "is the parameter type another case class with @zip?".
So the code generation happens "somewhere" during naming / typing.
Actually, many people are fighting with that problem (generate codebefore type-checking, but still require some symbol / type informationto generate it, e.g. Kevin Wright), it would be good to solve it at some point. I see two solutions: - type-checking a first time without reporting errors, then generate the   code, then type-check again. - providing some plugin-infrastructure for the namer/typer to generate    code.The first approach does not work well because type-checking changesthe trees in many situations ("adapt").
Having it in 2.8 is certainly not possible, there won't be any new features.
Lukas

On Wed, Jun 9, 2010 at 13:39, Daniel Kröni <daniel.kroeni@gmail.com> wrote:
This is just amazing! I also read Greg's blog post and realized how
cool this is but it didn't look comfortable for my use case (modifying
ASTs). Now I got exactly what I was looking for! Thank you very much.
Any chance you turn this into a compiler plugin or even put it into
the 2.8 release (I know I'm dreaming)

Cheers Daniel

On Wed, Jun 9, 2010 at 11:20 AM, Lukas Rytz <lukas.rytz@epfl.ch> wrote:
> Hi,
> inspired by Greg Meredith's blog post and Jesper Nordenberg's comment
> i implemented some code generation which allows navigating trees of case
> classes and performing functional updates on them.
> Here's an example. Suppose we model the state of a pacman game the
> following way:
>
> scala> @zip case class Pacman(lives: Int = 3, superMode: Boolean = false)
> scala> @zip case class Game(state: Symbol = 'pause, pacman: Pacman =
> Pacman())
> scala> val g = Game()
> g: Game = Game('pause,Pacman(3,false))
>
> Changing the game state to 'run is simple using the copy method:
>
> scala> val g1 = g.copy(state = 'run)
> g1: Game = Game('run,Pacman(3,false))
>
> However, changing pacman's super mode is much more cumbersome
> (and it gets worse for deeper structures):
>
> scala> val g2 = g1.copy(pacman = g1.pacman.copy(superMode = true))
> g2: Game = Game('run,Pacman(3,true))
>
> Using the compiler-generated location classes this gets much easier:
>
> scala> val g3 = g1.loc.pacman.superMode set true
> g3: Game = Game('run,Pacman(3,true))
>
>
> What happens? For a case class "@zip case class C(x: A, y: B)", suppose
> T is another case class annotated with @zip, U is any other type, the
> compiler
> generates:
>
> class CLoc[T, P <: Loc[T, _, _]](v: C, f: C => Option[P]) extends Loc[T, C,
> P](v, f) { self =>
>   override def copy(v: C) = new CLoc[T, P](v, f)
>   def x: ALoc[T, CLoc[T, P]] = new ALoc[T, CLoc[T, P]](v.x, (x: A) => {
>     Some(self.copy(v.copy(x = x)))
>   })
>   def y: Loc[T, B, CLoc[T, P]] = new Loc[T, B, CLoc[T, P]](v.y, (y: B) => {
>     Some(self.copy(v.copy(y = y)))
>   })
> }
>
> in the class C
>
> def loc = new CLoc[C, Nothing](this, v => None)
>
> The class Loc is defined in the library as
>
> /**
>  * T: top type
>  * E: element type of this location
>  * P: parent location type
>  */
> class Loc[T, E, P <: Loc[T, _, _]](v: E, f: E => Option[P]) {
>   def up: Option[P] = f(v)
>   def top: T = up match {
>     case Some(p) => p.top
>     case _ => v.asInstanceOf[T]
>   }
>   def copy(v: E): Loc[T, E, P] = new Loc[T, E, P](v, f)
>   def set(v: E) = copy(v).top
> }
>
>
> The prototype is on github, many things are not worked out yet e.g. generic
> case classes, case classes without copy method or name clashes.
> Comments welcome :)
> Lukas




--
Kevin Wright

mail/google talk: kev.lee.wright@gmail.com
wave: kev.lee.wright@googlewave.com
skype: kev.lee.wright
twitter: @thecoda

Copyright © 2012 École Polytechnique Fédérale de Lausanne (EPFL), Lausanne, Switzerland