- About Scala
- Documentation
- Code Examples
- Software
- Scala Developers
zipper
Wed, 2010-06-09, 10:21
Hi,
inspired by Greg Meredith's blog post and Jesper Nordenberg's comment i implemented some code generation which allows navigating trees of caseclasses and performing functional updates on them.
Here's an example. Suppose we model the state of a pacman game the following way:
Changing the game state to 'run is simple using the copy method:
However, changing pacman's super mode is much more cumbersome (and it gets worse for deeper structures):
Using the compiler-generated location classes this gets much easier:
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 compilergenerates:
in the class C
The class Loc is defined in the library as
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
inspired by Greg Meredith's blog post and Jesper Nordenberg's comment i implemented some code generation which allows navigating trees of caseclasses 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 compilergenerates:
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
Wed, 2010-06-09, 20:07
#2
Re: zipper
I coughed up some assless (i.e. not even half-assed) thoughts on this
topic 2.5 years ago, and there was a modicum of discussion on it at the
time:
http://wiki.liftweb.net/index.php/ASRUSP_is_A_Scala_Record_Update_Syntax_Proposal
-0xe1a
http://wiki.liftweb.net/index.php/ASRUSP_is_A_Scala_Record_Update_Syntax_Proposal
-0xe1a
scala> val a = A(1, "Hello")
a: records.A = A(1,Hello)
scala> val b = B(a, a)
b: records.B = B(A(1,Hello),A(1,Hello))
scala> val b2 = b ba1 (_ a1) set 4
b2: records.B = B(A(4,Hello),A(1,Hello))
scala> val p = b2 ba2 (_ a2)
p: records.FieldPath[records.B,String] = records$ConsPath@973678
scala> p set "Bye"
res0: records.B = B(A(4,Hello),A(1,Bye))
/Jesper Nordenberg
--- On Wed, 6/9/10, Lukas Rytz <lukas [dot] rytz [at] epfl [dot] ch> wrote: