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

Compiler-generated lenses for record fields

24 replies
Tony Morris 2
Joined: 2009-03-20,
User offline. Last seen 42 years 45 weeks ago.

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

OK, so we recently got the copy method for updating fields of case
classes.

However, what about lenses in a more general and composable form? It
is possible to implement as a library, right up to the point where it
would require you to implement the lens for each record (I use the
term "record" so as not to be necessarily specific to case classes) field.

Let us first define a lens in library code.

The Identity functor is trivial to get us started:

case class Identity[A](a: A) {
def map[B](f: A => B): Identity[A] = sys.error("todo")
def flatMap[B](f: A => Identity[B]): Identity[B] = sys.error("todo")
}

Next we define costate (the categorical dual to state) as a comonad
transformer:

// A Comonad for any Comonad[F] (todo)
case class CoStateT[A, F[_], B](put: F[A => B], set: A)

Next we are able to define a Lens:

case class Lens[T, R](k: T => CoStateT[R, Identity, T])

Now, I have been a bit elaborate here with the comonad transformer
then specialising to Identity. I did this because there is a huge and
useful library behind CoStateT, which benefits by being in transformer
version. Nevertheless, we may simplify for the purposes of staying on
track with lenses:

case class CoState[A, B](put: A => B, up: A) {
// A Comonad
def coFlatMap[C](f: CoState[A, B] => C): CoState[A, C] =
CoState(a => f(CoState(put, a)), up)
}

A lens is now a little simpler (specialised):

case class Lens[T, R](k: T => CoState[R, T])

Now, we sometimes think of a "lens" as the pair of a get and set method.

* get: T => R // get the field on T of type R
* set: T => R => T // set the field on T of type R, returning new T

I encoded it earlier with a representation using CoState to take
advantage of this particular data type and its library (it is very
rich). We can still construct a Lens using get/set pairs quite easily:

object Lens {
def lens[T, R](
get: T => R
, set: T => R => T
): Lens[T, R] =
Lens(t =>
CoState[R, T](set(t), get(t)))
}

We can also implement get and set methods on Lens easily (omitted for
brevity).

Now one of the important, killer useful properties of lenses is that
*they compose*. What does this mean? It means the following function
exists (also with an identity Lens[X, X]):

def compose[A, B, C](f: Lens[A, B], g: Lens[B, C]): Lens[A, C]

Let's write it as a method to demonstrate it:

case class Lens[T, R](k: T => CoState[R, T]) {
def compose[Q](that: Lens[R, Q]): Lens[T, Q] =
Lens[T, Q](t => {
val CoState(f, a) = k(t)
val CoState(g, b) = that.k(a)
CoState(f compose g, b)
})
}

To state it in English, "if we have a lens from T to R, and a lens
from R to Q, then we have a lens from T to Q." To provide an example:
if our Building has a (lens) Window and a Window has a (lens)
Perimeter, then our Building has a (lens) Window. This would allow us
to "get and set" the Window property on the Building class, thus,
answering the often-encountered questions regarding traversing down
object graphs and getting all messy. I digress, but hopefully, this
conveys an important point.

Further, this Lens class deserves to have all sorts of combinators on
it for manipulating record fields, but I have omitted them for brevity

Joshua.Suereth
Joined: 2008-09-02,
User offline. Last seen 32 weeks 5 days ago.
Re: Compiler-generated lenses for record fields
+1 on a lenses library with a simple interface.   Most of the time I would need lenses though, it's in a different context.  One of interfacing with other teams who've defined the same data abstractions with slightly differing values and I want to use lenses to convert between the two.   Nothing in your API prevents that, but the bit about compiler magic is where we may start disagreeing on what it should look like :)
- Josh

On Wed, May 25, 2011 at 8:08 AM, Tony Morris <tonymorris [at] gmail [dot] com> wrote:

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

OK, so we recently got the copy method for updating fields of case
classes.

However, what about lenses in a more general and composable form? It
is possible to implement as a library, right up to the point where it
would require you to implement the lens for each record (I use the
term "record" so as not to be necessarily specific to case classes) field.

Let us first define a lens in library code.

The Identity functor is trivial to get us started:

case class Identity[A](a: A) {
 def map[B](f: A => B): Identity[A] = sys.error("todo")
 def flatMap[B](f: A => Identity[B]): Identity[B] = sys.error("todo")
}

Next we define costate (the categorical dual to state) as a comonad
transformer:

// A Comonad for any Comonad[F] (todo)
case class CoStateT[A, F[_], B](put: F[A => B], set: A)

Next we are able to define a Lens:

case class Lens[T, R](k: T => CoStateT[R, Identity, T])

Now, I have been a bit elaborate here with the comonad transformer
then specialising to Identity. I did this because there is a huge and
useful library behind CoStateT, which benefits by being in transformer
version. Nevertheless, we may simplify for the purposes of staying on
track with lenses:

case class CoState[A, B](put: A => B, up: A) {
 // A Comonad
 def coFlatMap[C](f: CoState[A, B] => C): CoState[A, C] =
   CoState(a => f(CoState(put, a)), up)
}

A lens is now a little simpler (specialised):

case class Lens[T, R](k: T => CoState[R, T])

Now, we sometimes think of a "lens" as the pair of a get and set method.

* get: T => R // get the field on T of type R
* set: T => R => T // set the field on T of type R, returning new T

I encoded it earlier with a representation using CoState to take
advantage of this particular data type and its library (it is very
rich). We can still construct a Lens using get/set pairs quite easily:

object Lens {
 def lens[T, R](
   get: T => R
 , set: T => R => T
 ): Lens[T, R] =
   Lens(t =>
     CoState[R, T](set(t), get(t)))
}

We can also implement get and set methods on Lens easily (omitted for
brevity).

Now one of the important, killer useful properties of lenses is that
*they compose*. What does this mean? It means the following function
exists (also with an identity Lens[X, X]):

def compose[A, B, C](f: Lens[A, B], g: Lens[B, C]): Lens[A, C]

Let's write it as a method to demonstrate it:

case class Lens[T, R](k: T => CoState[R, T]) {
 def compose[Q](that: Lens[R, Q]): Lens[T, Q] =
   Lens[T, Q](t => {
     val CoState(f, a) = k(t)
     val CoState(g, b) = that.k(a)
     CoState(f compose g, b)
   })
}

To state it in English, "if we have a lens from T to R, and a lens
from R to Q, then we have a lens from T to Q." To provide an example:
if our Building has a (lens) Window and a Window has a (lens)
Perimeter, then our Building has a (lens) Window. This would allow us
to "get and set" the Window property on the Building class, thus,
answering the often-encountered questions regarding traversing down
object graphs and getting all messy. I digress, but hopefully, this
conveys an important point.

Further, this Lens class deserves to have all sorts of combinators on
it for manipulating record fields, but I have omitted them for brevity
Tony Morris 2
Joined: 2009-03-20,
User offline. Last seen 42 years 45 weeks ago.
Re: Compiler-generated lenses for record fields

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

How would you have it look such that you overcome a problem that is
not currently catered for?

On 25/05/11 22:34, Josh Suereth wrote:
> +1 on a lenses library with a simple interface. Most of the time
> I would need lenses though, it's in a different context. One of
> interfacing with other teams who've defined the same data
> abstractions with slightly differing values and I want to use
> lenses to convert between the two. Nothing in your API prevents
> that, but the bit about compiler magic is where we may start
> disagreeing on what it should look like :)
>
> - Josh
>
> On Wed, May 25, 2011 at 8:08 AM, Tony Morris tonymorris [at] gmail [dot] com (<tonymorris [at] gmail [dot] com>)
> wrote:
>
>>
> OK, so we recently got the copy method for updating fields of case
> classes.
>
> However, what about lenses in a more general and composable form?
> It is possible to implement as a library, right up to the point
> where it would require you to implement the lens for each record (I
> use the term "record" so as not to be necessarily specific to case
> classes) field.
>
> Let us first define a lens in library code.
>
> The Identity functor is trivial to get us started:
>
> case class Identity[A](a: A) { def map[B](f: A => B): Identity[A] =
> sys.error("todo") def flatMap[B](f: A => Identity[B]): Identity[B]
> = sys.error("todo") }
>
> Next we define costate (the categorical dual to state) as a
> comonad transformer:
>
> // A Comonad for any Comonad[F] (todo) case class CoStateT[A, F[_],
> B](put: F[A => B], set: A)
>
> Next we are able to define a Lens:
>
> case class Lens[T, R](k: T => CoStateT[R, Identity, T])
>
> Now, I have been a bit elaborate here with the comonad transformer
> then specialising to Identity. I did this because there is a huge
> and useful library behind CoStateT, which benefits by being in
> transformer version. Nevertheless, we may simplify for the purposes
> of staying on track with lenses:
>
> case class CoState[A, B](put: A => B, up: A) { // A Comonad def
> coFlatMap[C](f: CoState[A, B] => C): CoState[A, C] = CoState(a =>
> f(CoState(put, a)), up) }
>
> A lens is now a little simpler (specialised):
>
> case class Lens[T, R](k: T => CoState[R, T])
>
> Now, we sometimes think of a "lens" as the pair of a get and set
> method.
>
> * get: T => R // get the field on T of type R * set: T => R => T //
> set the field on T of type R, returning new T
>
> I encoded it earlier with a representation using CoState to take
> advantage of this particular data type and its library (it is very
> rich). We can still construct a Lens using get/set pairs quite
> easily:
>
> object Lens { def lens[T, R]( get: T => R , set: T => R => T ):
> Lens[T, R] = Lens(t => CoState[R, T](set(t), get(t))) }
>
> We can also implement get and set methods on Lens easily (omitted
> for brevity).
>
> Now one of the important, killer useful properties of lenses is
> that *they compose*. What does this mean? It means the following
> function exists (also with an identity Lens[X, X]):
>
> def compose[A, B, C](f: Lens[A, B], g: Lens[B, C]): Lens[A, C]
>
> Let's write it as a method to demonstrate it:
>
> case class Lens[T, R](k: T => CoState[R, T]) { def compose[Q](that:
> Lens[R, Q]): Lens[T, Q] = Lens[T, Q](t => { val CoState(f, a) =
> k(t) val CoState(g, b) = that.k(a) CoState(f compose g, b) }) }
>
> To state it in English, "if we have a lens from T to R, and a lens
> from R to Q, then we have a lens from T to Q." To provide an
> example: if our Building has a (lens) Window and a Window has a
> (lens) Perimeter, then our Building has a (lens) Window. This would
> allow us to "get and set" the Window property on the Building
> class, thus, answering the often-encountered questions regarding
> traversing down object graphs and getting all messy. I digress, but
> hopefully, this conveys an important point.
>
> Further, this Lens class deserves to have all sorts of combinators
> on it for manipulating record fields, but I have omitted them for
> brevity -- hopefully you can imagine the sorts of things that might
> appear. If not, I'll follow-up with some examples.
>
> So we are all fine and dandy at this point, with a nice useful
> library. Looks promising! Let's try to put it into practice.
>
> Suppose we have:
>
> case class Person(name: String, age: Int, friend: Person)
>
> We now want:
>
> * lname: Lens[Person, String] * lage: Lens[Person, Int] * lfriend:
> Lens[Person, Person] // these compose in any Traversable!
>
> However, this is tedious and full of boilerplate:
>
> val lname = Lens.lens(_.name, p => n => p.copy(name = n)) val lage
> = Lens.lens(_.age, p => a => p.copy(age = a)) val lfriend =
> Lens.lens(_.friend p => f => p.copy(friend = f))
>
> eek! We have required effort proportional to the number of fields
> in our record type. This can't be fun.
>
> So, now I can get to the question: Wouldn't it be useful to: a)
> develop a library around these data structures? b) have the
> compiler generate the latter boilerplate code?
>
> I haven't thought too far ahead to implementation, or pondered on
> issues of generating bytecode for each record field, however, I am
> simply tempted by just how useful this particular feature could be.
> It even has that "attraction" to Java programmers coming on over to
> Scala and wondering how we deal with this sort of thing -- can we,
> y'know, kick some serious arse!? Just askin'
>
> Comments? Thanks for listening.
>
>
>>
>>

- --
Tony Morris
http://tmorris.net/

-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.10 (GNU/Linux)
Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org/

iEYEARECAAYFAk3c+ZAACgkQmnpgrYe6r61fywCfbgLELh1KUD8tKMxbHyMwXuUv
4G0AoJIT5oO8O3WZZoV9rdnmQfJF9bfc
=ij7U
-----END PGP SIGNATURE-----

Robert Wills
Joined: 2009-03-04,
User offline. Last seen 32 weeks 4 days ago.
Re: Compiler-generated lenses for record fields
Is there any other way to get rid of the boilerplate than with compiler magic?
According to thishttp://stackoverflow.com/questions/3900307/cleaner-way-to-update-nested-structures http://www.scala-lang.org/node/6622
there was once a prototype compiler extension for automatically generating the boilerplate associated with zippers:
@zip case class Pacman(lives: Int = 3, superMode: Boolean = false) 
Presumably something similar could be done with lenses.
Judging by the conversation on scala-user yesterday about producing compiler plugins I guess this would be quite difficult to do as a plugin.
-Rob
On Wed, May 25, 2011 at 1:34 PM, Josh Suereth <joshua [dot] suereth [at] gmail [dot] com> wrote:
+1 on a lenses library with a simple interface.   Most of the time I would need lenses though, it's in a different context.  One of interfacing with other teams who've defined the same data abstractions with slightly differing values and I want to use lenses to convert between the two.   Nothing in your API prevents that, but the bit about compiler magic is where we may start disagreeing on what it should look like :)
- Josh

On Wed, May 25, 2011 at 8:08 AM, Tony Morris <tonymorris [at] gmail [dot] com> wrote:

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

OK, so we recently got the copy method for updating fields of case
classes.

However, what about lenses in a more general and composable form? It
is possible to implement as a library, right up to the point where it
would require you to implement the lens for each record (I use the
term "record" so as not to be necessarily specific to case classes) field.

Let us first define a lens in library code.

The Identity functor is trivial to get us started:

case class Identity[A](a: A) {
 def map[B](f: A => B): Identity[A] = sys.error("todo")
 def flatMap[B](f: A => Identity[B]): Identity[B] = sys.error("todo")
}

Next we define costate (the categorical dual to state) as a comonad
transformer:

// A Comonad for any Comonad[F] (todo)
case class CoStateT[A, F[_], B](put: F[A => B], set: A)

Next we are able to define a Lens:

case class Lens[T, R](k: T => CoStateT[R, Identity, T])

Now, I have been a bit elaborate here with the comonad transformer
then specialising to Identity. I did this because there is a huge and
useful library behind CoStateT, which benefits by being in transformer
version. Nevertheless, we may simplify for the purposes of staying on
track with lenses:

case class CoState[A, B](put: A => B, up: A) {
 // A Comonad
 def coFlatMap[C](f: CoState[A, B] => C): CoState[A, C] =
   CoState(a => f(CoState(put, a)), up)
}

A lens is now a little simpler (specialised):

case class Lens[T, R](k: T => CoState[R, T])

Now, we sometimes think of a "lens" as the pair of a get and set method.

* get: T => R // get the field on T of type R
* set: T => R => T // set the field on T of type R, returning new T

I encoded it earlier with a representation using CoState to take
advantage of this particular data type and its library (it is very
rich). We can still construct a Lens using get/set pairs quite easily:

object Lens {
 def lens[T, R](
   get: T => R
 , set: T => R => T
 ): Lens[T, R] =
   Lens(t =>
     CoState[R, T](set(t), get(t)))
}

We can also implement get and set methods on Lens easily (omitted for
brevity).

Now one of the important, killer useful properties of lenses is that
*they compose*. What does this mean? It means the following function
exists (also with an identity Lens[X, X]):

def compose[A, B, C](f: Lens[A, B], g: Lens[B, C]): Lens[A, C]

Let's write it as a method to demonstrate it:

case class Lens[T, R](k: T => CoState[R, T]) {
 def compose[Q](that: Lens[R, Q]): Lens[T, Q] =
   Lens[T, Q](t => {
     val CoState(f, a) = k(t)
     val CoState(g, b) = that.k(a)
     CoState(f compose g, b)
   })
}

To state it in English, "if we have a lens from T to R, and a lens
from R to Q, then we have a lens from T to Q." To provide an example:
if our Building has a (lens) Window and a Window has a (lens)
Perimeter, then our Building has a (lens) Window. This would allow us
to "get and set" the Window property on the Building class, thus,
answering the often-encountered questions regarding traversing down
object graphs and getting all messy. I digress, but hopefully, this
conveys an important point.

Further, this Lens class deserves to have all sorts of combinators on
it for manipulating record fields, but I have omitted them for brevity
Tony Morris 2
Joined: 2009-03-20,
User offline. Last seen 42 years 45 weeks ago.
Re: Compiler-generated lenses for record fields

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

On 25/05/11 22:08, Tony Morris wrote:
> To provide an example: if our Building has a (lens) Window and a
> Window has a (lens) Perimeter, then our Building has a (lens)
> Window. This would allow us to "get and set" the Window property on
> the Building class, thus

Correction:

To provide an example:
if our Building has a (lens) Window and a Window has a (lens)
Perimeter, then our Building has a (lens) Permiter. This would allow us
to "get and set" the Permiter property on the Building class, thus,

Joshua.Suereth
Joined: 2008-09-02,
User offline. Last seen 32 weeks 5 days ago.
Re: Compiler-generated lenses for record fields
In my case I have type project1.Building and type project2.Building.   I want to define a lens from project1.Building => project2.Building so I can adapt incoming data from project1 for project2's use case.   This involves manipulating *all* the fields, not just a single one, so the boilerplate is larger.  The base abstraction is correct, but I think the boilerplate may be slightly different.   I'd just like to account for that if there were any sort of compiler magic going on.
- Josh

On Wed, May 25, 2011 at 8:44 AM, Tony Morris <tonymorris [at] gmail [dot] com> wrote:

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

How would you have it look such that you overcome a problem that is
not currently catered for?

On 25/05/11 22:34, Josh Suereth wrote:
> +1 on a lenses library with a simple interface. Most of the time
> I would need lenses though, it's in a different context. One of
> interfacing with other teams who've defined the same data
> abstractions with slightly differing values and I want to use
> lenses to convert between the two. Nothing in your API prevents
> that, but the bit about compiler magic is where we may start
> disagreeing on what it should look like :)
>
> - Josh
>
> On Wed, May 25, 2011 at 8:08 AM, Tony Morris tonymorris [at] gmail [dot] com (<tonymorris [at] gmail [dot] com>)
> wrote:
>
>>
> OK, so we recently got the copy method for updating fields of case
> classes.
>
> However, what about lenses in a more general and composable form?
> It is possible to implement as a library, right up to the point
> where it would require you to implement the lens for each record (I
> use the term "record" so as not to be necessarily specific to case
> classes) field.
>
> Let us first define a lens in library code.
>
> The Identity functor is trivial to get us started:
>
> case class Identity[A](a: A) { def map[B](f: A => B): Identity[A] =
> sys.error("todo") def flatMap[B](f: A => Identity[B]): Identity[B]
> = sys.error("todo") }
>
> Next we define costate (the categorical dual to state) as a
> comonad transformer:
>
> // A Comonad for any Comonad[F] (todo) case class CoStateT[A, F[_],
> B](put: F[A => B], set: A)
>
> Next we are able to define a Lens:
>
> case class Lens[T, R](k: T => CoStateT[R, Identity, T])
>
> Now, I have been a bit elaborate here with the comonad transformer
> then specialising to Identity. I did this because there is a huge
> and useful library behind CoStateT, which benefits by being in
> transformer version. Nevertheless, we may simplify for the purposes
> of staying on track with lenses:
>
> case class CoState[A, B](put: A => B, up: A) { // A Comonad def
> coFlatMap[C](f: CoState[A, B] => C): CoState[A, C] = CoState(a =>
> f(CoState(put, a)), up) }
>
> A lens is now a little simpler (specialised):
>
> case class Lens[T, R](k: T => CoState[R, T])
>
> Now, we sometimes think of a "lens" as the pair of a get and set
> method.
>
> * get: T => R // get the field on T of type R * set: T => R => T //
> set the field on T of type R, returning new T
>
> I encoded it earlier with a representation using CoState to take
> advantage of this particular data type and its library (it is very
> rich). We can still construct a Lens using get/set pairs quite
> easily:
>
> object Lens { def lens[T, R]( get: T => R , set: T => R => T ):
> Lens[T, R] = Lens(t => CoState[R, T](set(t), get(t))) }
>
> We can also implement get and set methods on Lens easily (omitted
> for brevity).
>
> Now one of the important, killer useful properties of lenses is
> that *they compose*. What does this mean? It means the following
> function exists (also with an identity Lens[X, X]):
>
> def compose[A, B, C](f: Lens[A, B], g: Lens[B, C]): Lens[A, C]
>
> Let's write it as a method to demonstrate it:
>
> case class Lens[T, R](k: T => CoState[R, T]) { def compose[Q](that:
> Lens[R, Q]): Lens[T, Q] = Lens[T, Q](t => { val CoState(f, a) =
> k(t) val CoState(g, b) = that.k(a) CoState(f compose g, b) }) }
>
> To state it in English, "if we have a lens from T to R, and a lens
> from R to Q, then we have a lens from T to Q." To provide an
> example: if our Building has a (lens) Window and a Window has a
> (lens) Perimeter, then our Building has a (lens) Window. This would
> allow us to "get and set" the Window property on the Building
> class, thus, answering the often-encountered questions regarding
> traversing down object graphs and getting all messy. I digress, but
> hopefully, this conveys an important point.
>
> Further, this Lens class deserves to have all sorts of combinators
> on it for manipulating record fields, but I have omitted them for
> brevity -- hopefully you can imagine the sorts of things that might
> appear. If not, I'll follow-up with some examples.
>
> So we are all fine and dandy at this point, with a nice useful
> library. Looks promising! Let's try to put it into practice.
>
> Suppose we have:
>
> case class Person(name: String, age: Int, friend: Person)
>
> We now want:
>
> * lname: Lens[Person, String] * lage: Lens[Person, Int] * lfriend:
> Lens[Person, Person] // these compose in any Traversable!
>
> However, this is tedious and full of boilerplate:
>
> val lname = Lens.lens(_.name, p => n => p.copy(name = n)) val lage
> = Lens.lens(_.age, p => a => p.copy(age = a)) val lfriend =
> Lens.lens(_.friend p => f => p.copy(friend = f))
>
> eek! We have required effort proportional to the number of fields
> in our record type. This can't be fun.
>
> So, now I can get to the question: Wouldn't it be useful to: a)
> develop a library around these data structures? b) have the
> compiler generate the latter boilerplate code?
>
> I haven't thought too far ahead to implementation, or pondered on
> issues of generating bytecode for each record field, however, I am
> simply tempted by just how useful this particular feature could be.
> It even has that "attraction" to Java programmers coming on over to
> Scala and wondering how we deal with this sort of thing -- can we,
> y'know, kick some serious arse!? Just askin'
>
> Comments? Thanks for listening.
>
>
>>
>>

- --
Tony Morris
http://tmorris.net/

-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.10 (GNU/Linux)
Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org/

iEYEARECAAYFAk3c+ZAACgkQmnpgrYe6r61fywCfbgLELh1KUD8tKMxbHyMwXuUv
4G0AoJIT5oO8O3WZZoV9rdnmQfJF9bfc
=ij7U
-----END PGP SIGNATURE-----


dcsobral
Joined: 2009-04-23,
User offline. Last seen 38 weeks 5 days ago.
Re: Compiler-generated lenses for record fields

I wonder how to build the compiler support for the lens without loss
of generality. One solution I can think of is this:

case class C(field1: T, field2: R)

// This gets auto-generated
object C {
object lens {
def field1: Tuple2[C => T, C => T => C] = ((_: C).field1,
(that: C) => (value: T) => that.copy(field1 = value))
def field2: Tuple2[C => R, C => R => C] = ((_: C).field2,
(that: C) => (value: R) => that.copy(field2 = value))
}
}

It could then be used as basis to provide any lens implementation:

object Lens {
implicit def lens[T, R](l: (T => R, T => R => T)): Lens[T, R] =
Lens(t =>
CoState[R, T](l._2(t), l._1(t)))
}

val lname: Lens[Person, String] = Person.lens.name

On Wed, May 25, 2011 at 09:08, Tony Morris wrote:
>
> -----BEGIN PGP SIGNED MESSAGE-----
> Hash: SHA1
>
> OK, so we recently got the copy method for updating fields of case
> classes.
>
> However, what about lenses in a more general and composable form? It
> is possible to implement as a library, right up to the point where it
> would require you to implement the lens for each record (I use the
> term "record" so as not to be necessarily specific to case classes) field.
>
> Let us first define a lens in library code.
>
> The Identity functor is trivial to get us started:
>
> case class Identity[A](a: A) {
>  def map[B](f: A => B): Identity[A] = sys.error("todo")
>  def flatMap[B](f: A => Identity[B]): Identity[B] = sys.error("todo")
> }
>
> Next we define costate (the categorical dual to state) as a comonad
> transformer:
>
> // A Comonad for any Comonad[F] (todo)
> case class CoStateT[A, F[_], B](put: F[A => B], set: A)
>
> Next we are able to define a Lens:
>
> case class Lens[T, R](k: T => CoStateT[R, Identity, T])
>
> Now, I have been a bit elaborate here with the comonad transformer
> then specialising to Identity. I did this because there is a huge and
> useful library behind CoStateT, which benefits by being in transformer
> version. Nevertheless, we may simplify for the purposes of staying on
> track with lenses:
>
> case class CoState[A, B](put: A => B, up: A) {
>  // A Comonad
>  def coFlatMap[C](f: CoState[A, B] => C): CoState[A, C] =
>    CoState(a => f(CoState(put, a)), up)
> }
>
> A lens is now a little simpler (specialised):
>
> case class Lens[T, R](k: T => CoState[R, T])
>
> Now, we sometimes think of a "lens" as the pair of a get and set method.
>
> * get: T => R // get the field on T of type R
> * set: T => R => T // set the field on T of type R, returning new T
>
> I encoded it earlier with a representation using CoState to take
> advantage of this particular data type and its library (it is very
> rich). We can still construct a Lens using get/set pairs quite easily:
>
> object Lens {
>  def lens[T, R](
>    get: T => R
>  , set: T => R => T
>  ): Lens[T, R] =
>    Lens(t =>
>      CoState[R, T](set(t), get(t)))
> }
>
> We can also implement get and set methods on Lens easily (omitted for
> brevity).
>
> Now one of the important, killer useful properties of lenses is that
> *they compose*. What does this mean? It means the following function
> exists (also with an identity Lens[X, X]):
>
> def compose[A, B, C](f: Lens[A, B], g: Lens[B, C]): Lens[A, C]
>
> Let's write it as a method to demonstrate it:
>
> case class Lens[T, R](k: T => CoState[R, T]) {
>  def compose[Q](that: Lens[R, Q]): Lens[T, Q] =
>    Lens[T, Q](t => {
>      val CoState(f, a) = k(t)
>      val CoState(g, b) = that.k(a)
>      CoState(f compose g, b)
>    })
> }
>
> To state it in English, "if we have a lens from T to R, and a lens
> from R to Q, then we have a lens from T to Q." To provide an example:
> if our Building has a (lens) Window and a Window has a (lens)
> Perimeter, then our Building has a (lens) Window. This would allow us
> to "get and set" the Window property on the Building class, thus,
> answering the often-encountered questions regarding traversing down
> object graphs and getting all messy. I digress, but hopefully, this
> conveys an important point.
>
> Further, this Lens class deserves to have all sorts of combinators on
> it for manipulating record fields, but I have omitted them for brevity
> - -- hopefully you can imagine the sorts of things that might appear. If
> not, I'll follow-up with some examples.
>
> So we are all fine and dandy at this point, with a nice useful
> library. Looks promising! Let's try to put it into practice.
>
> Suppose we have:
>
> case class Person(name: String, age: Int, friend: Person)
>
> We now want:
>
> * lname: Lens[Person, String]
> * lage: Lens[Person, Int]
> * lfriend: Lens[Person, Person] // these compose in any Traversable!
>
> However, this is tedious and full of boilerplate:
>
> val lname = Lens.lens(_.name, p => n => p.copy(name = n))
> val lage = Lens.lens(_.age, p => a => p.copy(age = a))
> val lfriend = Lens.lens(_.friend p => f => p.copy(friend = f))
>
> eek! We have required effort proportional to the number of fields in
> our record type. This can't be fun.
>
> So, now I can get to the question: Wouldn't it be useful to:
> a) develop a library around these data structures?
> b) have the compiler generate the latter boilerplate code?
>
> I haven't thought too far ahead to implementation, or pondered on
> issues of generating bytecode for each record field, however, I am
> simply tempted by just how useful this particular feature could be. It
> even has that "attraction" to Java programmers coming on over to Scala
> and wondering how we deal with this sort of thing -- can we, y'know,
> kick some serious arse!? Just askin'
>
> Comments? Thanks for listening.
>
>
> - --
> Tony Morris
> http://tmorris.net/
>
> -----BEGIN PGP SIGNATURE-----
> Version: GnuPG v1.4.10 (GNU/Linux)
> Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org/
>
> iEYEARECAAYFAk3c8UEACgkQmnpgrYe6r615SACgvbmIRGKgVhjrP6sWKfFfdiVg
> MZMAoLNDD9IscdX2vE7huBLMkIkiU6WF
> =lhww
> -----END PGP SIGNATURE-----
>
>

Naftoli Gugenheim
Joined: 2008-12-17,
User offline. Last seen 42 years 45 weeks ago.
Re: Compiler-generated lenses for record fields


On Wed, May 25, 2011 at 8:08 AM, Tony Morris <tonymorris [at] gmail [dot] com> wrote:
However, this is tedious and full of boilerplate:

val lname = Lens.lens(_.name, p => n => p.copy(name = n))
val lage = Lens.lens(_.age, p => a => p.copy(age = a))
val lfriend = Lens.lens(_.friend p => f => p.copy(friend = f))

eek! We have required effort proportional to the number of fields in
our record type. This can't be fun.

If you're targeting case classes, without making the effort less than proportional to the number of fields, perhaps we can still make it less per field. Using scala.reflect.Code, maybe you could get the AST for _.name and infer the p=>n=>p.copy(name = n) part.


Tony Morris 2
Joined: 2009-03-20,
User offline. Last seen 42 years 45 weeks ago.
Re: Compiler-generated lenses for record fields
In a similar spirit.

Edward Kmett: Lenses: A Functional Imperative
Presented @ Boston Area Scala Enthusiasts (BASE) 24 May 2011

http://www.youtube.com/playlist?p=PLEDE5BE0C69AF6CCE


On 25/05/11 22:08, Tony Morris wrote:
>
OK, so we recently got the copy method for updating fields of case
classes.

However, what about lenses in a more general and composable form? It
is possible to implement as a library, right up to the point where it
would require you to implement the lens for each record (I use the
term "record" so as not to be necessarily specific to case classes) field.

Let us first define a lens in library code.

The Identity functor is trivial to get us started:

case class Identity[A](a: A) {
  def map[B](f: A => B): Identity[A] = sys.error("todo")
  def flatMap[B](f: A => Identity[B]): Identity[B] = sys.error("todo")
}

Next we define costate (the categorical dual to state) as a comonad
transformer:

// A Comonad for any Comonad[F] (todo)
case class CoStateT[A, F[_], B](put: F[A => B], set: A)

Next we are able to define a Lens:

case class Lens[T, R](k: T => CoStateT[R, Identity, T])

Now, I have been a bit elaborate here with the comonad transformer
then specialising to Identity. I did this because there is a huge and
useful library behind CoStateT, which benefits by being in transformer
version. Nevertheless, we may simplify for the purposes of staying on
track with lenses:

case class CoState[A, B](put: A => B, up: A) {
  // A Comonad
  def coFlatMap[C](f: CoState[A, B] => C): CoState[A, C] =
    CoState(a => f(CoState(put, a)), up)
}

A lens is now a little simpler (specialised):

case class Lens[T, R](k: T => CoState[R, T])

Now, we sometimes think of a "lens" as the pair of a get and set method.

* get: T => R // get the field on T of type R
* set: T => R => T // set the field on T of type R, returning new T

I encoded it earlier with a representation using CoState to take
advantage of this particular data type and its library (it is very
rich). We can still construct a Lens using get/set pairs quite easily:

object Lens {
  def lens[T, R](
    get: T => R
  , set: T => R => T
  ): Lens[T, R] =
    Lens(t =>
      CoState[R, T](set(t), get(t)))
}

We can also implement get and set methods on Lens easily (omitted for
brevity).

Now one of the important, killer useful properties of lenses is that
*they compose*. What does this mean? It means the following function
exists (also with an identity Lens[X, X]):

def compose[A, B, C](f: Lens[A, B], g: Lens[B, C]): Lens[A, C]

Let's write it as a method to demonstrate it:

case class Lens[T, R](k: T => CoState[R, T]) {
  def compose[Q](that: Lens[R, Q]): Lens[T, Q] =
    Lens[T, Q](t => {
      val CoState(f, a) = k(t)
      val CoState(g, b) = that.k(a)
      CoState(f compose g, b)
    })
}

To state it in English, "if we have a lens from T to R, and a lens
from R to Q, then we have a lens from T to Q." To provide an example:
if our Building has a (lens) Window and a Window has a (lens)
Perimeter, then our Building has a (lens) Window. This would allow us
to "get and set" the Window property on the Building class, thus,
answering the often-encountered questions regarding traversing down
object graphs and getting all messy. I digress, but hopefully, this
conveys an important point.

Further, this Lens class deserves to have all sorts of combinators on
it for manipulating record fields, but I have omitted them for brevity
-- hopefully you can imagine the sorts of things that might appear. If
not, I'll follow-up with some examples.

So we are all fine and dandy at this point, with a nice useful
library. Looks promising! Let's try to put it into practice.

Suppose we have:

case class Person(name: String, age: Int, friend: Person)

We now want:

* lname: Lens[Person, String]
* lage: Lens[Person, Int]
* lfriend: Lens[Person, Person] // these compose in any Traversable!

However, this is tedious and full of boilerplate:

val lname = Lens.lens(_.name, p => n => p.copy(name = n))
val lage = Lens.lens(_.age, p => a => p.copy(age = a))
val lfriend = Lens.lens(_.friend p => f => p.copy(friend = f))

eek! We have required effort proportional to the number of fields in
our record type. This can't be fun.

So, now I can get to the question: Wouldn't it be useful to:
a) develop a library around these data structures?
b) have the compiler generate the latter boilerplate code?

I haven't thought too far ahead to implementation, or pondered on
issues of generating bytecode for each record field, however, I am
simply tempted by just how useful this particular feature could be. It
even has that "attraction" to Java programmers coming on over to Scala
and wondering how we deal with this sort of thing -- can we, y'know,
kick some serious arse!? Just askin'

Comments? Thanks for listening.



--
Tony Morris
http://tmorris.net/


phlegmaticprogrammer
Joined: 2010-07-23,
User offline. Last seen 2 years 15 weeks ago.
Re: Re: Compiler-generated lenses for record fields
Thank you for posting this talk! I have seen "Lenses" floating around and mentioned often lately, but had not yet looked up what they actually are; now I know. 
Turns out, I might have great use for lenses in my language Babel-17 (www.babel-17.com); I think Babel-17 should support this natively. You can already do 
val x = {a = 2, b = 3}x.b = 7
in Babel-17; here x.b = 7 is just short for 
val x = {a = x.a, b = 7}
So it is basically what copy for case classes does in Scala, but more tightly integrated into the language. I always had in the back of my mind that I would like to generalize this shortcut update notation somehow, and lenses seem to be the perfect tool to do it as they seem to mix great with my concept of linear scope, just like they were made for each other.
There is a new version of Babel-17 coming out in the next 5 to 10 days, v0.3. Lenses will probably not make it into this release, but expect them in v0.3.1 .
Ah, two comments about the talk: I liked it a lot, but I noticed two things:
a) there is nothing wrong with exceptions in a purely functional language, they just need to be done right, like in Babel-17b) there was a lot of typing involved in explaining what lenses are; I have no problems with this; but I can also see how lenses can be explained in a nice way to people and used by people without any static types getting in the way
- Steven


On 27.05.2011, at 08:46, Tony Morris wrote:
In a similar spirit.

Edward Kmett: Lenses: A Functional Imperative
Presented @ Boston Area Scala Enthusiasts (BASE) 24 May 2011

http://www.youtube.com/playlist?p=PLEDE5BE0C69AF6CCE


On 25/05/11 22:08, Tony Morris wrote:
>
OK, so we recently got the copy method for updating fields of case
classes.

However, what about lenses in a more general and composable form? It
is possible to implement as a library, right up to the point where it
would require you to implement the lens for each record (I use the
term "record" so as not to be necessarily specific to case classes) field.

Let us first define a lens in library code.

The Identity functor is trivial to get us started:

case class Identity[A](a: A) {
  def map[B](f: A => B): Identity[A] = sys.error("todo")
  def flatMap[B](f: A => Identity[B]): Identity[B] = sys.error("todo")
}

Next we define costate (the categorical dual to state) as a comonad
transformer:

// A Comonad for any Comonad[F] (todo)
case class CoStateT[A, F[_], B](put: F[A => B], set: A)

Next we are able to define a Lens:

case class Lens[T, R](k: T => CoStateT[R, Identity, T])

Now, I have been a bit elaborate here with the comonad transformer
then specialising to Identity. I did this because there is a huge and
useful library behind CoStateT, which benefits by being in transformer
version. Nevertheless, we may simplify for the purposes of staying on
track with lenses:

case class CoState[A, B](put: A => B, up: A) {
  // A Comonad
  def coFlatMap[C](f: CoState[A, B] => C): CoState[A, C] =
    CoState(a => f(CoState(put, a)), up)
}

A lens is now a little simpler (specialised):

case class Lens[T, R](k: T => CoState[R, T])

Now, we sometimes think of a "lens" as the pair of a get and set method.

* get: T => R // get the field on T of type R
* set: T => R => T // set the field on T of type R, returning new T

I encoded it earlier with a representation using CoState to take
advantage of this particular data type and its library (it is very
rich). We can still construct a Lens using get/set pairs quite easily:

object Lens {
  def lens[T, R](
    get: T => R
  , set: T => R => T
  ): Lens[T, R] =
    Lens(t =>
      CoState[R, T](set(t), get(t)))
}

We can also implement get and set methods on Lens easily (omitted for
brevity).

Now one of the important, killer useful properties of lenses is that
*they compose*. What does this mean? It means the following function
exists (also with an identity Lens[X, X]):

def compose[A, B, C](f: Lens[A, B], g: Lens[B, C]): Lens[A, C]

Let's write it as a method to demonstrate it:

case class Lens[T, R](k: T => CoState[R, T]) {
  def compose[Q](that: Lens[R, Q]): Lens[T, Q] =
    Lens[T, Q](t => {
      val CoState(f, a) = k(t)
      val CoState(g, b) = that.k(a)
      CoState(f compose g, b)
    })
}

To state it in English, "if we have a lens from T to R, and a lens
from R to Q, then we have a lens from T to Q." To provide an example:
if our Building has a (lens) Window and a Window has a (lens)
Perimeter, then our Building has a (lens) Window. This would allow us
to "get and set" the Window property on the Building class, thus,
answering the often-encountered questions regarding traversing down
object graphs and getting all messy. I digress, but hopefully, this
conveys an important point.

Further, this Lens class deserves to have all sorts of combinators on
it for manipulating record fields, but I have omitted them for brevity
-- hopefully you can imagine the sorts of things that might appear. If
not, I'll follow-up with some examples.

So we are all fine and dandy at this point, with a nice useful
library. Looks promising! Let's try to put it into practice.

Suppose we have:

case class Person(name: String, age: Int, friend: Person)

We now want:

* lname: Lens[Person, String]
* lage: Lens[Person, Int]
* lfriend: Lens[Person, Person] // these compose in any Traversable!

However, this is tedious and full of boilerplate:

val lname = Lens.lens(_.name, p => n => p.copy(name = n))
val lage = Lens.lens(_.age, p => a => p.copy(age = a))
val lfriend = Lens.lens(_.friend p => f => p.copy(friend = f))

eek! We have required effort proportional to the number of fields in
our record type. This can't be fun.

So, now I can get to the question: Wouldn't it be useful to:
a) develop a library around these data structures?
b) have the compiler generate the latter boilerplate code?

I haven't thought too far ahead to implementation, or pondered on
issues of generating bytecode for each record field, however, I am
simply tempted by just how useful this particular feature could be. It
even has that "attraction" to Java programmers coming on over to Scala
and wondering how we deal with this sort of thing -- can we, y'know,
kick some serious arse!? Just askin'

Comments? Thanks for listening.



--
Tony Morris
http://tmorris.net/



Tony Morris 2
Joined: 2009-03-20,
User offline. Last seen 42 years 45 weeks ago.
Re: Compiler-generated lenses for record fields

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

Am I to assume the silence is a result of distaste for the idea, or
more that one would prefer to see the patch before taking it
seriously? (or something else)


On 25/05/11 22:08, Tony Morris wrote:
>
> OK, so we recently got the copy method for updating fields of case
> classes.
>
> However, what about lenses in a more general and composable form?
> It is possible to implement as a library, right up to the point
> where it would require you to implement the lens for each record (I
> use the term "record" so as not to be necessarily specific to case
> classes) field.
>
> Let us first define a lens in library code.
>
> The Identity functor is trivial to get us started:
>
> case class Identity[A](a: A) { def map[B](f: A => B): Identity[A] =
> sys.error("todo") def flatMap[B](f: A => Identity[B]): Identity[B]
> = sys.error("todo") }
>
> Next we define costate (the categorical dual to state) as a
> comonad transformer:
>
> // A Comonad for any Comonad[F] (todo) case class CoStateT[A, F[_],
> B](put: F[A => B], set: A)
>
> Next we are able to define a Lens:
>
> case class Lens[T, R](k: T => CoStateT[R, Identity, T])
>
> Now, I have been a bit elaborate here with the comonad transformer
> then specialising to Identity. I did this because there is a huge
> and useful library behind CoStateT, which benefits by being in
> transformer version. Nevertheless, we may simplify for the purposes
> of staying on track with lenses:
>
> case class CoState[A, B](put: A => B, up: A) { // A Comonad def
> coFlatMap[C](f: CoState[A, B] => C): CoState[A, C] = CoState(a =>
> f(CoState(put, a)), up) }
>
> A lens is now a little simpler (specialised):
>
> case class Lens[T, R](k: T => CoState[R, T])
>
> Now, we sometimes think of a "lens" as the pair of a get and set
> method.
>
> * get: T => R // get the field on T of type R * set: T => R => T //
> set the field on T of type R, returning new T
>
> I encoded it earlier with a representation using CoState to take
> advantage of this particular data type and its library (it is very
> rich). We can still construct a Lens using get/set pairs quite
> easily:
>
> object Lens { def lens[T, R]( get: T => R , set: T => R => T ):
> Lens[T, R] = Lens(t => CoState[R, T](set(t), get(t))) }
>
> We can also implement get and set methods on Lens easily (omitted
> for brevity).
>
> Now one of the important, killer useful properties of lenses is
> that *they compose*. What does this mean? It means the following
> function exists (also with an identity Lens[X, X]):
>
> def compose[A, B, C](f: Lens[A, B], g: Lens[B, C]): Lens[A, C]
>
> Let's write it as a method to demonstrate it:
>
> case class Lens[T, R](k: T => CoState[R, T]) { def compose[Q](that:
> Lens[R, Q]): Lens[T, Q] = Lens[T, Q](t => { val CoState(f, a) =
> k(t) val CoState(g, b) = that.k(a) CoState(f compose g, b) }) }
>
> To state it in English, "if we have a lens from T to R, and a lens
> from R to Q, then we have a lens from T to Q." To provide an
> example: if our Building has a (lens) Window and a Window has a
> (lens) Perimeter, then our Building has a (lens) Window. This would
> allow us to "get and set" the Window property on the Building
> class, thus, answering the often-encountered questions regarding
> traversing down object graphs and getting all messy. I digress, but
> hopefully, this conveys an important point.
>
> Further, this Lens class deserves to have all sorts of combinators
> on it for manipulating record fields, but I have omitted them for
> brevity -- hopefully you can imagine the sorts of things that might
> appear. If not, I'll follow-up with some examples.
>
> So we are all fine and dandy at this point, with a nice useful
> library. Looks promising! Let's try to put it into practice.
>
> Suppose we have:
>
> case class Person(name: String, age: Int, friend: Person)
>
> We now want:
>
> * lname: Lens[Person, String] * lage: Lens[Person, Int] * lfriend:
> Lens[Person, Person] // these compose in any Traversable!
>
> However, this is tedious and full of boilerplate:
>
> val lname = Lens.lens(_.name, p => n => p.copy(name = n)) val lage
> = Lens.lens(_.age, p => a => p.copy(age = a)) val lfriend =
> Lens.lens(_.friend p => f => p.copy(friend = f))
>
> eek! We have required effort proportional to the number of fields
> in our record type. This can't be fun.
>
> So, now I can get to the question: Wouldn't it be useful to: a)
> develop a library around these data structures? b) have the
> compiler generate the latter boilerplate code?
>
> I haven't thought too far ahead to implementation, or pondered on
> issues of generating bytecode for each record field, however, I am
> simply tempted by just how useful this particular feature could be.
> It even has that "attraction" to Java programmers coming on over to
> Scala and wondering how we deal with this sort of thing -- can we,
> y'know, kick some serious arse!? Just askin'
>
> Comments? Thanks for listening.
>
>

- --
Tony Morris
http://tmorris.net/

-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.10 (GNU/Linux)
Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org/

iEYEARECAAYFAk3hnIMACgkQmnpgrYe6r62Q6gCdGMnyzReb4Gu9c4W1/8VbqZ/O
JuEAoKNIUtxkfrpqQqFgUJ9bKQC8Cji3
=PIHN
-----END PGP SIGNATURE-----

Andreas Scheinert
Joined: 2011-02-11,
User offline. Last seen 42 years 45 weeks ago.
Re: Compiler-generated lenses for record fields

I hope the later one.

On 29 Mai, 03:08, Tony Morris wrote:
> -----BEGIN PGP SIGNED MESSAGE-----
> Hash: SHA1
>
> Am I to assume the silence is a result of distaste for the idea, or
> more that one would prefer to see the patch before taking it
> seriously? (or something else)
>
> On25/05/11 22:08, Tony Morris wrote:
>
>
>
>
>
>
>
> > OK, so we recently got the copy method for updating fields of case
> > classes.
>
> > However, what about lenses in a more general and composable form?
> > It is possible to implement as a library, right up to the point
> > where it would require you to implement the lens for each record (I
> > use the term "record" so as not to be necessarily specific to case
> > classes) field.
>
> > Let us first define a lens in library code.
>
> > The Identity functor is trivial to get us started:
>
> > case class Identity[A](a: A) { def map[B](f: A => B): Identity[A] =
> > sys.error("todo") def flatMap[B](f: A => Identity[B]): Identity[B]
> > = sys.error("todo") }
>
> > Next we define costate (the categorical dual to state) as a
> > comonad transformer:
>
> > // A Comonad for any Comonad[F] (todo) case class CoStateT[A, F[_],
> > B](put: F[A => B], set: A)
>
> > Next we are able to define a Lens:
>
> > case class Lens[T, R](k: T => CoStateT[R, Identity, T])
>
> > Now, I have been a bit elaborate here with the comonad transformer
> > then specialising to Identity. I did this because there is a huge
> > and useful library behind CoStateT, which benefits by being in
> > transformer version. Nevertheless, we may simplify for the purposes
> > of staying on track with lenses:
>
> > case class CoState[A, B](put: A => B, up: A) { // A Comonad def
> > coFlatMap[C](f: CoState[A, B] => C): CoState[A, C] = CoState(a =>
> > f(CoState(put, a)), up) }
>
> > A lens is now a little simpler (specialised):
>
> > case class Lens[T, R](k: T => CoState[R, T])
>
> > Now, we sometimes think of a "lens" as the pair of a get and set
> > method.
>
> > * get: T => R // get the field on T of type R * set: T => R => T //
> > set the field on T of type R, returning new T
>
> > I encoded it earlier with a representation using CoState to take
> > advantage of this particular data type and its library (it is very
> > rich). We can still construct a Lens using get/set pairs quite
> > easily:
>
> > object Lens { def lens[T, R]( get: T => R , set: T => R => T ):
> > Lens[T, R] = Lens(t => CoState[R, T](set(t), get(t))) }
>
> > We can also implement get and set methods on Lens easily (omitted
> > for brevity).
>
> > Now one of the important, killer useful properties of lenses is
> > that *they compose*. What does this mean? It means the following
> > function exists (also with an identity Lens[X, X]):
>
> > def compose[A, B, C](f: Lens[A, B], g: Lens[B, C]): Lens[A, C]
>
> > Let's write it as a method to demonstrate it:
>
> > case class Lens[T, R](k: T => CoState[R, T]) { def compose[Q](that:
> > Lens[R, Q]): Lens[T, Q] = Lens[T, Q](t => { val CoState(f, a) =
> > k(t) val CoState(g, b) = that.k(a) CoState(f compose g, b) }) }
>
> > To state it in English, "if we have a lens from T to R, and a lens
> > from R to Q, then we have a lens from T to Q." To provide an
> > example: if our Building has a (lens) Window and a Window has a
> > (lens) Perimeter, then our Building has a (lens) Window. This would
> > allow us to "get and set" the Window property on the Building
> > class, thus, answering the often-encountered questions regarding
> > traversing down object graphs and getting all messy. I digress, but
> > hopefully, this conveys an important point.
>
> > Further, this Lens class deserves to have all sorts of combinators
> > on it for manipulating record fields, but I have omitted them for
> > brevity -- hopefully you can imagine the sorts of things that might
> > appear. If not, I'll follow-up with some examples.
>
> > So we are all fine and dandy at this point, with a nice useful
> > library. Looks promising! Let's try to put it into practice.
>
> > Suppose we have:
>
> > case class Person(name: String, age: Int, friend: Person)
>
> > We now want:
>
> > * lname: Lens[Person, String] * lage: Lens[Person, Int] * lfriend:
> > Lens[Person, Person] // these compose in any Traversable!
>
> > However, this is tedious and full of boilerplate:
>
> > val lname = Lens.lens(_.name, p => n => p.copy(name = n)) val lage
> > = Lens.lens(_.age, p => a => p.copy(age = a)) val lfriend =
> > Lens.lens(_.friend p => f => p.copy(friend = f))
>
> > eek! We have required effort proportional to the number of fields
> > in our record type. This can't be fun.
>
> > So, now I can get to the question: Wouldn't it be useful to: a)
> > develop a library around these data structures? b) have the
> > compiler generate the latter boilerplate code?
>
> > I haven't thought too far ahead to implementation, or pondered on
> > issues of generating bytecode for each record field, however, I am
> > simply tempted by just how useful this particular feature could be.
> > It even has that "attraction" to Java programmers coming on over to
> > Scala and wondering how we deal with this sort of thing -- can we,
> > y'know, kick some serious arse!? Just askin'
>
> > Comments? Thanks for listening.
>
> - --
> Tony Morrishttp://tmorris.net/
>
> -----BEGIN PGP SIGNATURE-----
> Version: GnuPG v1.4.10 (GNU/Linux)
> Comment: Using GnuPG with Mozilla -http://enigmail.mozdev.org/
>
> iEYEARECAAYFAk3hnIMACgkQmnpgrYe6r62Q6gCdGMnyzReb4Gu9c4W1/8VbqZ/O
> JuEAoKNIUtxkfrpqQqFgUJ9bKQC8Cji3
> =PIHN
> -----END PGP SIGNATURE-----

Chris Marshall
Joined: 2009-06-17,
User offline. Last seen 44 weeks 3 days ago.
RE: Re: Compiler-generated lenses for record fields
Speaking purely for myself; before watching Edward Kmett's presentation on lenses, I had little to no idea what they were for, hence why I should care about a compiler plugin for them. My interest is now piqued and I certainly agree that a compiler plugin is critical to their use. Or at the very least a lens-code-generator.
Chris

Date: Sun, 29 May 2011 11:08:19 +1000
From: tonymorris [at] gmail [dot] com
To: scala-debate [at] googlegroups [dot] com
Subject: [scala-debate] Re: Compiler-generated lenses for record fields


-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

Am I to assume the silence is a result of distaste for the idea, or
more that one would prefer to see the patch before taking it
seriously? (or something else)


On 25/05/11 22:08, Tony Morris wrote:
>
> OK, so we recently got the copy method for updating fields of case
> classes.
>
> However, what about lenses in a more general and composable form?
> It is possible to implement as a library, right up to the point
> where it would require you to implement the lens for each record (I
> use the term "record" so as not to be necessarily specific to case
> classes) field.
>
> Let us first define a lens in library code.
>
> The Identity functor is trivial to get us started:
>
> case class Identity[A](a: A) { def map[B](f: A => B): Identity[A] =
> sys.error("todo") def flatMap[B](f: A => Identity[B]): Identity[B]
> = sys.error("todo") }
>
> Next we define costate (the categorical dual to state) as a
> comonad transformer:
>
> // A Comonad for any Comonad[F] (todo) case class CoStateT[A, F[_],
> B](put: F[A => B], set: A)
>
> Next we are able to define a Lens:
>
> case class Lens[T, R](k: T => CoStateT[R, Identity, T])
>
> Now, I have been a bit elaborate here with the comonad transformer
> then specialising to Identity. I did this because there is a huge
> and useful library behind CoStateT, which benefits by being in
> transformer version. Nevertheless, we may simplify for the purposes
> of staying on track with lenses:
>
> case class CoState[A, B](put: A => B, up: A) { // A Comonad def
> coFlatMap[C](f: CoState[A, B] => C): CoState[A, C] = CoState(a =>
> f(CoState(put, a)), up) }
>
> A lens is now a little simpler (specialised):
>
> case class Lens[T, R](k: T => CoState[R, T])
>
> Now, we sometimes think of a "lens" as the pair of a get and set
> method.
>
> * get: T => R // get the field on T of type R * set: T => R => T //
> set the field on T of type R, returning new T
>
> I encoded it earlier with a representation using CoState to take
> advantage of this particular data type and its library (it is very
> rich). We can still construct a Lens using get/set pairs quite
> easily:
>
> object Lens { def lens[T, R]( get: T => R , set: T => R => T ):
> Lens[T, R] = Lens(t => CoState[R, T](set(t), get(t))) }
>
> We can also implement get and set methods on Lens easily (omitted
> for brevity).
>
> Now one of the important, killer useful properties of lenses is
> that *they compose*. What does this mean? It means the following
> function exists (also with an identity Lens[X, X]):
>
> def compose[A, B, C](f: Lens[A, B], g: Lens[B, C]): Lens[A, C]
>
> Let's write it as a method to demonstrate it:
>
> case class Lens[T, R](k: T => CoState[R, T]) { def compose[Q](that:
> Lens[R, Q]): Lens[T, Q] = Lens[T, Q](t => { val CoState(f, a) =
> k(t) val CoState(g, b) = that.k(a) CoState(f compose g, b) }) }
>
> To state it in English, "if we have a lens from T to R, and a lens
> from R to Q, then we have a lens from T to Q." To provide an
> example: if our Building has a (lens) Window and a Window has a
> (lens) Perimeter, then our Building has a (lens) Window. This would
> allow us to "get and set" the Window property on the Building
> class, thus, answering the often-encountered questions regarding
> traversing down object graphs and getting all messy. I digress, but
> hopefully, this conveys an important point.
>
> Further, this Lens class deserves to have all sorts of combinators
> on it for manipulating record fields, but I have omitted them for
> brevity -- hopefully you can imagine the sorts of things that might
> appear. If not, I'll follow-up with some examples.
>
> So we are all fine and dandy at this point, with a nice useful
> library. Looks promising! Let's try to put it into practice.
>
> Suppose we have:
>
> case class Person(name: String, age: Int, friend: Person)
>
> We now want:
>
> * lname: Lens[Person, String] * lage: Lens[Person, Int] * lfriend:
> Lens[Person, Person] // these compose in any Traversable!
>
> However, this is tedious and full of boilerplate:
>
> val lname = Lens.lens(_.name, p => n => p.copy(name = n)) val lage
> = Lens.lens(_.age, p => a => p.copy(age = a)) val lfriend =
> Lens.lens(_.friend p => f => p.copy(friend = f))
>
> eek! We have required effort proportional to the number of fields
> in our record type. This can't be fun.
>
> So, now I can get to the question: Wouldn't it be useful to: a)
> develop a library around these data structures? b) have the
> compiler generate the latter boilerplate code?
>
> I haven't thought too far ahead to implementation, or pondered on
> issues of generating bytecode for each record field, however, I am
> simply tempted by just how useful this particular feature could be.
> It even has that "attraction" to Java programmers coming on over to
> Scala and wondering how we deal with this sort of thing -- can we,
> y'know, kick some serious arse!? Just askin'
>
> Comments? Thanks for listening.
>
>

- --
Tony Morris
http://tmorris.net/

-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.10 (GNU/Linux)
Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org/

iEYEARECAAYFAk3hnIMACgkQmnpgrYe6r62Q6gCdGMnyzReb4Gu9c4W1/8VbqZ/O
JuEAoKNIUtxkfrpqQqFgUJ9bKQC8Cji3
=PIHN
-----END PGP SIGNATURE-----

Kevin Wright 2
Joined: 2010-05-30,
User offline. Last seen 26 weeks 4 days ago.
Re: Re: Compiler-generated lenses for record fields
Boiling it down to the core requirement, I don't see that this would be especially nasty for a plugin to handle.
The main hurdle for any plugin at present is where type checking would fail in the absence of generated code, but that's not the case here.  If the definitions were made abstract and explicitly typed, with an annotation:
    @lens("Person.name") val lname: Lens[Person, String]
    @lens("Person.age") val lage: Lens[Person, Int]     @lens("Person.friend") val lfriend: Lens[Person, Person]
This would pass through the namer and typer phases without problem (the check that calls are to a concrete method don't occur until a later compiler phase).
The plugin could then run after the typer phase, synthesising the necessary code and stripping the ABSTRACT flag from the methods.  The @lens annotation could be removed, but there's real benefit in doing so.
    @lens("Person.name")     val lname: Lens[Person, String] =       Lens.lens(_.name, p => n => p.copy(name = n))
    @lens("Person.age")     val lage: Lens[Person, Int] =       Lens.lens(_.age, p => a => p.copy(age = a))
    @lens("Person.friend")     val lfriend: Lens[Person, Person] =       Lens.lens(_.friend p => f => p.copy(friend = f))
Assuming that the above code is valid for lenses in Scalaz, I'll put this in my backlog to experiment with, on the offchance that I somehow manage to find any free time in the near future.

On 29 May 2011 12:23, Chris Marshall <oxbow_lakes [at] hotmail [dot] com> wrote:
Speaking purely for myself; before watching Edward Kmett's presentation on lenses, I had little to no idea what they were for, hence why I should care about a compiler plugin for them. My interest is now piqued and I certainly agree that a compiler plugin is critical to their use. Or at the very least a lens-code-generator.
Chris

Date: Sun, 29 May 2011 11:08:19 +1000
From: tonymorris [at] gmail [dot] com
To: scala-debate [at] googlegroups [dot] com
Subject: [scala-debate] Re: Compiler-generated lenses for record fields


-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

Am I to assume the silence is a result of distaste for the idea, or
more that one would prefer to see the patch before taking it
seriously? (or something else)


On 25/05/11 22:08, Tony Morris wrote:
>
> OK, so we recently got the copy method for updating fields of case
> classes.
>
> However, what about lenses in a more general and composable form?
> It is possible to implement as a library, right up to the point
> where it would require you to implement the lens for each record (I
> use the term "record" so as not to be necessarily specific to case
> classes) field.
>
> Let us first define a lens in library code.
>
> The Identity functor is trivial to get us started:
>
> case class Identity[A](a: A) { def map[B](f: A => B): Identity[A] =
> sys.error("todo") def flatMap[B](f: A => Identity[B]): Identity[B]
> = sys.error("todo") }
>
> Next we define costate (the categorical dual to state) as a
> comonad transformer:
>
> // A Comonad for any Comonad[F] (todo) case class CoStateT[A, F[_],
> B](put: F[A => B], set: A)
>
> Next we are able to define a Lens:
>
> case class Lens[T, R](k: T => CoStateT[R, Identity, T])
>
> Now, I have been a bit elaborate here with the comonad transformer
> then specialising to Identity. I did this because there is a huge
> and useful library behind CoStateT, which benefits by being in
> transformer version. Nevertheless, we may simplify for the purposes
> of staying on track with lenses:
>
> case class CoState[A, B](put: A => B, up: A) { // A Comonad def
> coFlatMap[C](f: CoState[A, B] => C): CoState[A, C] = CoState(a =>
> f(CoState(put, a)), up) }
>
> A lens is now a little simpler (specialised):
>
> case class Lens[T, R](k: T => CoState[R, T])
>
> Now, we sometimes think of a "lens" as the pair of a get and set
> method.
>
> * get: T => R // get the field on T of type R * set: T => R => T //
> set the field on T of type R, returning new T
>
> I encoded it earlier with a representation using CoState to take
> advantage of this particular data type and its library (it is very
> rich). We can still construct a Lens using get/set pairs quite
> easily:
>
> object Lens { def lens[T, R]( get: T => R , set: T => R => T ):
> Lens[T, R] = Lens(t => CoState[R, T](set(t), get(t))) }
>
> We can also implement get and set methods on Lens easily (omitted
> for brevity).
>
> Now one of the important, killer useful properties of lenses is
> that *they compose*. What does this mean? It means the following
> function exists (also with an identity Lens[X, X]):
>
> def compose[A, B, C](f: Lens[A, B], g: Lens[B, C]): Lens[A, C]
>
> Let's write it as a method to demonstrate it:
>
> case class Lens[T, R](k: T => CoState[R, T]) { def compose[Q](that:
> Lens[R, Q]): Lens[T, Q] = Lens[T, Q](t => { val CoState(f, a) =
> k(t) val CoState(g, b) = that.k(a) CoState(f compose g, b) }) }
>
> To state it in English, "if we have a lens from T to R, and a lens
> from R to Q, then we have a lens from T to Q." To provide an
> example: if our Building has a (lens) Window and a Window has a
> (lens) Perimeter, then our Building has a (lens) Window. This would
> allow us to "get and set" the Window property on the Building
> class, thus, answering the often-encountered questions regarding
> traversing down object graphs and getting all messy. I digress, but
> hopefully, this conveys an important point.
>
> Further, this Lens class deserves to have all sorts of combinators
> on it for manipulating record fields, but I have omitted them for
> brevity -- hopefully you can imagine the sorts of things that might
> appear. If not, I'll follow-up with some examples.
>
> So we are all fine and dandy at this point, with a nice useful
> library. Looks promising! Let's try to put it into practice.
>
> Suppose we have:
>
> case class Person(name: String, age: Int, friend: Person)
>
> We now want:
>
> * lname: Lens[Person, String] * lage: Lens[Person, Int] * lfriend:
> Lens[Person, Person] // these compose in any Traversable!
>
> However, this is tedious and full of boilerplate:
>
> val lname = Lens.lens(_.name, p => n => p.copy(name = n)) val lage
> = Lens.lens(_.age, p => a => p.copy(age = a)) val lfriend =
> Lens.lens(_.friend p => f => p.copy(friend = f))
>
> eek! We have required effort proportional to the number of fields
> in our record type. This can't be fun.
>
> So, now I can get to the question: Wouldn't it be useful to: a)
> develop a library around these data structures? b) have the
> compiler generate the latter boilerplate code?
>
> I haven't thought too far ahead to implementation, or pondered on
> issues of generating bytecode for each record field, however, I am
> simply tempted by just how useful this particular feature could be.
> It even has that "attraction" to Java programmers coming on over to
> Scala and wondering how we deal with this sort of thing -- can we,
> y'know, kick some serious arse!? Just askin'
>
> Comments? Thanks for listening.
>
>

- --
Tony Morris
http://tmorris.net/

-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.10 (GNU/Linux)
Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org/

iEYEARECAAYFAk3hnIMACgkQmnpgrYe6r62Q6gCdGMnyzReb4Gu9c4W1/8VbqZ/O
JuEAoKNIUtxkfrpqQqFgUJ9bKQC8Cji3
=PIHN
-----END PGP SIGNATURE-----




--
Kevin Wright

gtalk / msn : kev [dot] lee [dot] wright [at] gmail [dot] comkev [dot] lee [dot] wright [at] gmail [dot] commail: kevin [dot] wright [at] scalatechnology [dot] com
vibe / skype: kev.lee.wrightquora: http://www.quora.com/Kevin-Wright
twitter: @thecoda

"My point today is that, if we wish to count lines of code, we should not regard them as "lines produced" but as "lines spent": the current conventional wisdom is so foolish as to book that count on the wrong side of the ledger" ~ Dijkstra
Kevin Wright 2
Joined: 2010-05-30,
User offline. Last seen 26 weeks 4 days ago.
Re: Re: Compiler-generated lenses for record fields
Looking back, that could be simplified further, there's no need to mention the target Type in the annotation:
    @lens("name") val lname: Lens[Person, String]
    @lens("age") val lage: Lens[Person, Int]     @lens("friend") val lfriend: Lens[Person, Person]

On 29 May 2011 12:59, Kevin Wright <kev [dot] lee [dot] wright [at] gmail [dot] com> wrote:
Boiling it down to the core requirement, I don't see that this would be especially nasty for a plugin to handle.
The main hurdle for any plugin at present is where type checking would fail in the absence of generated code, but that's not the case here.  If the definitions were made abstract and explicitly typed, with an annotation:
    @lens("Person.name") val lname: Lens[Person, String]
    @lens("Person.age") val lage: Lens[Person, Int]     @lens("Person.friend") val lfriend: Lens[Person, Person]
This would pass through the namer and typer phases without problem (the check that calls are to a concrete method don't occur until a later compiler phase).
The plugin could then run after the typer phase, synthesising the necessary code and stripping the ABSTRACT flag from the methods.  The @lens annotation could be removed, but there's real benefit in doing so.
    @lens("Person.name")     val lname: Lens[Person, String] =       Lens.lens(_.name, p => n => p.copy(name = n))
    @lens("Person.age")     val lage: Lens[Person, Int] =       Lens.lens(_.age, p => a => p.copy(age = a))
    @lens("Person.friend")     val lfriend: Lens[Person, Person] =       Lens.lens(_.friend p => f => p.copy(friend = f))
Assuming that the above code is valid for lenses in Scalaz, I'll put this in my backlog to experiment with, on the offchance that I somehow manage to find any free time in the near future.

On 29 May 2011 12:23, Chris Marshall <oxbow_lakes [at] hotmail [dot] com> wrote:
Speaking purely for myself; before watching Edward Kmett's presentation on lenses, I had little to no idea what they were for, hence why I should care about a compiler plugin for them. My interest is now piqued and I certainly agree that a compiler plugin is critical to their use. Or at the very least a lens-code-generator.
Chris

Date: Sun, 29 May 2011 11:08:19 +1000
From: tonymorris [at] gmail [dot] com
To: scala-debate [at] googlegroups [dot] com
Subject: [scala-debate] Re: Compiler-generated lenses for record fields


-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

Am I to assume the silence is a result of distaste for the idea, or
more that one would prefer to see the patch before taking it
seriously? (or something else)


On 25/05/11 22:08, Tony Morris wrote:
>
> OK, so we recently got the copy method for updating fields of case
> classes.
>
> However, what about lenses in a more general and composable form?
> It is possible to implement as a library, right up to the point
> where it would require you to implement the lens for each record (I
> use the term "record" so as not to be necessarily specific to case
> classes) field.
>
> Let us first define a lens in library code.
>
> The Identity functor is trivial to get us started:
>
> case class Identity[A](a: A) { def map[B](f: A => B): Identity[A] =
> sys.error("todo") def flatMap[B](f: A => Identity[B]): Identity[B]
> = sys.error("todo") }
>
> Next we define costate (the categorical dual to state) as a
> comonad transformer:
>
> // A Comonad for any Comonad[F] (todo) case class CoStateT[A, F[_],
> B](put: F[A => B], set: A)
>
> Next we are able to define a Lens:
>
> case class Lens[T, R](k: T => CoStateT[R, Identity, T])
>
> Now, I have been a bit elaborate here with the comonad transformer
> then specialising to Identity. I did this because there is a huge
> and useful library behind CoStateT, which benefits by being in
> transformer version. Nevertheless, we may simplify for the purposes
> of staying on track with lenses:
>
> case class CoState[A, B](put: A => B, up: A) { // A Comonad def
> coFlatMap[C](f: CoState[A, B] => C): CoState[A, C] = CoState(a =>
> f(CoState(put, a)), up) }
>
> A lens is now a little simpler (specialised):
>
> case class Lens[T, R](k: T => CoState[R, T])
>
> Now, we sometimes think of a "lens" as the pair of a get and set
> method.
>
> * get: T => R // get the field on T of type R * set: T => R => T //
> set the field on T of type R, returning new T
>
> I encoded it earlier with a representation using CoState to take
> advantage of this particular data type and its library (it is very
> rich). We can still construct a Lens using get/set pairs quite
> easily:
>
> object Lens { def lens[T, R]( get: T => R , set: T => R => T ):
> Lens[T, R] = Lens(t => CoState[R, T](set(t), get(t))) }
>
> We can also implement get and set methods on Lens easily (omitted
> for brevity).
>
> Now one of the important, killer useful properties of lenses is
> that *they compose*. What does this mean? It means the following
> function exists (also with an identity Lens[X, X]):
>
> def compose[A, B, C](f: Lens[A, B], g: Lens[B, C]): Lens[A, C]
>
> Let's write it as a method to demonstrate it:
>
> case class Lens[T, R](k: T => CoState[R, T]) { def compose[Q](that:
> Lens[R, Q]): Lens[T, Q] = Lens[T, Q](t => { val CoState(f, a) =
> k(t) val CoState(g, b) = that.k(a) CoState(f compose g, b) }) }
>
> To state it in English, "if we have a lens from T to R, and a lens
> from R to Q, then we have a lens from T to Q." To provide an
> example: if our Building has a (lens) Window and a Window has a
> (lens) Perimeter, then our Building has a (lens) Window. This would
> allow us to "get and set" the Window property on the Building
> class, thus, answering the often-encountered questions regarding
> traversing down object graphs and getting all messy. I digress, but
> hopefully, this conveys an important point.
>
> Further, this Lens class deserves to have all sorts of combinators
> on it for manipulating record fields, but I have omitted them for
> brevity -- hopefully you can imagine the sorts of things that might
> appear. If not, I'll follow-up with some examples.
>
> So we are all fine and dandy at this point, with a nice useful
> library. Looks promising! Let's try to put it into practice.
>
> Suppose we have:
>
> case class Person(name: String, age: Int, friend: Person)
>
> We now want:
>
> * lname: Lens[Person, String] * lage: Lens[Person, Int] * lfriend:
> Lens[Person, Person] // these compose in any Traversable!
>
> However, this is tedious and full of boilerplate:
>
> val lname = Lens.lens(_.name, p => n => p.copy(name = n)) val lage
> = Lens.lens(_.age, p => a => p.copy(age = a)) val lfriend =
> Lens.lens(_.friend p => f => p.copy(friend = f))
>
> eek! We have required effort proportional to the number of fields
> in our record type. This can't be fun.
>
> So, now I can get to the question: Wouldn't it be useful to: a)
> develop a library around these data structures? b) have the
> compiler generate the latter boilerplate code?
>
> I haven't thought too far ahead to implementation, or pondered on
> issues of generating bytecode for each record field, however, I am
> simply tempted by just how useful this particular feature could be.
> It even has that "attraction" to Java programmers coming on over to
> Scala and wondering how we deal with this sort of thing -- can we,
> y'know, kick some serious arse!? Just askin'
>
> Comments? Thanks for listening.
>
>

- --
Tony Morris
http://tmorris.net/

-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.10 (GNU/Linux)
Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org/

iEYEARECAAYFAk3hnIMACgkQmnpgrYe6r62Q6gCdGMnyzReb4Gu9c4W1/8VbqZ/O
JuEAoKNIUtxkfrpqQqFgUJ9bKQC8Cji3
=PIHN
-----END PGP SIGNATURE-----




--
Kevin Wright

gtalk / msn : kev [dot] lee [dot] wright [at] gmail [dot] comkev [dot] lee [dot] wright [at] gmail [dot] commail: kevin [dot] wright [at] scalatechnology [dot] com
vibe / skype: kev.lee.wrightquora: http://www.quora.com/Kevin-Wright
twitter: @thecoda

"My point today is that, if we wish to count lines of code, we should not regard them as "lines produced" but as "lines spent": the current conventional wisdom is so foolish as to book that count on the wrong side of the ledger" ~ Dijkstra



--
Kevin Wright

gtalk / msn : kev [dot] lee [dot] wright [at] gmail [dot] comkev [dot] lee [dot] wright [at] gmail [dot] commail: kevin [dot] wright [at] scalatechnology [dot] com
vibe / skype: kev.lee.wrightquora: http://www.quora.com/Kevin-Wright
twitter: @thecoda

"My point today is that, if we wish to count lines of code, we should not regard them as "lines produced" but as "lines spent": the current conventional wisdom is so foolish as to book that count on the wrong side of the ledger" ~ Dijkstra
Naftoli Gugenheim
Joined: 2008-12-17,
User offline. Last seen 42 years 45 weeks ago.
Re: Re: Compiler-generated lenses for record fields
Can someone show a sample application that would benefit from composing calls to copy in such a way?


On Sun, May 29, 2011 at 8:04 AM, Kevin Wright <kev [dot] lee [dot] wright [at] gmail [dot] com> wrote:
Looking back, that could be simplified further, there's no need to mention the target Type in the annotation:
    @lens("name") val lname: Lens[Person, String]
    @lens("age") val lage: Lens[Person, Int]     @lens("friend") val lfriend: Lens[Person, Person]

On 29 May 2011 12:59, Kevin Wright <kev [dot] lee [dot] wright [at] gmail [dot] com> wrote:
Boiling it down to the core requirement, I don't see that this would be especially nasty for a plugin to handle.
The main hurdle for any plugin at present is where type checking would fail in the absence of generated code, but that's not the case here.  If the definitions were made abstract and explicitly typed, with an annotation:
    @lens("Person.name") val lname: Lens[Person, String]
    @lens("Person.age") val lage: Lens[Person, Int]     @lens("Person.friend") val lfriend: Lens[Person, Person]
This would pass through the namer and typer phases without problem (the check that calls are to a concrete method don't occur until a later compiler phase).
The plugin could then run after the typer phase, synthesising the necessary code and stripping the ABSTRACT flag from the methods.  The @lens annotation could be removed, but there's real benefit in doing so.
    @lens("Person.name")     val lname: Lens[Person, String] =       Lens.lens(_.name, p => n => p.copy(name = n))
    @lens("Person.age")     val lage: Lens[Person, Int] =       Lens.lens(_.age, p => a => p.copy(age = a))
    @lens("Person.friend")     val lfriend: Lens[Person, Person] =       Lens.lens(_.friend p => f => p.copy(friend = f))
Assuming that the above code is valid for lenses in Scalaz, I'll put this in my backlog to experiment with, on the offchance that I somehow manage to find any free time in the near future.

On 29 May 2011 12:23, Chris Marshall <oxbow_lakes [at] hotmail [dot] com> wrote:
Speaking purely for myself; before watching Edward Kmett's presentation on lenses, I had little to no idea what they were for, hence why I should care about a compiler plugin for them. My interest is now piqued and I certainly agree that a compiler plugin is critical to their use. Or at the very least a lens-code-generator.
Chris

Date: Sun, 29 May 2011 11:08:19 +1000
From: tonymorris [at] gmail [dot] com
To: scala-debate [at] googlegroups [dot] com
Subject: [scala-debate] Re: Compiler-generated lenses for record fields


-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

Am I to assume the silence is a result of distaste for the idea, or
more that one would prefer to see the patch before taking it
seriously? (or something else)


On 25/05/11 22:08, Tony Morris wrote:
>
> OK, so we recently got the copy method for updating fields of case
> classes.
>
> However, what about lenses in a more general and composable form?
> It is possible to implement as a library, right up to the point
> where it would require you to implement the lens for each record (I
> use the term "record" so as not to be necessarily specific to case
> classes) field.
>
> Let us first define a lens in library code.
>
> The Identity functor is trivial to get us started:
>
> case class Identity[A](a: A) { def map[B](f: A => B): Identity[A] =
> sys.error("todo") def flatMap[B](f: A => Identity[B]): Identity[B]
> = sys.error("todo") }
>
> Next we define costate (the categorical dual to state) as a
> comonad transformer:
>
> // A Comonad for any Comonad[F] (todo) case class CoStateT[A, F[_],
> B](put: F[A => B], set: A)
>
> Next we are able to define a Lens:
>
> case class Lens[T, R](k: T => CoStateT[R, Identity, T])
>
> Now, I have been a bit elaborate here with the comonad transformer
> then specialising to Identity. I did this because there is a huge
> and useful library behind CoStateT, which benefits by being in
> transformer version. Nevertheless, we may simplify for the purposes
> of staying on track with lenses:
>
> case class CoState[A, B](put: A => B, up: A) { // A Comonad def
> coFlatMap[C](f: CoState[A, B] => C): CoState[A, C] = CoState(a =>
> f(CoState(put, a)), up) }
>
> A lens is now a little simpler (specialised):
>
> case class Lens[T, R](k: T => CoState[R, T])
>
> Now, we sometimes think of a "lens" as the pair of a get and set
> method.
>
> * get: T => R // get the field on T of type R * set: T => R => T //
> set the field on T of type R, returning new T
>
> I encoded it earlier with a representation using CoState to take
> advantage of this particular data type and its library (it is very
> rich). We can still construct a Lens using get/set pairs quite
> easily:
>
> object Lens { def lens[T, R]( get: T => R , set: T => R => T ):
> Lens[T, R] = Lens(t => CoState[R, T](set(t), get(t))) }
>
> We can also implement get and set methods on Lens easily (omitted
> for brevity).
>
> Now one of the important, killer useful properties of lenses is
> that *they compose*. What does this mean? It means the following
> function exists (also with an identity Lens[X, X]):
>
> def compose[A, B, C](f: Lens[A, B], g: Lens[B, C]): Lens[A, C]
>
> Let's write it as a method to demonstrate it:
>
> case class Lens[T, R](k: T => CoState[R, T]) { def compose[Q](that:
> Lens[R, Q]): Lens[T, Q] = Lens[T, Q](t => { val CoState(f, a) =
> k(t) val CoState(g, b) = that.k(a) CoState(f compose g, b) }) }
>
> To state it in English, "if we have a lens from T to R, and a lens
> from R to Q, then we have a lens from T to Q." To provide an
> example: if our Building has a (lens) Window and a Window has a
> (lens) Perimeter, then our Building has a (lens) Window. This would
> allow us to "get and set" the Window property on the Building
> class, thus, answering the often-encountered questions regarding
> traversing down object graphs and getting all messy. I digress, but
> hopefully, this conveys an important point.
>
> Further, this Lens class deserves to have all sorts of combinators
> on it for manipulating record fields, but I have omitted them for
> brevity -- hopefully you can imagine the sorts of things that might
> appear. If not, I'll follow-up with some examples.
>
> So we are all fine and dandy at this point, with a nice useful
> library. Looks promising! Let's try to put it into practice.
>
> Suppose we have:
>
> case class Person(name: String, age: Int, friend: Person)
>
> We now want:
>
> * lname: Lens[Person, String] * lage: Lens[Person, Int] * lfriend:
> Lens[Person, Person] // these compose in any Traversable!
>
> However, this is tedious and full of boilerplate:
>
> val lname = Lens.lens(_.name, p => n => p.copy(name = n)) val lage
> = Lens.lens(_.age, p => a => p.copy(age = a)) val lfriend =
> Lens.lens(_.friend p => f => p.copy(friend = f))
>
> eek! We have required effort proportional to the number of fields
> in our record type. This can't be fun.
>
> So, now I can get to the question: Wouldn't it be useful to: a)
> develop a library around these data structures? b) have the
> compiler generate the latter boilerplate code?
>
> I haven't thought too far ahead to implementation, or pondered on
> issues of generating bytecode for each record field, however, I am
> simply tempted by just how useful this particular feature could be.
> It even has that "attraction" to Java programmers coming on over to
> Scala and wondering how we deal with this sort of thing -- can we,
> y'know, kick some serious arse!? Just askin'
>
> Comments? Thanks for listening.
>
>

- --
Tony Morris
http://tmorris.net/

-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.10 (GNU/Linux)
Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org/

iEYEARECAAYFAk3hnIMACgkQmnpgrYe6r62Q6gCdGMnyzReb4Gu9c4W1/8VbqZ/O
JuEAoKNIUtxkfrpqQqFgUJ9bKQC8Cji3
=PIHN
-----END PGP SIGNATURE-----




--
Kevin Wright

gtalk / msn : kev [dot] lee [dot] wright [at] gmail [dot] comkev [dot] lee [dot] wright [at] gmail [dot] commail: kevin [dot] wright [at] scalatechnology [dot] com
vibe / skype: kev.lee.wrightquora: http://www.quora.com/Kevin-Wright
twitter: @thecoda

"My point today is that, if we wish to count lines of code, we should not regard them as "lines produced" but as "lines spent": the current conventional wisdom is so foolish as to book that count on the wrong side of the ledger" ~ Dijkstra



--
Kevin Wright

gtalk / msn : kev [dot] lee [dot] wright [at] gmail [dot] comkev [dot] lee [dot] wright [at] gmail [dot] commail: kevin [dot] wright [at] scalatechnology [dot] com
vibe / skype: kev.lee.wrightquora: http://www.quora.com/Kevin-Wright
twitter: @thecoda

"My point today is that, if we wish to count lines of code, we should not regard them as "lines produced" but as "lines spent": the current conventional wisdom is so foolish as to book that count on the wrong side of the ledger" ~ Dijkstra

Chris Marshall
Joined: 2009-06-17,
User offline. Last seen 44 weeks 3 days ago.
RE: Re: Compiler-generated lenses for record fields
Naftoli - watch Edward Kmett's presentation. The idea is to use imperative-like code with what will *look* like "deep" mutation using for comprehensions
Chris

Date: Sun, 29 May 2011 14:03:13 -0400
Subject: Re: [scala-debate] Re: Compiler-generated lenses for record fields
From: naftoligug [at] gmail [dot] com
To: kev [dot] lee [dot] wright [at] gmail [dot] com
CC: oxbow_lakes [at] hotmail [dot] com; scala-debate [at] googlegroups [dot] com

Can someone show a sample application that would benefit from composing calls to copy in such a way?


On Sun, May 29, 2011 at 8:04 AM, Kevin Wright <kev [dot] lee [dot] wright [at] gmail [dot] com> wrote:
Looking back, that could be simplified further, there's no need to mention the target Type in the annotation:
    @lens("name") val lname: Lens[Person, String]
    @lens("age") val lage: Lens[Person, Int]     @lens("friend") val lfriend: Lens[Person, Person]

On 29 May 2011 12:59, Kevin Wright <kev [dot] lee [dot] wright [at] gmail [dot] com> wrote:
Boiling it down to the core requirement, I don't see that this would be especially nasty for a plugin to handle.
The main hurdle for any plugin at present is where type checking would fail in the absence of generated code, but that's not the case here.  If the definitions were made abstract and explicitly typed, with an annotation:
    @lens("Person.name") val lname: Lens[Person, String]
    @lens("Person.age") val lage: Lens[Person, Int]     @lens("Person.friend") val lfriend: Lens[Person, Person]
This would pass through the namer and typer phases without problem (the check that calls are to a concrete method don't occur until a later compiler phase).
The plugin could then run after the typer phase, synthesising the necessary code and stripping the ABSTRACT flag from the methods.  The @lens annotation could be removed, but there's real benefit in doing so.
    @lens("Person.name")     val lname: Lens[Person, String] =       Lens.lens(_.name, p => n => p.copy(name = n))
    @lens("Person.age")     val lage: Lens[Person, Int] =       Lens.lens(_.age, p => a => p.copy(age = a))
    @lens("Person.friend")     val lfriend: Lens[Person, Person] =       Lens.lens(_.friend p => f => p.copy(friend = f))
Assuming that the above code is valid for lenses in Scalaz, I'll put this in my backlog to experiment with, on the offchance that I somehow manage to find any free time in the near future.

On 29 May 2011 12:23, Chris Marshall <oxbow_lakes [at] hotmail [dot] com> wrote:
Speaking purely for myself; before watching Edward Kmett's presentation on lenses, I had little to no idea what they were for, hence why I should care about a compiler plugin for them. My interest is now piqued and I certainly agree that a compiler plugin is critical to their use. Or at the very least a lens-code-generator.
Chris

Date: Sun, 29 May 2011 11:08:19 +1000
From: tonymorris [at] gmail [dot] com
To: scala-debate [at] googlegroups [dot] com
Subject: [scala-debate] Re: Compiler-generated lenses for record fields


-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

Am I to assume the silence is a result of distaste for the idea, or
more that one would prefer to see the patch before taking it
seriously? (or something else)


On 25/05/11 22:08, Tony Morris wrote:
>
> OK, so we recently got the copy method for updating fields of case
> classes.
>
> However, what about lenses in a more general and composable form?
> It is possible to implement as a library, right up to the point
> where it would require you to implement the lens for each record (I
> use the term "record" so as not to be necessarily specific to case
> classes) field.
>
> Let us first define a lens in library code.
>
> The Identity functor is trivial to get us started:
>
> case class Identity[A](a: A) { def map[B](f: A => B): Identity[A] =
> sys.error("todo") def flatMap[B](f: A => Identity[B]): Identity[B]
> = sys.error("todo") }
>
> Next we define costate (the categorical dual to state) as a
> comonad transformer:
>
> // A Comonad for any Comonad[F] (todo) case class CoStateT[A, F[_],
> B](put: F[A => B], set: A)
>
> Next we are able to define a Lens:
>
> case class Lens[T, R](k: T => CoStateT[R, Identity, T])
>
> Now, I have been a bit elaborate here with the comonad transformer
> then specialising to Identity. I did this because there is a huge
> and useful library behind CoStateT, which benefits by being in
> transformer version. Nevertheless, we may simplify for the purposes
> of staying on track with lenses:
>
> case class CoState[A, B](put: A => B, up: A) { // A Comonad def
> coFlatMap[C](f: CoState[A, B] => C): CoState[A, C] = CoState(a =>
> f(CoState(put, a)), up) }
>
> A lens is now a little simpler (specialised):
>
> case class Lens[T, R](k: T => CoState[R, T])
>
> Now, we sometimes think of a "lens" as the pair of a get and set
> method.
>
> * get: T => R // get the field on T of type R * set: T => R => T //
> set the field on T of type R, returning new T
>
> I encoded it earlier with a representation using CoState to take
> advantage of this particular data type and its library (it is very
> rich). We can still construct a Lens using get/set pairs quite
> easily:
>
> object Lens { def lens[T, R]( get: T => R , set: T => R => T ):
> Lens[T, R] = Lens(t => CoState[R, T](set(t), get(t))) }
>
> We can also implement get and set methods on Lens easily (omitted
> for brevity).
>
> Now one of the important, killer useful properties of lenses is
> that *they compose*. What does this mean? It means the following
> function exists (also with an identity Lens[X, X]):
>
> def compose[A, B, C](f: Lens[A, B], g: Lens[B, C]): Lens[A, C]
>
> Let's write it as a method to demonstrate it:
>
> case class Lens[T, R](k: T => CoState[R, T]) { def compose[Q](that:
> Lens[R, Q]): Lens[T, Q] = Lens[T, Q](t => { val CoState(f, a) =
> k(t) val CoState(g, b) = that.k(a) CoState(f compose g, b) }) }
>
> To state it in English, "if we have a lens from T to R, and a lens
> from R to Q, then we have a lens from T to Q." To provide an
> example: if our Building has a (lens) Window and a Window has a
> (lens) Perimeter, then our Building has a (lens) Window. This would
> allow us to "get and set" the Window property on the Building
> class, thus, answering the often-encountered questions regarding
> traversing down object graphs and getting all messy. I digress, but
> hopefully, this conveys an important point.
>
> Further, this Lens class deserves to have all sorts of combinators
> on it for manipulating record fields, but I have omitted them for
> brevity -- hopefully you can imagine the sorts of things that might
> appear. If not, I'll follow-up with some examples.
>
> So we are all fine and dandy at this point, with a nice useful
> library. Looks promising! Let's try to put it into practice.
>
> Suppose we have:
>
> case class Person(name: String, age: Int, friend: Person)
>
> We now want:
>
> * lname: Lens[Person, String] * lage: Lens[Person, Int] * lfriend:
> Lens[Person, Person] // these compose in any Traversable!
>
> However, this is tedious and full of boilerplate:
>
> val lname = Lens.lens(_.name, p => n => p.copy(name = n)) val lage
> = Lens.lens(_.age, p => a => p.copy(age = a)) val lfriend =
> Lens.lens(_.friend p => f => p.copy(friend = f))
>
> eek! We have required effort proportional to the number of fields
> in our record type. This can't be fun.
>
> So, now I can get to the question: Wouldn't it be useful to: a)
> develop a library around these data structures? b) have the
> compiler generate the latter boilerplate code?
>
> I haven't thought too far ahead to implementation, or pondered on
> issues of generating bytecode for each record field, however, I am
> simply tempted by just how useful this particular feature could be.
> It even has that "attraction" to Java programmers coming on over to
> Scala and wondering how we deal with this sort of thing -- can we,
> y'know, kick some serious arse!? Just askin'
>
> Comments? Thanks for listening.
>
>

- --
Tony Morris
http://tmorris.net/

-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.10 (GNU/Linux)
Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org/

iEYEARECAAYFAk3hnIMACgkQmnpgrYe6r62Q6gCdGMnyzReb4Gu9c4W1/8VbqZ/O
JuEAoKNIUtxkfrpqQqFgUJ9bKQC8Cji3
=PIHN
-----END PGP SIGNATURE-----




--
Kevin Wright

gtalk / msn : kev [dot] lee [dot] wright [at] gmail [dot] comkev [dot] lee [dot] wright [at] gmail [dot] commail: kevin [dot] wright [at] scalatechnology [dot] com
vibe / skype: kev.lee.wrightquora: http://www.quora.com/Kevin-Wright
twitter: @thecoda

"My point today is that, if we wish to count lines of code, we should not regard them as "lines produced" but as "lines spent": the current conventional wisdom is so foolish as to book that count on the wrong side of the ledger" ~ Dijkstra



--
Kevin Wright

gtalk / msn : kev [dot] lee [dot] wright [at] gmail [dot] comkev [dot] lee [dot] wright [at] gmail [dot] commail: kevin [dot] wright [at] scalatechnology [dot] com
vibe / skype: kev.lee.wrightquora: http://www.quora.com/Kevin-Wright
twitter: @thecoda

"My point today is that, if we wish to count lines of code, we should not regard them as "lines produced" but as "lines spent": the current conventional wisdom is so foolish as to book that count on the wrong side of the ledger" ~ Dijkstra

dcsobral
Joined: 2009-04-23,
User offline. Last seen 38 weeks 5 days ago.
Re: Re: Compiler-generated lenses for record fields

On Sun, May 29, 2011 at 15:03, Naftoli Gugenheim wrote:
> Can someone show a sample application that would benefit from composing
> calls to copy in such a way?

http://code.google.com/p/virtual-combat-cards/source/detail?r=789

>
>
> On Sun, May 29, 2011 at 8:04 AM, Kevin Wright
> wrote:
>>
>> Looking back, that could be simplified further, there's no need to mention
>> the target Type in the annotation:
>>     @lens("name") val lname: Lens[Person, String]
>>     @lens("age") val lage: Lens[Person, Int]
>>     @lens("friend") val lfriend: Lens[Person, Person]
>>
>> On 29 May 2011 12:59, Kevin Wright wrote:
>>>
>>> Boiling it down to the core requirement, I don't see that this would be
>>> especially nasty for a plugin to handle.
>>> The main hurdle for any plugin at present is where type checking would
>>> fail in the absence of generated code, but that's not the case here.  If the
>>> definitions were made abstract and explicitly typed, with an annotation:
>>>     @lens("Person.name") val lname: Lens[Person, String]
>>>     @lens("Person.age") val lage: Lens[Person, Int]
>>>     @lens("Person.friend") val lfriend: Lens[Person, Person]
>>> This would pass through the namer and typer phases without problem (the
>>> check that calls are to a concrete method don't occur until a later compiler
>>> phase).
>>> The plugin could then run after the typer phase, synthesising the
>>> necessary code and stripping the ABSTRACT flag from the methods.  The @lens
>>> annotation could be removed, but there's real benefit in doing so.
>>>     @lens("Person.name")
>>>     val lname: Lens[Person, String] =
>>>       Lens.lens(_.name, p => n => p.copy(name = n))
>>>     @lens("Person.age")
>>>     val lage: Lens[Person, Int] =
>>>       Lens.lens(_.age, p => a => p.copy(age = a))
>>>     @lens("Person.friend")
>>>     val lfriend: Lens[Person, Person] =
>>>       Lens.lens(_.friend p => f => p.copy(friend = f))
>>> Assuming that the above code is valid for lenses in Scalaz, I'll put this
>>> in my backlog to experiment with, on the offchance that I somehow manage to
>>> find any free time in the near future.
>>>
>>> On 29 May 2011 12:23, Chris Marshall wrote:
>>>>
>>>> Speaking purely for myself; before watching Edward Kmett's presentation
>>>> on lenses, I had little to no idea what they were for, hence why I should
>>>> care about a compiler plugin for them. My interest is now piqued and I
>>>> certainly agree that a compiler plugin is critical to their use. Or at the
>>>> very least a lens-code-generator.
>>>> Chris
>>>>
>>>> ________________________________
>>>> Date: Sun, 29 May 2011 11:08:19 +1000
>>>> From: tonymorris [at] gmail [dot] com
>>>> To: scala-debate [at] googlegroups [dot] com
>>>> Subject: [scala-debate] Re: Compiler-generated lenses for record fields
>>>>
>>>>
>>>> -----BEGIN PGP SIGNED MESSAGE-----
>>>> Hash: SHA1
>>>>
>>>> Am I to assume the silence is a result of distaste for the idea, or
>>>> more that one would prefer to see the patch before taking it
>>>> seriously? (or something else)
>>>>
>>>>
>>>> On 25/05/11 22:08, Tony Morris wrote:
>>>> >
>>>> > OK, so we recently got the copy method for updating fields of case
>>>> > classes.
>>>> >
>>>> > However, what about lenses in a more general and composable form?
>>>> > It is possible to implement as a library, right up to the point
>>>> > where it would require you to implement the lens for each record (I
>>>> > use the term "record" so as not to be necessarily specific to case
>>>> > classes) field.
>>>> >
>>>> > Let us first define a lens in library code.
>>>> >
>>>> > The Identity functor is trivial to get us started:
>>>> >
>>>> > case class Identity[A](a: A) { def map[B](f: A => B): Identity[A] =
>>>> > sys.error("todo") def flatMap[B](f: A => Identity[B]): Identity[B]
>>>> > = sys.error("todo") }
>>>> >
>>>> > Next we define costate (the categorical dual to state) as a
>>>> > comonad transformer:
>>>> >
>>>> > // A Comonad for any Comonad[F] (todo) case class CoStateT[A, F[_],
>>>> > B](put: F[A => B], set: A)
>>>> >
>>>> > Next we are able to define a Lens:
>>>> >
>>>> > case class Lens[T, R](k: T => CoStateT[R, Identity, T])
>>>> >
>>>> > Now, I have been a bit elaborate here with the comonad transformer
>>>> > then specialising to Identity. I did this because there is a huge
>>>> > and useful library behind CoStateT, which benefits by being in
>>>> > transformer version. Nevertheless, we may simplify for the purposes
>>>> > of staying on track with lenses:
>>>> >
>>>> > case class CoState[A, B](put: A => B, up: A) { // A Comonad def
>>>> > coFlatMap[C](f: CoState[A, B] => C): CoState[A, C] = CoState(a =>
>>>> > f(CoState(put, a)), up) }
>>>> >
>>>> > A lens is now a little simpler (specialised):
>>>> >
>>>> > case class Lens[T, R](k: T => CoState[R, T])
>>>> >
>>>> > Now, we sometimes think of a "lens" as the pair of a get and set
>>>> > method.
>>>> >
>>>> > * get: T => R // get the field on T of type R * set: T => R => T //
>>>> > set the field on T of type R, returning new T
>>>> >
>>>> > I encoded it earlier with a representation using CoState to take
>>>> > advantage of this particular data type and its library (it is very
>>>> > rich). We can still construct a Lens using get/set pairs quite
>>>> > easily:
>>>> >
>>>> > object Lens { def lens[T, R]( get: T => R , set: T => R => T ):
>>>> > Lens[T, R] = Lens(t => CoState[R, T](set(t), get(t))) }
>>>> >
>>>> > We can also implement get and set methods on Lens easily (omitted
>>>> > for brevity).
>>>> >
>>>> > Now one of the important, killer useful properties of lenses is
>>>> > that *they compose*. What does this mean? It means the following
>>>> > function exists (also with an identity Lens[X, X]):
>>>> >
>>>> > def compose[A, B, C](f: Lens[A, B], g: Lens[B, C]): Lens[A, C]
>>>> >
>>>> > Let's write it as a method to demonstrate it:
>>>> >
>>>> > case class Lens[T, R](k: T => CoState[R, T]) { def compose[Q](that:
>>>> > Lens[R, Q]): Lens[T, Q] = Lens[T, Q](t => { val CoState(f, a) =
>>>> > k(t) val CoState(g, b) = that.k(a) CoState(f compose g, b) }) }
>>>> >
>>>> > To state it in English, "if we have a lens from T to R, and a lens
>>>> > from R to Q, then we have a lens from T to Q." To provide an
>>>> > example: if our Building has a (lens) Window and a Window has a
>>>> > (lens) Perimeter, then our Building has a (lens) Window. This would
>>>> > allow us to "get and set" the Window property on the Building
>>>> > class, thus, answering the often-encountered questions regarding
>>>> > traversing down object graphs and getting all messy. I digress, but
>>>> > hopefully, this conveys an important point.
>>>> >
>>>> > Further, this Lens class deserves to have all sorts of combinators
>>>> > on it for manipulating record fields, but I have omitted them for
>>>> > brevity -- hopefully you can imagine the sorts of things that might
>>>> > appear. If not, I'll follow-up with some examples.
>>>> >
>>>> > So we are all fine and dandy at this point, with a nice useful
>>>> > library. Looks promising! Let's try to put it into practice.
>>>> >
>>>> > Suppose we have:
>>>> >
>>>> > case class Person(name: String, age: Int, friend: Person)
>>>> >
>>>> > We now want:
>>>> >
>>>> > * lname: Lens[Person, String] * lage: Lens[Person, Int] * lfriend:
>>>> > Lens[Person, Person] // these compose in any Traversable!
>>>> >
>>>> > However, this is tedious and full of boilerplate:
>>>> >
>>>> > val lname = Lens.lens(_.name, p => n => p.copy(name = n)) val lage
>>>> > = Lens.lens(_.age, p => a => p.copy(age = a)) val lfriend =
>>>> > Lens.lens(_.friend p => f => p.copy(friend = f))
>>>> >
>>>> > eek! We have required effort proportional to the number of fields
>>>> > in our record type. This can't be fun.
>>>> >
>>>> > So, now I can get to the question: Wouldn't it be useful to: a)
>>>> > develop a library around these data structures? b) have the
>>>> > compiler generate the latter boilerplate code?
>>>> >
>>>> > I haven't thought too far ahead to implementation, or pondered on
>>>> > issues of generating bytecode for each record field, however, I am
>>>> > simply tempted by just how useful this particular feature could be.
>>>> > It even has that "attraction" to Java programmers coming on over to
>>>> > Scala and wondering how we deal with this sort of thing -- can we,
>>>> > y'know, kick some serious arse!? Just askin'
>>>> >
>>>> > Comments? Thanks for listening.
>>>> >
>>>> >
>>>>
>>>> - --
>>>> Tony Morris
>>>> http://tmorris.net/
>>>>
>>>> -----BEGIN PGP SIGNATURE-----
>>>> Version: GnuPG v1.4.10 (GNU/Linux)
>>>> Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org/
>>>>
>>>> iEYEARECAAYFAk3hnIMACgkQmnpgrYe6r62Q6gCdGMnyzReb4Gu9c4W1/8VbqZ/O
>>>> JuEAoKNIUtxkfrpqQqFgUJ9bKQC8Cji3
>>>> =PIHN
>>>> -----END PGP SIGNATURE-----
>>>>
>>>
>>>
>>>
>>> --
>>> Kevin Wright
>>>
>>> gtalk / msn : kev [dot] lee [dot] wright [at] gmail [dot] com
>>> mail: kevin [dot] wright [at] scalatechnology [dot] com
>>> vibe / skype: kev.lee.wright
>>> quora: http://www.quora.com/Kevin-Wright
>>> twitter: @thecoda
>>>
>>> "My point today is that, if we wish to count lines of code, we should not
>>> regard them as "lines produced" but as "lines spent": the current
>>> conventional wisdom is so foolish as to book that count on the wrong side of
>>> the ledger" ~ Dijkstra
>>
>>
>>
>> --
>> Kevin Wright
>>
>> gtalk / msn : kev [dot] lee [dot] wright [at] gmail [dot] com
>> mail: kevin [dot] wright [at] scalatechnology [dot] com
>> vibe / skype: kev.lee.wright
>> quora: http://www.quora.com/Kevin-Wright
>> twitter: @thecoda
>>
>> "My point today is that, if we wish to count lines of code, we should not
>> regard them as "lines produced" but as "lines spent": the current
>> conventional wisdom is so foolish as to book that count on the wrong side of
>> the ledger" ~ Dijkstra
>
>

ichoran
Joined: 2009-08-14,
User offline. Last seen 2 years 3 weeks ago.
Re: Re: Compiler-generated lenses for record fields
On Sat, May 28, 2011 at 9:08 PM, Tony Morris <tonymorris [at] gmail [dot] com> wrote:

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

Am I to assume the silence is a result of distaste for the idea, or
more that one would prefer to see the patch before taking it
seriously? (or something else)

These would be considerably more exciting to me if
  (a) there were hooks for notifications as well
  (b) there was a mutable version also for efficiency and/or Java interaction

I rarely need lenses--case class copy does fine, plus local definition of a method for updating a single deep field if I have to do it more than once, used in conjunction with a pipe.  (Can compose the methods with andThen if you really want to.)

I also rarely need notification hooks; I build in what I need at the time, and generally try not to structure code that way anyway unless I have to (e.g. with Swing).

And if I have a mutable version I just go update it directly (except I sometimes have to do it in a side effecting block, which is a bit awkward--with lenses I could more easily do it in-line, which would reduce the risk of errors).

If there was one tool that would combine all of these getter/setter/notifier tasks under one framework, however, I would use it at least three times as often as rarely, which is probably often enough for me to be pretty interested.  Otherwise, it's just not very compelling for me.  There's only one project that I've created lately where they'd be really nice to have.

  --Rex

Tony Morris 2
Joined: 2009-03-20,
User offline. Last seen 42 years 45 weeks ago.
Re: Re: Compiler-generated lenses for record fields

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

On 30/05/11 12:16, Rex Kerr wrote:
> On Sat, May 28, 2011 at 9:08 PM, Tony Morris
> tonymorris [at] gmail [dot] com (<tonymorris [at] gmail [dot] com>) wrote:
>
>>
>> -----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1
>>
>> Am I to assume the silence is a result of distaste for the idea,
>> or more that one would prefer to see the patch before taking it
>> seriously? (or something else)
>>
>
> These would be considerably more exciting to me if (a) there were
> hooks for notifications as well

That's what composition is all about!

> (b) there was a mutable version also for efficiency and/or Java
> interaction
It doesn't really make sense for there "to be a mutable version." In
particular, the differences between lenses and mutable getter/setter
is as big as the differences between any two arbitrary data
structures. I'm really not fond of the conflation of
"mutable/immutable" versions of data structures -- the consequences
are pretty disastrous.

>
> I rarely need lenses--case class copy does fine, plus local
> definition of a method for updating a single deep field if I have
> to do it more than once, used in conjunction with a pipe. (Can
> compose the methods with andThen if you really want to.)
It's not a matter of "needing" lenses, but rather exploiting the
library that it gives rise to. I've only alluded to it. If I had a
dollar for the number of projects that I have seen that invented
specialised lenses and the subsequent library... (I have a rough
version of it if you like)

>
> I also rarely need notification hooks; I build in what I need at
> the time, and generally try not to structure code that way anyway
> unless I have to (e.g. with Swing).
This is just composition -- lenses deliberately preserve compositional
program properties. That is, we intentionally have this.

>
> And if I have a mutable version I just go update it directly
> (except I sometimes have to do it in a side effecting block, which
> is a bit awkward--with lenses I could more easily do it in-line,
> which would reduce the risk of errors).

Email.scala:133 "mutable version" conflation error.

>
> If there was one tool that would combine all of these
> getter/setter/notifier tasks under one framework, however, I would
> use it at least three times as often as rarely, which is probably
> often enough for me to be pretty interested.
I bet if you realised you were already using it, you'd change your mind!
> Otherwise, it's just not very compelling for me. There's only one
> project that I've created lately where they'd be really nice to
> have.
>
> --Rex
>
Thanks for the feedback.

- --
Tony Morris
http://tmorris.net/

-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.10 (GNU/Linux)
Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org/

iEYEARECAAYFAk3jSrcACgkQmnpgrYe6r62cpQCggwy7kCIiPCf03L1zceLF4vY8
EAoAnjCMg0lYX+KYFZ/vrtzLFXHXUt5Y
=MnKB
-----END PGP SIGNATURE-----

ichoran
Joined: 2009-08-14,
User offline. Last seen 2 years 3 weeks ago.
Re: Re: Compiler-generated lenses for record fields


On Mon, May 30, 2011 at 3:43 AM, Tony Morris <tonymorris [at] gmail [dot] com> wrote:

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

On 30/05/11 12:16, Rex Kerr wrote:
> On Sat, May 28, 2011 at 9:08 PM, Tony Morris
> tonymorris [at] gmail [dot] com (<tonymorris [at] gmail [dot] com>) wrote:
>
>>
>> -----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1
>>
>> Am I to assume the silence is a result of distaste for the idea,
>> or more that one would prefer to see the patch before taking it
>> seriously? (or something else)
>>
>
> These would be considerably more exciting to me if (a) there were
> hooks for notifications as well

That's what composition is all about!

I don't see any place to insert side-effects _or_ to include an additional return value (without messing up composability) in auto-generated code.  I see how to do it if you're creating the lenses by hand.
 

> (b) there was a mutable version also for efficiency and/or Java
> interaction
It doesn't really make sense for there "to be a mutable version." In
particular, the differences between lenses and mutable getter/setter
is as big as the differences between any two arbitrary data
structures. I'm really not fond of the conflation of
"mutable/immutable" versions of data structures -- the consequences
are pretty disastrous.

The consequences of making immutable and mutable data structures so similar semantically that you cannot tell them apart are, I agree, pretty disastrous.  But if one is passing data through multiple functions in a single chain, then whether the underlying representation is mutable or immutable is just an implementation detail.

In particular, mutable and immutable constructs have opposite problems: with mutable structures, the error is that one forgets that one needs a copy of the state; with immutable structures, the error is that one forgets that the old copy of the state is now useless (out of date).

In both cases, chains of operations come to the rescue: the language syntax makes it nearly impossible for you to be mistaken about which version of the state you are working with; the correct state is the one that is the easiest to interact with.

Thus, I claim that the _most obvious_ application of lenses is almost equally useful in a mutable context.  Since I haven't seen the library you're alluding to, I can't really tell whether the less-obvious applications are also almost equally useful in both contexts.  So, barring any revelations, I'd like to see a similar capability for mutable constructs as well, preferably with semantics chosen so that anyone who knows one will recognize the other, but will also be under no delusion about which case they're dealing with.  (For example, for mathematics I've used + - * for immutable and +~ -~ *~ for mutable operations.  It's very hard to get confused about what is happening, but the math is still fairly readable even when done mutably.)

  --Rex


dcsobral
Joined: 2009-04-23,
User offline. Last seen 38 weeks 5 days ago.
Re: Re: Compiler-generated lenses for record fields

On Mon, May 30, 2011 at 05:29, Rex Kerr wrote:
>
>> > These would be considerably more exciting to me if (a) there were
>> > hooks for notifications as well
>>
>> That's what composition is all about!
>
> I don't see any place to insert side-effects _or_ to include an additional
> return value (without messing up composability) in auto-generated code.  I
> see how to do it if you're creating the lenses by hand.

The point is that you can declare a class LensPublisher[T](lens:
Lens[T, T]) extends Lens[T, T] with Publisher[Event[T]] and then use
it like this:

case class Person(name: String)

// Assuming my proposal for compiler aid
val personPublisher = new LensPublisher[Person](Lens(Person.Lens.person))
val personNamePublisher = personPublisher compose Lens(Person.Lens.name)
personNamePublisher subscribe (mySubscriber)

val person = Person(Rex)
personNamePublisher.set(person, "Rex Kerr")

And that will send a notification to mySubscriber. To make the point
clearer, you can create generic lenses for various purposes, and
compose them with specific lenses. Likewise, you can write code that
handles data structure generically, and pass it lenses that do the
right thing.

ichoran
Joined: 2009-08-14,
User offline. Last seen 2 years 3 weeks ago.
Re: Re: Compiler-generated lenses for record fields

On Mon, May 30, 2011 at 9:31 AM, Daniel Sobral <dcsobral [at] gmail [dot] com> wrote:
On Mon, May 30, 2011 at 05:29, Rex Kerr <ichoran [at] gmail [dot] com> wrote:
>
>> > These would be considerably more exciting to me if (a) there were
>> > hooks for notifications as well
>>
>> That's what composition is all about!
>
> I don't see any place to insert side-effects _or_ to include an additional
> return value (without messing up composability) in auto-generated code.  I
> see how to do it if you're creating the lenses by hand.

The point is that you can declare a class LensPublisher[T](lens:
Lens[T, T]) extends Lens[T, T] with Publisher[Event[T]] and then use
it like this:

case class Person(name: String)

// Assuming my proposal for compiler aid
val personPublisher = new LensPublisher[Person](Lens(Person.Lens.person))
val personNamePublisher = personPublisher compose Lens(Person.Lens.name)
personNamePublisher subscribe (mySubscriber)

val person = Person(Rex)
personNamePublisher.set(person, "Rex Kerr")

And that will send a notification to mySubscriber. To make the point
clearer, you can create generic lenses for various purposes, and
compose them with specific lenses. Likewise, you can write code that
handles data structure generically, and pass it lenses that do the
right thing.

You move the responsibility for giving the notification from the source of the lens (the case class) to yourself (composing in a publisher), but I see the point.  With a little bit of discipline, this could be okay.

Incidentally, exactly the same construct as you proposed would work for mutable fields also.

  --Rex

Andreas Scheinert
Joined: 2011-02-11,
User offline. Last seen 42 years 45 weeks ago.
Re: Compiler-generated lenses for record fields

Hi Rex!
I think the point was not to show how such a mechanism works for
immutable datastructures, all agreed before (have you seen e. Kmetts
talk?) that with mutably it works 'just' fine.
Regards Andreas
On 30 Mai, 20:53, Rex Kerr wrote:
> On Mon, May 30, 2011 at 9:31 AM, Daniel Sobral wrote:
> > On Mon, May 30, 2011 at 05:29, Rex Kerr wrote:
>
> > >> > These would be considerably more exciting to me if (a) there were
> > >> > hooks for notifications as well
>
> > >> That's what composition is all about!
>
> > > I don't see any place to insert side-effects _or_ to include an
> > additional
> > > return value (without messing up composability) in auto-generated code.
> > I
> > > see how to do it if you're creating the lenses by hand.
>
> > The point is that you can declare a class LensPublisher[T](lens:
> > Lens[T, T]) extends Lens[T, T] with Publisher[Event[T]] and then use
> > it like this:
>
> > case class Person(name: String)
>
> > // Assuming my proposal for compiler aid
> > val personPublisher = new LensPublisher[Person](Lens(Person.Lens.person))
> > val personNamePublisher = personPublisher compose Lens(Person.Lens.name)
> > personNamePublisher subscribe (mySubscriber)
>
> > val person = Person(Rex)
> > personNamePublisher.set(person, "Rex Kerr")
>
> > And that will send a notification to mySubscriber. To make the point
> > clearer, you can create generic lenses for various purposes, and
> > compose them with specific lenses. Likewise, you can write code that
> > handles data structure generically, and pass it lenses that do the
> > right thing.
>
> You move the responsibility for giving the notification from the source of
> the lens (the case class) to yourself (composing in a publisher), but I see
> the point.  With a little bit of discipline, this could be okay.
>
> Incidentally, exactly the same construct as you proposed would work for
> mutable fields also.
>
>   --Rex

Naftoli Gugenheim
Joined: 2008-12-17,
User offline. Last seen 42 years 45 weeks ago.
Re: Re: Compiler-generated lenses for record fields
I'm wondering if it would be possible to do something like this, possibly instead of special compiler support.
Given case class Case(n: Int), to automatically get a Lens[Case,Int] from the expression c.n, perhaps something like the following could be done:
def lens[A,B](accessor: scala.reflect.Code[A=>B]): Lens[A,B]
enabling you to write lens(c)(_.n). Internally lens would analyze the AST and generate a getter that just reads the field, and a setter that calls copy, via reflection.


On Tue, May 31, 2011 at 6:38 AM, Andreas Scheinert <andreas [dot] scheinert [at] googlemail [dot] com> wrote:
Hi Rex!
I think the point was not to show how such a mechanism works for
immutable datastructures, all agreed before (have you seen e. Kmetts
talk?) that with mutably it works 'just' fine.
Regards Andreas
On 30 Mai, 20:53, Rex Kerr <icho [dot] [dot] [dot] [at] gmail [dot] com> wrote:
> On Mon, May 30, 2011 at 9:31 AM, Daniel Sobral <dcsob [dot] [dot] [dot] [at] gmail [dot] com> wrote:
> > On Mon, May 30, 2011 at 05:29, Rex Kerr <icho [dot] [dot] [dot] [at] gmail [dot] com> wrote:
>
> > >> > These would be considerably more exciting to me if (a) there were
> > >> > hooks for notifications as well
>
> > >> That's what composition is all about!
>
> > > I don't see any place to insert side-effects _or_ to include an
> > additional
> > > return value (without messing up composability) in auto-generated code.
> > I
> > > see how to do it if you're creating the lenses by hand.
>
> > The point is that you can declare a class LensPublisher[T](lens:
> > Lens[T, T]) extends Lens[T, T] with Publisher[Event[T]] and then use
> > it like this:
>
> > case class Person(name: String)
>
> > // Assuming my proposal for compiler aid
> > val personPublisher = new LensPublisher[Person](Lens(Person.Lens.person))
> > val personNamePublisher = personPublisher compose Lens(Person.Lens.name)
> > personNamePublisher subscribe (mySubscriber)
>
> > val person = Person(Rex)
> > personNamePublisher.set(person, "Rex Kerr")
>
> > And that will send a notification to mySubscriber. To make the point
> > clearer, you can create generic lenses for various purposes, and
> > compose them with specific lenses. Likewise, you can write code that
> > handles data structure generically, and pass it lenses that do the
> > right thing.
>
> You move the responsibility for giving the notification from the source of
> the lens (the case class) to yourself (composing in a publisher), but I see
> the point.  With a little bit of discipline, this could be okay.
>
> Incidentally, exactly the same construct as you proposed would work for
> mutable fields also.
>
>   --Rex

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