Skip to content

Instantly share code, notes, and snippets.

@milessabin
Created April 24, 2014 07:17
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save milessabin/11244675 to your computer and use it in GitHub Desktop.
Save milessabin/11244675 to your computer and use it in GitHub Desktop.
Lens inference in shapeless 2.0.0
import shapeless._
import ops.record.{ Selector, Updater }
import record.{ FieldType, field }
trait PathLens[T, P] {
type Elem
def get(t : T) : Elem
def set(t : T)(e : Elem) : T
}
object PathLens {
type Aux[T, P, E] = PathLens[T, P] { type Elem = E }
implicit def mkPathLens[T, R <: HList, P, E]
(implicit
gen: LabelledGeneric.Aux[T, R],
sel: Selector.Aux[R, P, E],
upd: Updater.Aux[R, FieldType[P, E], R]
): PathLens.Aux[T, P, E] =
new PathLens[T, P] {
type Elem = E
def get(t : T) : Elem = sel(gen.to(t))
def set(t : T)(e : E) : T = gen.from(upd(gen.to(t), field[P](e)))
}
}
object Demo extends App {
case class Person(name : String, age : Int)
case class Dog(name : String, age : Int)
def updateField[T, F, E](t: T, f: Witness)(e: E)(implicit lens: PathLens.Aux[T, f.T, E]): (T, E) = {
val old = lens.get(t)
val updated = lens.set(t)(e)
(updated, old)
}
val mary = Person("Mary", 23)
val boris = Dog("Boris", 5)
// Lens for Person at Int field `age` is inferred
val mary1 = updateField(mary, 'age)(24)
assert(mary1 == (Person("Mary", 24), 23))
// Lens for Dog at Int field `age` is inferred
val boris1 = updateField(boris, 'age)(6)
assert(boris1 == (Dog("Boris", 6), 5))
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment