Last active
July 15, 2021 08:14
-
-
Save aoiroaoino/854213db3f65aa182a4864451e501657 to your computer and use it in GitHub Desktop.
Lens の Getter, Setter
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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