Skip to content

Instantly share code, notes, and snippets.

@aoiroaoino
Last active July 15, 2021 08:14
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save aoiroaoino/854213db3f65aa182a4864451e501657 to your computer and use it in GitHub Desktop.
Save aoiroaoino/854213db3f65aa182a4864451e501657 to your computer and use it in GitHub Desktop.
Lens の Getter, Setter
trait Functor[F[_]] {
def map[A, B](fa: F[A])(f: A => B): F[B]
}
type Id[A] = A
object Id {
implicit def idFunctor = new Functor[Id] {
def map[A, B](fa: Id[A])(f: A => B): Id[B] = f(fa)
}
}
type Const[A, B] = B
object Const {
implicit def constFunctor[C] = new Functor[({type F[X] = Const[X, C]})#F] {
def map[A, B](fa: Const[A, C])(f: A => B): Const[B, C] = fa
}
}
type Lens[F[_], S, A] = (A => F[A]) => S => F[S]
def lens[F[_]: Functor, S, A](get: S => A)(set: S => A => S): Lens[F, S, A] =
afa => s => implicitly[Functor[F]].map(afa(get(s)))(set(s))
type Getter[S, A] = Lens[({type F[X] = Const[X, A]})#F, S, A]
type Setter[S, A] = Lens[Id, S, A]
case class User(id: Int, name: String, address: Address)
case class Address(street: String)
def nameLens[F[_]: Functor]: Lens[F, User, String] =
lens[F, User, String](_.name)(u => n => u.copy(name = n))
def addressLens[F[_]: Functor]: Lens[F, User, Address] =
lens[F, User, Address](_.address)(u => a => u.copy(address = a))
def streetLens[F[_]: Functor]: Lens[F, Address, String] =
lens[F, Address, String](_.street)(a => s => a.copy(street = s))
def adressStreetLens[F[_]: Functor]: Lens[F, User, String] =
addressLens compose streetLens
scala> import Id._, Const._
|
| def getName: Getter[User, String] = nameLens[({type F[X] = Const[X, String]})#F]
| def setName: Setter[User, String] = nameLens[Id]
|
| val user = User(42, "John", Address("street"))
import Id._
import Const._
def getName: Getter[User,String]
def setName: Setter[User,String]
val user: User = User(42,John,Address(street))
scala> getName(identity)(user)
val res0: String = John
scala> setName(_.toUpperCase)(user)
val res1: Id[User] = User(42,JOHN,Address(street))
scala> adressStreetLens[Id](implicitly)(_.toUpperCase)(user)
val res2: Id[User] = User(42,John,Address(STREET))
scala> adressStreetLens[({type F[X] = Const[X, String]})#F](implicitly)(identity)(user)
val res3: String = street
trait Functor[F[_]] {
def map[A, B](fa: F[A])(f: A => B): F[B]
}
type Id[A] = A
object Id {
given Functor[Id] with
def map[A, B](fa: Id[A])(f: A => B): Id[B] = f(fa)
}
type Const[A, C] = C
object Const {
given [C]: Functor[[X] =>> Const[X, C]] with
def map[A, B](fa: Const[A, C])(f: A => B): Const[B, C] = fa
}
type Lens[S, A] = [F[_]] => Functor[F] ?=> (A => F[A]) => S => F[S]
def lens[S, A](get: S => A)(set: S => A => S): Lens[S, A] =
[F[_]] => (F: Functor[F]) ?=>
(afa: A => F[A]) => (s: S) => F.map(afa(get(s)))(set(s))
def get[S, A](lens: Lens[S, A]): S => A = lens[[X] =>> Const[X, A]](identity)
def set[S, A](lens: Lens[S, A]): A => S => S = a => lens[Id](_ => a)
extension [S, A](lens: Lens[S, A]) {
def get: S => A = lens[[X] =>> Const[X, A]](identity)
def set: A => S => S = a => lens[Id](_ => a)
}
// ===
case class User(id: Int, name: String, address: Address)
case class Address(street: String)
def address: Lens[User, Address] =
lens[User, Address](_.address)(u => a => u.copy(address = a))
def street: Lens[Address, String] =
lens[Address, String](_.street)(a => s => a.copy(street = s))
def addressStreet: Lens[User, String] =
[F[_]] => (F: Functor[F]) ?=>
address[F] compose street[F]
// ===
scala> val user = User(42, "John", Address("street"))
| {
| import Id.given, Const.given
| println(addressStreet[[X] =>> Const[X, String]](identity)(user))
| println(addressStreet[Id](_.toUpperCase)(user))
| }
street
User(42,John,Address(STREET))
val user: User = User(42,John,Address(street))
scala> {
| import Id.given, Const.given
| println( get(addressStreet)(user) )
| println( set(addressStreet)("foobar")(user) )
| }
street
User(42,John,Address(foobar))
scala> {
| import Id.given, Const.given
| println( addressStreet.get(user) )
| println( addressStreet.set("foobar")(user) )
| }
street
User(42,John,Address(foobar))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment