Lenses: what they are and why you may want to use them ;)
Published: 2021-11-25 19:34:52

This is my first post here, and I hope you will enjoy it.
I was trying to figure out which topic I can talk about and I came up with one I encounter in the last few weeks.

I am a backend software engineer in N26, Barcelona office. In N26 we use kotlin and we build microservices, as you can imagine, to run the digital bank.

We were working on a backoffice microservice, that somehow handled some data about our customers and exposed some endpoints to change them (like a CRUD but not exactly).

So I cannot tell the problem using the same data because those are privileged, but I can explain the same problem using a simpler example.

Suppose you have a model in your application to store information about a Person:

```kotlin
data class Person(
 val name: String, 
 val address: Address
)

data class Address(
 val streetName: String, 
 val number: String, 
 val city: String
) 
```

Now imagine you have an endpoint and so a domain service to update information about the person, let's pretend you have an interface like:

```kotlin
interface UpdateAddressService {
 
 fun updateStreetName(
   person: Person, 
   newStreetName: String
 ): Person
}
```

Now if you try to implement such an interface using just kotlin language you can end up with a class like this:

```kotlin
class SimpleUpdateAddressService: UpdateAddressService {
 
 fun updateStreetName(
   person: Person, 
   newStreetName: String
 ): Person {
  val newAddress = 
    person.address.copy(streetName = newStreetName)
  val updatedPerson = 
    person.copy(address = newAddress)
  return updatedPerson
 }
}
```

Now  you can imagine that things can easily become cumbersome, imagine for example if the `streetName` was another value object, something like:

```kotlin
data class StreetName(
 val streetType: String,
 val name: String
) 
```

then you would end up with something like:

```kotlin
class SimpleUpdateAddressService: UpdateAddressService {
 
 fun updateStreetName(
   person: Person, 
   newStreetName: String
 ): Person {
  val updatedStreetName = 
    person.address.streetName.copy(name = newStreetName)
  val newAddress = 
    person.address.copy(streetName = updatedStreetName)
  val updatedPerson = 
    person.copy(address = newAddress)
  return updatedPerson
 }
}
```

As you can see, as soon as your model starts to become nested the longer you will take to do the simple update you were required to.

There is a simple concept in functional programming, called Lens. A lens is a very simple interface that lets you get something from a source, let's call it a target, and let you also set a new target given a source value.

```kotlin
interface Lens<S, T> {

  fun get(s: S): T
  fun set(s: S, newT: T): S
}
```

Now the real power as often happens in functional programming is that this structure supports what is called a Semigroup, meaning you can define an operation that takes two lenses and combines them, and also this operation is associative.

So for example you can define this `combine` method in the `Lens` interface like this:

```kotlin
interface Lens<S, T> {

  fun get(s: S): T
  fun set(newT: T, s: S): S

  fun <A> combine(l2: Lens<T, A>): Lens<S, A> {
    val self = this
    return object : Lens<S, A> {

      override fun get(s: S): A {
        val function: (s: S) -> A = self::get andThen l2::get
        return function(s)
      }

      override fun set(newT: A, s: S): S {
        val newT1 = l2.set(newT, self.get(s))
        return self.set(newT1, s)
      }
    }
  }
}
```

This is basically what [arrow-kt](https://arrow-kt.io/docs/optics/dsl/) provides to you without the need of having to define the `Lens` interface and all yours needed lenses by yourself.

Arrow 1.0 uses `kapt` [kotlin annotation processor](https://kotlinlang.org/docs/kapt.html#using-in-gradle), and using the `@optics` annotation you will be able to leverage the generated code at compile time like this:

```kotlin
@optics
data class Person(
 val name:String, 
 val address: Address
) {
 companion object
}

@optics
data class Address(
 val streetName: String, 
 val number: String, 
 val city: String
) {
 companion object
}
```

And so with these newly defined data classes we can implement the previous interface in a different way:

```kotlin
class ArrowUpdateAddressService: UpdateAddressService {
 private val lens = Person.address.streetname
 fun updateStreetName(
   person: Person, 
   newStreetName: String
 ): Person {
    return lens.set(source = person, focus = newStreetName)
 }
}
```

and in case we had the `StreetName` value object, we just need to change the lens:

```kotlin
class ArrowUpdateAddressService: UpdateAddressService {
 private val lens = Person.address.streetname.name
 // unchanged code
```

So you can see how powerful and clean this structure is.

I hope you enjoyed this article. 

See you!

# References

The image in the cover is taken from [Wikimedia](https://commons.wikimedia.org/wiki/File:Photographic_lenses_front_view.jpg)