How to update Map using monocle

1.7k views Asked by At

I'd like to try Monocle library. But i could not find help resources for base syntax.

In short i need optics Map[K,V] -> A having optics V -> A, how could i define this?

Suppose i have some

import monocle.macros.GenLens

case class DirState(opened: Boolean)

object DirState {
  val opened = GenLens[DirState](_.opened)
}

type Path = List[String]
type StateStore = Map[Path, DirState]

Next I encounter place where i need simple StateStore => StateStore, so I'm importing

import monocle._
import monocle.std._
import monocle.syntax._
import monocle.function._

And trying to define first:

def setOpened(path: Path): StateStore => StateStore = 
  at(path) composeLens DirState.opened set true

Getting here

ambiguous implicit values: both method atMap in trait MapInstances of type [K, V]=> monocle.function.At[Map[K,V],K,V] and method atSet in trait SetInstances of type [A]=> monocle.function.At[Set[A],A,Unit] match expected type monocle.function.At[S,Path,A]

Trying to change my definition to

def setOpened(path: Path): StateStore => StateStore =
  index(path) composeLens DirState.opened set true

Getting now:

type mismatch; found : monocle.function.Index[Map[Path,Nothing],Path,Nothing] (which expands to) monocle.function.Index[Map[List[String],Nothing],List[String],Nothing] required: monocle.function.Index[Map[Path,Nothing],Path,A] (which expands to) monocle.function.Index[Map[List[String],Nothing],List[String],A]

Note: Nothing <: A, but trait Index is invariant in type A. You may wish to define A as +A instead. (SLS 4.5)

2

There are 2 answers

0
Julien Truffaut On BEST ANSWER
import monocle.function.index._
import monocle.std.map._
import monocle.syntax._

def setOpened(path: Path)(s: StateStore): StateStore =
  (s applyOptional index(path) composeLens DirState.opened).set(true)

let's have a look at the type of index

def index[S, I, A](i: I)(implicit ev: Index[S, I, A]): Optional[S, A] = 
  ev.index(i)

trait Index[S, I, A] {
  def index(i: I): Optional[S, A]
}

So index summon an instance of type class Index of type Index[S, I, A]. This permits to use index for Map, List, Vector and so on.

The problem is that scala compiler needs to infer 3 types: S, I and A at the call site of index. I is easy, it is the type of the parameter you pass to index. However,S and A are only know when you call set.

The apply syntax has been created to guide type inference for such scenario, basically applyOptionalcaptures S which is Map[Path, DirState]. This gives enough information for the compiler to infer A =:= DirState.

0
Armin On