Hello guys. I’m new to Arrow and functional progra...
# arrow
u
Hello guys. I’m new to Arrow and functional programming. Trying to find a way to create a copy of sealed class instance with some modification avoiding the following `when`s (in this case, I would like to update
abstract val
property):
Copy code
@optics
sealed class TestArrow {
    abstract val isSelected : Boolean

    companion object {}

    @optics
    data class Test1(override val isSelected: Boolean, val fooBar1: String) : TestArrow() {
        companion object {}
    }

    @optics
    data class Test2(override val isSelected: Boolean, val fooBar1: String) : TestArrow() {
        companion object {}
    }
    @optics
    data class Test3(override val isSelected: Boolean,  val fooBar1: String) : TestArrow() {
        companion object {}
    }
}

fun TestArrow.updateSelected(isSelected: Boolean) : TestArrow {
    return when(this) {
        is TestArrow.Test1 -> this.copy(isSelected = isSelected)
        is TestArrow.Test2 -> this.copy(isSelected = isSelected)
        is TestArrow.Test3 -> this.copy(isSelected = isSelected)
    }
}
Any help will be very appreciated
s
Hey ubu, That's definitely a great use-case to write a
Lens
to update a common value in a
sealed class
but it's not yet supported by the
@optics
generator. There is a ticket for it, which has been open for quite some time, but we're waiting until we can replace
kapt
by a MPP meta solution. Which is expected soon-ish. You can however easily manually write a optic for it:
Copy code
val isSelectedLens: Lens<TestArrow, Boolean> =
  Lens(
    get = { it.isSelected },
    set = {
      when(it) {
         is TestArrow.Test1 -> this.copy(isSelected = isSelected)
         is TestArrow.Test2 -> this.copy(isSelected = isSelected)
         is TestArrow.Test3 -> this.copy(isSelected = isSelected)
      }
    } 
  )
You can also make it work with the DSL if needed 🙂 I'd be glad to help!
u
@simon.vergauwen, I’m starting to appreciate Arrow. what if I would like to update two fields, which is not present by all children classes? how would you do it?
From your example I didn’t quite understand how to apply the modification on
TestArrow
class. Could you elaborate on that?
s
Hey Ubu, Sorry for the delay in reply. Modifying
TestArrow
can be done with
isSelectedLens
in the following manners.
Copy code
val value: TestArrow = ...
isSelectedLenes.modify(value) { boolean -> !boolean } // There is also `get`, `set`, etc

Or when nested in other types
val values: List<TestArrow> = ...

(Every.list<TestArrow>() compose isSelectedLens).modify(values) { bool -> !bool } // Modifies *all* TestArrow in the list
Updating multiple values at once is not possible with Optics out of the box, in the sense that
@optics
always generates optics for all values independenly. You could however create a:
Lens<TestArrow, Pair<Boolean, String>>
which also updates the
foobar
properties even though it's not a shared value in
TestArrow
.
Copy code
val isSelectedLens: Lens<TestArrow, Boolean> =
  Lens(
    get = { 
      when(it) {
         is TestArrow.Test1 -> Pair(isSelected, foobar1)
         is TestArrow.Test2 -> Pair(isSelected, foobar1)
         is TestArrow.Test3 -> Pair(isSelected, foobar1)
      }    },
    set = { value, (isSelected, foobar1) ->
      when(value) {
         is TestArrow.Test1 -> value.copy(isSelected = isSelected, foobar1= foobar1)
         is TestArrow.Test2 -> value.copy(isSelected = isSelected, foobar1= foobar1)
         is TestArrow.Test3 -> value.copy(isSelected = isSelected, foobar1= foobar1)
      }
    } 
  )
Which then could be used in the same manner as the other
Lens
above.
More info can be found here: https://arrow-kt.io/docs/optics/lens/