Using Lenses With Scalaz 7

The release of Scalaz 7 is getting closer and closer every day and many projects are migrating to new version of this awesome library. This post is intended to help with migration of Scalaz’ lenses. As it is assumed that you are already familiar with lenses some basic changes and additions will be demonstrated by examples.

For our examples we will use the following simple model:

1
2
case class Address(city: String, zip: Int)
case class User(name: String, address: Address)
Reminder: out of the box field accessing/updating boilerplate
1
2
3
val user = User("Nad", Address("Sallad", 79071))
val updatedUser = user.copy(name = "Evets")
val updatedAddress = user.copy(address = user.address.copy(city = "Revned"))

The type signature of Lens is different (if not completely rewritten :)) in Scalaz 7:

What a Lens is in Scalaz 6
1
case class Lens[A,B](get: A => B, set: (A,B) => A)
What a Lens is in Scalaz 7
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
type Lens[A, B] = LensT[Id, A, B]

object Lens extends LensTFunctions with LensTInstances {
  def apply[A, B](r: A => Store[B, A]): Lens[A, B] =
    lens(r)
}

// A The type of the record
// B The type of the field
sealed trait LensT[F[+_], A, B] {
  def run(a: A): F[Store[B, A]]

  def apply(a: A): F[Store[B, A]] =
    run(a)
  ...
}

type Store[A, B] = StoreT[Id, A, B]
// flipped
type |-->[A, B] = Store[B, A]
object Store {
  def apply[A, B](f: A => B, a: A): Store[A, B] = StoreT.store(a)(f)
}

The constructor of Lens have been completely changed in Scalaz 7. Nevertheless there is a Lens.lensu constructor that takes the same but flipped arguments as the old one. So let’s create the lenses for user name, address and zip code (through address):

1
2
3
val nameL: Lens[User, String] = Lens.lensu((u, newName) => u.copy(name = newName), _.name)
val addrL: Lens[User, Address] = Lens.lensu((u, newAddr) => u.copy(address = newAddr), _.address)
val zipL: Lens[Address, Int] = Lens.lensu((a, newZip) => a.copy(zip = newZip), _.zip)

Now we can read and update user fields with appropriate lenses as before:

1
2
3
4
5
scala> nameL.get(user)  //reading user name (nameL(user) is not identical to nameL.get(user) anymore)
res0: scalaz.Id.Id[String] = Nad

scala> addrL.set(user, Address("Empty", 0))  // updating user address
res1: scalaz.Id.Id[User] = User(Nad,Address(Empty,0))

In contrast to set parameters of mod function have been flipped:

1
2
scala> zipL.mod((1+), user.address)  // modifying user zip code through address (user.address)
res2: scalaz.Id.Id[Address] = Address(Sallad,79072)

There is a useful addition to mod function called =>=:

1
2
3
4
5
scala> val partialMod = nameL =>= (_ + "!")
partialMod: User => scalaz.Id.Id[User] = <function1>

scala> partialMod(user)
res3: scalaz.Id.Id[User] = User(Nad!,Address(Sallad,79071))

In addition to compose and andThen functions there are aliases <=< and >=> (respectively):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
scala> zipL compose addrL
res4: scalaz.LensT[scalaz.Id.Id,User,Int] = scalaz.LensTFunctions$$anon$5@51557949

scala> zipL <=< addrL // just alias to compose function
res5: scalaz.LensT[scalaz.Id.Id,User,Int] = scalaz.LensTFunctions$$anon$5@3f1cf257

scala> val zipThroughUserL = addrL andThen zipL  // composing two lenses (Lens[User, Address] andThen Lens[Address, Int] = Lens[User, Int])
zipThroughUserL: scalaz.LensT[scalaz.Id.Id,User,Int] = scalaz.LensTFunctions$$anon$5@5c921914

scala> zipThroughUserL.mod((_ - 1), user)  // modifying user zip code through user itself with composed lenses
res6: scalaz.Id.Id[User] = User(Nad,Address(Sallad,79070))

scala> (addrL >=> zipL).mod((_ - 1), user) // the same as two previous lines
res7: scalaz.Id.Id[User] = User(Nad,Address(Sallad,79070))

Homomorphism of lens categories lets us to get a value as of Option[B] type (where B is the type of the field):

A homomorphism of lens categories
1
2
3
4
5
scala> nameL get user  // result is of type String
res8: scalaz.Id.Id[String] = Nad

scala> ~nameL get user  // but here the result is of type Option[String]!
res9: scalaz.Id.Id[Option[String]] = Some(Nad)

Using lens as a State monad (including map and flatMap as >- and >>- respectively):

Set the portion of the state viewed through the lens and return its new value
1
2
3
4
5
6
7
8
9
10
11
scala> val zipState = for {
     |   x <- zipL
     |   _ <- zipL := x + 1
     | } yield x
zipState: scalaz.StateT[scalaz.Id.Id,Address,Int] = scalaz.StateT$$anon$7@346d9067

scala> zipState.run(user.address)
res10: (Address, Int) = (Address(Sallad,79072),79071)

scala> zipState.eval(user.address)  // discard the final state
res11: scalaz.Id.Id[Int] = 79071
Modify the portion of the state viewed through the lens and return its new value
1
2
scala> (nameL %= (_ + "!")) run user
res12: (User, String) = (User(Nad!,Address(Sallad,79071)),Nad!)
Modify the portion of the state viewed through the lens, but do not return its new value
1
2
scala> (nameL %== (_ + "!")) run user
res13: (User, Unit) = (User(Nad!,Address(Sallad,79071)),())
Map the function over the value under the lens, as a state action
1
2
scala> (nameL >- (_.toUpperCase)) run user  // >- is an alias for map
res14: (User, java.lang.String) = (User(Nad,Address(Sallad,79071)),NAD)
Bind the function over the value under the lens, as a state action
1
2
3
4
val upNameL: Lens[User, String] = Lens.lensu((u, newName) => u.copy(name = newName.toUpperCase), _.name.toUpperCase) // yet another lens for user name

scala> (nameL >>- (_ => upNameL)) run user  // >>- is an alias for flatMap
res15: (User, String) = (User(Nad,Address(Sallad,79071)),NAD)
Sequence the monadic action of looking through the lens to occur before the state action
1
2
scala> nameL ->>- upNameL run user  // uses flatMap (>>-) for sequencing monadic actions
res16: (User, String) = (User(Nad,Address(Sallad,79071)),NAD)

Looking through the examples above it is not difficult to see that there is a bunch of new methods (and nice aliases) especially for working with a lens as a state monad. Since type complexity is under the hood working with lenses is even easier now than before. Waiting for Scalaz 7 release!

Slow Down Your DSL Recipe in Clojure

Recipe history

The recipe should be used for making your elegant (in/ex)ternal DSL code much more slower than it could be. It is said that using suggested ingredients correctly can make your code slower up to 10 times without lack of elegance and readability. (All the examples are based on hypothetical Porter (also called Porter 1) stemming algorithm implementation).

Ingredients: Clojure, understanding of partial function application and what’s under the hood of Clojure data types.

Step I: Parse strings manually when regular expressions could do the dirty work

Just try to figure out whether ‘Y’ letter is a consonant or not. Don’t use regexp. Ever. Did you crave for slower code? Now you have it. So think twice before using regular expressions with JVM-based languages because it can make your code much faster.

Step II: Use partial functions everywhere to beautify your DSL

For example in step 2 we could use the following rule definitions:

1
2
3
{:condition (m > 1) :suffix1 "logi" :suffix2 "log"}
{:condition (m > 1) :suffix1 "ational" :suffix2 "ate"}
;; and so on

where m is a function that takes two arguments, a function and a number, and returns the partial function which is waiting for the last argument (a given word in our case) and returns the measure of the word. Something like this:

1
2
3
(defn m
  [f x]
    (partial #(%1 (measure %3) %2) f x))

where measure takes the word and produces its measure. In spite of Clojure’s prefix notation we could easily make the readable infix expressions in our DSL. It looks nice, doesn’t it? Of course it does! And what’s more important for our recipe that it evaluates slower than it could do! Of course we could avoid it and write something like this:

1
2
3
4
5
(let [m-cond (m > 1)]
  (apply to-given-word
    {:condition m-cond :suffix1 "logi" :suffix2 "log"}
    {:condition m-cond :suffix1 "logi" :suffix2 "log"}
    ;; and so on ))

It’s always faster but not so “DSLish” as the initial example.

Step III: Don’t use partial functions with map, filter and so on.

What? “To use or not to use [partial function application]” you might be asking. As always the best way is to show it by example. Imagine that we have some data structure, let’s say a vector of letters:

1
(def letters [\a \b \c \d \e])

So if we want to check if there are any letters in the other vector that equal to the last letter of the existing vector we could use the built-in function some like this:

1
(some #(= (peek x) %) [\f \g \h \i \j])

In our case it is just a great solution because it is slow :). As you might guess one of the approaches to make it faster (up to 3 times) is to use partial function instead of our anonymous function:

1
(some (partial = (peek x)) [\f \g \h \i \j])

(And yes, peek is faster than last for vectors).

Step IV: Use hashes as frequently as you can

Here is the example of rule definitions you have already seen before:

1
2
3
{:condition (m > 1) :suffix1 "logi" :suffix2 "log"}
{:condition (m > 1) :suffix1 "ational" :suffix2 "ate"}
;; and so on

You already know what to do (or not) with these (m > 1) expressions. I’m sure you know better how to make a DSL slower and the recipe tastier. But it’s not what makes the code much much slower. That’s our last ingredient - curly braces! Of course not as some additional characters or tokens in our expression but as a data structure behind it. Good Old The Java Hash Map. As using curly braces in Clojure is much more pleasant than creating these cumbersome definitions in Java (Map<Symbol, Object> rule = new HashMap<Symbol, Object>(); vs {}) it creates the illusion of some kind of magic under the hood. The trick is that it’s still The Java Hash Map and creating a bunch of them in our DSL is what makes it much much slower than it could be. So as you can see the rule is pretty simple - just create as many hashes in the form of curly braces as possible to kill the performance of any DSL.