4

Monocle's optionals have the following access functions (for Optional[C,A]):

getOption: C => Option[A]
set: A => C => C

This is at odds with the original definition of (partial) asymmetric data lenses. I would expect:

getOption: C => Option[A]
setOption: A => C => Option[C]

What is the reason for that? How do I obtain classic partial lenses with Monocle? When programming lenses I found that it much more problematic to ensure totality of set than of get ...

  • Monocle provides a `setOption` method on `Optional` that provides the operation you're looking for. `set` just returns the given `C` unmodified if there's no match. – Travis Brown Nov 15 '15 at 22:31
  • So is the optional setter derivable from an optional getter and a total setter? – Andrzej Wąsowski Nov 16 '15 at 07:20
  • After inspecting the source code for `setOption`, it seems to me that it can never produce `None` so making a partial lense this way does not seem viable, does it? – Andrzej Wąsowski Nov 16 '15 at 08:31

1 Answers1

7

Consider the following partial lens for looking up values in a list by index (note that this is just a pedagogical example, since monocle.std.list.listIndex provides this functionality off the shelf):

import monocle.Optional

def listIndexOptional[A](i: Int): Optional[List[A], A] =
  Optional[List[A], A](_.lift(i))(a => l =>
    if (l.isDefinedAt(i)) l.updated(i, a) else l
  )

Now we can define an Optional that points to the third item in a list of strings:

val thirdString = listIndexOptional[String](2)

And use it like this:

scala> thirdString.set("0")(List("a", "b", "c"))
res4: List[String] = List(a, b, 0)

scala> thirdString.set("0")(List("a", "b"))
res5: List[String] = List(a, b)

Note that if there is no third item, the operation just returns the list unmodified. If we wanted to know whether the item had been updated, we can use setOption:

scala> thirdString.setOption("0")(List("a", "b", "c"))
res6: Option[List[String]] = Some(List(a, b, 0))

scala> thirdString.setOption("0")(List("a", "b"))
res7: Option[List[String]] = None

The fact that the Optional.apply method takes as its second argument a function A => S => S is partly a convenience, since we often want to define partial lenses in this way, and partly so that we can't define a partial lens where getOption and setOption disagree on whether the target exists.

If you really want to, you can always define an Optional in terms of a A => S => Option[S] setter by tacking a getOrElse(s) on the end.

Travis Brown
  • 138,631
  • 12
  • 375
  • 680
  • Another reason why (`getOption`, `set`) was selected as canonical definition of `Optional` is the diamond relationship between `Iso`, `Prism`, `Lens` and `Optional`. @puffnfresh made a nice [diagram](https://twitter.com/puffnfresh/status/654862731548446720) to show this. – Julien Truffaut Nov 16 '15 at 20:57
  • Thanks! I understand. I misunderstood the implementation of setOption. I can see now that it uses modify and getOption, so it has all it needs to discover whether merging has succeded and failed. I think I can work with this :) – Andrzej Wąsowski Nov 16 '15 at 21:29