[Preemptive Proposal] Operator for Lenses


(Michael Henson) #1

There hasn't been a major Lens thread on the list so far, but I'm intrigued
by the idea and was tinkering with a related proposal until I stumbled
across Brandon William's "Lenses in Swift" video and discovered that Lenses
cover everything I wanted to do and more.

The one thing that stuck with me, though, is the question of syntax. If
Lenses are to be a native part of the Swift language, how would one declare
them?

We've currently got not-so-secret methods at the type level of Structs and
Classes for every member function:

class Example {
    func action() {}
}

let example = Example()
example.action // () -> Void
Example.action // (Example) -> Void

That looks a lot like a Lens on the member function, though if I understand
correctly the current plan is for those to go away in a future version.

We also have to deal with Type-level members and functions:

class Example {
   static action() {}
   static name: String
}

Example.action() // () -> Void
Example.name // String

So, using a dot-operator as the way to get a Lens could be problematic due
to name collisions.

The Proposal:

Use the '#' character in place of the dot operator to retrieve a Lens for a
Type/member pair:

Example#action() // (Example) -> Void
Example#name // Lens<Example,String>, autogenerated to work on the
'name' member

Member function names should be fully-specified with the mechanism from
Doug Gregor's method naming proposal.

Some notes:
* A specific operator stands out and is easier to scan for as a code reader.
* The octothorpe seems to be available in Swift.
* It also has a current meaning in a technology familiar to most everyone -
the Document Fragment in HTML - which could make the idea easier to explain
to newcomers, by analogy.

What about Lenses on Type-level members? My first thought is that we don't
have to support that because they're more like namespaced globals rather
than parts of a data type. They can always be referenced directly. That
might be a vacuous observation to people more familiar with the Lens
concept, but I noted it for completeness.

Mike


(Andrey Tarantsov) #2

Could you please explain what a lens is/means? I only found [1], but quick skimming didn't reveal any lenses.

[1] https://www.youtube.com/watch?v=estNbh2TF3E

A.

···

On Jan 15, 2016, at 6:10 AM, Michael Henson via swift-evolution <swift-evolution@swift.org> wrote:

There hasn't been a major Lens thread on the list so far, but I'm intrigued by the idea and was tinkering with a related proposal until I stumbled across Brandon William's "Lenses in Swift" video and discovered that Lenses cover everything I wanted to do and more.

The one thing that stuck with me, though, is the question of syntax. If Lenses are to be a native part of the Swift language, how would one declare them?

We've currently got not-so-secret methods at the type level of Structs and Classes for every member function:

class Example {
    func action() {}
}

let example = Example()
example.action // () -> Void
Example.action // (Example) -> Void

That looks a lot like a Lens on the member function, though if I understand correctly the current plan is for those to go away in a future version.

We also have to deal with Type-level members and functions:

class Example {
   static action() {}
   static name: String
}

Example.action() // () -> Void
Example.name // String

So, using a dot-operator as the way to get a Lens could be problematic due to name collisions.

The Proposal:

Use the '#' character in place of the dot operator to retrieve a Lens for a Type/member pair:

Example#action() // (Example) -> Void
Example#name // Lens<Example,String>, autogenerated to work on the 'name' member

Member function names should be fully-specified with the mechanism from Doug Gregor's method naming proposal.

Some notes:
* A specific operator stands out and is easier to scan for as a code reader.
* The octothorpe seems to be available in Swift.
* It also has a current meaning in a technology familiar to most everyone - the Document Fragment in HTML - which could make the idea easier to explain to newcomers, by analogy.

What about Lenses on Type-level members? My first thought is that we don't have to support that because they're more like namespaced globals rather than parts of a data type. They can always be referenced directly. That might be a vacuous observation to people more familiar with the Lens concept, but I noted it for completeness.

Mike
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Michael Henson) #3

It's a way of referring to a specific member of a type with a data
structure that, when given an instance of the type, behaves as if it's
actually a direct reference to that member on that instance.

If you have:

struct Example {
    let name: String
}

If you have a Lens for the 'name' member of the 'Example' type:

let nameLens = Example#name // assume this generates a Lens, for this
example

then you can hold on to that, pass it around, etc. and apply it to any
Example instance you might encounter later on.

The idea comes primarily from Functional Programming. I'm not familiar
enough with FP concepts to go into depth about the ways it's useful in that
field, but it has come up in Swift because there isn't currently a way to
refer to getters and setters on data properties in the same way that you
might be able to directly refer to a member function. The Lens approach
seems to be the current front-runner for Swift's answer to that problem.

Mike

···

On Thu, Jan 14, 2016 at 4:44 PM, Andrey Tarantsov <andrey@tarantsov.com> wrote:

Could you please explain what a lens is/means? I only found [1], but quick
skimming didn't reveal any lenses.

[1] https://www.youtube.com/watch?v=estNbh2TF3E

A.

On Jan 15, 2016, at 6:10 AM, Michael Henson via swift-evolution < > swift-evolution@swift.org> wrote:

There hasn't been a major Lens thread on the list so far, but I'm
intrigued by the idea and was tinkering with a related proposal until I
stumbled across Brandon William's "Lenses in Swift" video and discovered
that Lenses cover everything I wanted to do and more.

The one thing that stuck with me, though, is the question of syntax. If
Lenses are to be a native part of the Swift language, how would one declare
them?

We've currently got not-so-secret methods at the type level of Structs and
Classes for every member function:

class Example {
    func action() {}
}

let example = Example()
example.action // () -> Void
Example.action // (Example) -> Void

That looks a lot like a Lens on the member function, though if I
understand correctly the current plan is for those to go away in a future
version.

We also have to deal with Type-level members and functions:

class Example {
   static action() {}
   static name: String
}

Example.action() // () -> Void
Example.name // String

So, using a dot-operator as the way to get a Lens could be problematic due
to name collisions.

The Proposal:

Use the '#' character in place of the dot operator to retrieve a Lens for
a Type/member pair:

Example#action() // (Example) -> Void
Example#name // Lens<Example,String>, autogenerated to work on the
'name' member

Member function names should be fully-specified with the mechanism from
Doug Gregor's method naming proposal.

Some notes:
* A specific operator stands out and is easier to scan for as a code
reader.
* The octothorpe seems to be available in Swift.
* It also has a current meaning in a technology familiar to most everyone
- the Document Fragment in HTML - which could make the idea easier to
explain to newcomers, by analogy.

What about Lenses on Type-level members? My first thought is that we don't
have to support that because they're more like namespaced globals rather
than parts of a data type. They can always be referenced directly. That
might be a vacuous observation to people more familiar with the Lens
concept, but I noted it for completeness.

Mike
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Brent Royal-Gordon) #4

Could you please explain what a lens is/means? I only found [1], but quick skimming didn't reveal any lenses.

[1] https://www.youtube.com/watch?v=estNbh2TF3E

In pure functional programming languages, where you can't mutate existing instances, a lens is a function which takes an instance and a new value for some field inside the instance—sometimes deep inside it—and returns a copy of the original instance with that new value in place. So, for instance, if you have a `User` type which has a `name` property:

  let nameLens = User.name
  let currentName = nameLens.get(originalUser) // get the name from originalUser
  let modifiedUser = nameLens.set(originalUser, to: "Joe") // return a copy of originalUser with name set to "Joe"

In functional programming, you can easily chain lenses together, so that you could create a lens for, say, `User.creditCards[0].billingAddress.houseNumber`—and the setter on that lens will actually take a house number and return a whole different `User` instance with that house number applied.

Now, Swift is a little different from a pure functional language. It doesn't have the limitation that functional languages do—it's perfectly fine to modify existing instances—so a Swift lens's setter would probably actually modify the existing instance. On the other hand, Swift often modifies an existing value inside a property instead of creating a whole new one, so the "setter" is better modeled as a read-modify-write operation:

  let nameLens = User.name
  let currentName = nameLens.get(originalUser)
  nameLens.modify(originalUser) { (inout name: String) in
    name = "Joe"
  }

Finally, there's the question of how you should *access* a lens. `User.name`—that is, a lens that can be used with any instance of `User`—is good for some things, but you'd also like to be able to get a lens that's already bound to a particular instance.

Michael Henson is basically proposing syntaxes to retrieve a lens for a particular instance, but personally, I think this is putting the cart before the horse a bit. Right now, I'm personally more interested in finding out what a lens *is* in Swift. What can it do? How is it expressed? What can you use it for?

···

--
Brent Royal-Gordon
Architechies


(David Sweeris) #5

Mutability issues aside, isn’t this kinda the same thing?
let nameLens: (_: User)->String = {$0.name}
let currentName = nameLens(originalUser)

···

On Jan 14, 2016, at 17:30, Brent Royal-Gordon via swift-evolution <swift-evolution@swift.org> wrote:

Could you please explain what a lens is/means? I only found [1], but quick skimming didn't reveal any lenses.

[1] https://www.youtube.com/watch?v=estNbh2TF3E

In pure functional programming languages, where you can't mutate existing instances, a lens is a function which takes an instance and a new value for some field inside the instance—sometimes deep inside it—and returns a copy of the original instance with that new value in place. So, for instance, if you have a `User` type which has a `name` property:

  let nameLens = User.name
  let currentName = nameLens.get(originalUser) // get the name from originalUser
  let modifiedUser = nameLens.set(originalUser, to: "Joe") // return a copy of originalUser with name set to "Joe"

In functional programming, you can easily chain lenses together, so that you could create a lens for, say, `User.creditCards[0].billingAddress.houseNumber`—and the setter on that lens will actually take a house number and return a whole different `User` instance with that house number applied.

Now, Swift is a little different from a pure functional language. It doesn't have the limitation that functional languages do—it's perfectly fine to modify existing instances—so a Swift lens's setter would probably actually modify the existing instance. On the other hand, Swift often modifies an existing value inside a property instead of creating a whole new one, so the "setter" is better modeled as a read-modify-write operation:

  let nameLens = User.name
  let currentName = nameLens.get(originalUser)
  nameLens.modify(originalUser) { (inout name: String) in
    name = "Joe"
  }

Finally, there's the question of how you should *access* a lens. `User.name`—that is, a lens that can be used with any instance of `User`—is good for some things, but you'd also like to be able to get a lens that's already bound to a particular instance.

Michael Henson is basically proposing syntaxes to retrieve a lens for a particular instance, but personally, I think this is putting the cart before the horse a bit. Right now, I'm personally more interested in finding out what a lens *is* in Swift. What can it do? How is it expressed? What can you use it for?

--
Brent Royal-Gordon
Architechies

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Robert Widmann) #6

Speaking as a co-author of a Lens library, the need is far greater for richer data types, typing schemes, and Metaprogramming in general than for first-class lens support. Currently, you can only get to about the level of `Optional` (lens' Optional, not Swift) before the language falls out from under you and you lose generality of implementation. If anything, this proposal could be the start of a great standalone mogenerator-esque tool.

~Robert Widmann

2016/01/14 20:11、Michael Henson via swift-evolution <swift-evolution@swift.org> のメッセージ:

···

It's a way of referring to a specific member of a type with a data structure that, when given an instance of the type, behaves as if it's actually a direct reference to that member on that instance.

If you have:

struct Example {
    let name: String
}

If you have a Lens for the 'name' member of the 'Example' type:

let nameLens = Example#name // assume this generates a Lens, for this example

then you can hold on to that, pass it around, etc. and apply it to any Example instance you might encounter later on.

The idea comes primarily from Functional Programming. I'm not familiar enough with FP concepts to go into depth about the ways it's useful in that field, but it has come up in Swift because there isn't currently a way to refer to getters and setters on data properties in the same way that you might be able to directly refer to a member function. The Lens approach seems to be the current front-runner for Swift's answer to that problem.

Mike

On Thu, Jan 14, 2016 at 4:44 PM, Andrey Tarantsov <andrey@tarantsov.com> wrote:
Could you please explain what a lens is/means? I only found [1], but quick skimming didn't reveal any lenses.

[1] https://www.youtube.com/watch?v=estNbh2TF3E

A.

On Jan 15, 2016, at 6:10 AM, Michael Henson via swift-evolution <swift-evolution@swift.org> wrote:

There hasn't been a major Lens thread on the list so far, but I'm intrigued by the idea and was tinkering with a related proposal until I stumbled across Brandon William's "Lenses in Swift" video and discovered that Lenses cover everything I wanted to do and more.

The one thing that stuck with me, though, is the question of syntax. If Lenses are to be a native part of the Swift language, how would one declare them?

We've currently got not-so-secret methods at the type level of Structs and Classes for every member function:

class Example {
    func action() {}
}

let example = Example()
example.action // () -> Void
Example.action // (Example) -> Void

That looks a lot like a Lens on the member function, though if I understand correctly the current plan is for those to go away in a future version.

We also have to deal with Type-level members and functions:

class Example {
   static action() {}
   static name: String
}

Example.action() // () -> Void
Example.name // String

So, using a dot-operator as the way to get a Lens could be problematic due to name collisions.

The Proposal:

Use the '#' character in place of the dot operator to retrieve a Lens for a Type/member pair:

Example#action() // (Example) -> Void
Example#name // Lens<Example,String>, autogenerated to work on the 'name' member

Member function names should be fully-specified with the mechanism from Doug Gregor's method naming proposal.

Some notes:
* A specific operator stands out and is easier to scan for as a code reader.
* The octothorpe seems to be available in Swift.
* It also has a current meaning in a technology familiar to most everyone - the Document Fragment in HTML - which could make the idea easier to explain to newcomers, by analogy.

What about Lenses on Type-level members? My first thought is that we don't have to support that because they're more like namespaced globals rather than parts of a data type. They can always be referenced directly. That might be a vacuous observation to people more familiar with the Lens concept, but I noted it for completeness.

Mike
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Craig Cruden) #7

If I read this right, things like “withFilter” in Scala could be considered a lens on the original collection. In that it does not actually perform any function on the collection, but the following function call on the views the data through that lens.

Scala

val a = List(1,2,3,4,5)

a: List[Int] = List(1, 2, 3, 4, 5)

val b = a.withFilter(x => x % 2 == 0 )

scala.collection.generic.FilterMonadic[Int,List[Int]] = scala.collection.TraversableLike$WithFilter@77f0d43c

val c = b.map(x => x)

···

c: List[Int] = List(2, 4)

On 2016-01-15, at 8:11:56, Michael Henson via swift-evolution <swift-evolution@swift.org> wrote:

It's a way of referring to a specific member of a type with a data structure that, when given an instance of the type, behaves as if it's actually a direct reference to that member on that instance.

If you have:

struct Example {
    let name: String
}

If you have a Lens for the 'name' member of the 'Example' type:

let nameLens = Example#name // assume this generates a Lens, for this example

then you can hold on to that, pass it around, etc. and apply it to any Example instance you might encounter later on.

The idea comes primarily from Functional Programming. I'm not familiar enough with FP concepts to go into depth about the ways it's useful in that field, but it has come up in Swift because there isn't currently a way to refer to getters and setters on data properties in the same way that you might be able to directly refer to a member function. The Lens approach seems to be the current front-runner for Swift's answer to that problem.

Mike

On Thu, Jan 14, 2016 at 4:44 PM, Andrey Tarantsov <andrey@tarantsov.com <mailto:andrey@tarantsov.com>> wrote:
Could you please explain what a lens is/means? I only found [1], but quick skimming didn't reveal any lenses.

[1] https://www.youtube.com/watch?v=estNbh2TF3E <https://www.youtube.com/watch?v=estNbh2TF3E>

A.

On Jan 15, 2016, at 6:10 AM, Michael Henson via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

There hasn't been a major Lens thread on the list so far, but I'm intrigued by the idea and was tinkering with a related proposal until I stumbled across Brandon William's "Lenses in Swift" video and discovered that Lenses cover everything I wanted to do and more.

The one thing that stuck with me, though, is the question of syntax. If Lenses are to be a native part of the Swift language, how would one declare them?

We've currently got not-so-secret methods at the type level of Structs and Classes for every member function:

class Example {
    func action() {}
}

let example = Example()
example.action // () -> Void
Example.action // (Example) -> Void

That looks a lot like a Lens on the member function, though if I understand correctly the current plan is for those to go away in a future version.

We also have to deal with Type-level members and functions:

class Example {
   static action() {}
   static name: String
}

Example.action() // () -> Void
Example.name // String

So, using a dot-operator as the way to get a Lens could be problematic due to name collisions.

The Proposal:

Use the '#' character in place of the dot operator to retrieve a Lens for a Type/member pair:

Example#action() // (Example) -> Void
Example#name // Lens<Example,String>, autogenerated to work on the 'name' member

Member function names should be fully-specified with the mechanism from Doug Gregor's method naming proposal.

Some notes:
* A specific operator stands out and is easier to scan for as a code reader.
* The octothorpe seems to be available in Swift.
* It also has a current meaning in a technology familiar to most everyone - the Document Fragment in HTML - which could make the idea easier to explain to newcomers, by analogy.

What about Lenses on Type-level members? My first thought is that we don't have to support that because they're more like namespaced globals rather than parts of a data type. They can always be referenced directly. That might be a vacuous observation to people more familiar with the Lens concept, but I noted it for completeness.

Mike
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution