Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
512 changes: 512 additions & 0 deletions core/src/main/java/fj/data/Lens.java

Large diffs are not rendered by default.

113 changes: 113 additions & 0 deletions demo/src/main/java/fj/demo/Lens_demo.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
package fj.demo;

import fj.P;
import fj.P2;
import fj.data.Lens;
import fj.data.State;

public final class Lens_demo {

static final class Point {
private final double x;
private final double y;

Point(final double x, final double y) {
this.x = x;
this.y = y;
}

double x() {
return x;
}

Point withX(final double newX) {
return new Point(newX, y);
}

double y() {
return y;
}

Point withY(final double newY) {
return new Point(x, newY);
}

@Override
public String toString() {
return String.format("Point{x=%s, y=%s}", x, y);
}

static final Lens<Point, Double> X =
Lens.lens(Point::x, Point::withX);

static final Lens<Point, Double> Y =
Lens.lens(Point::y, Point::withY);
}

static final class Turtle {
private final Point position;
private final double heading;

Turtle(final Point position, final double heading) {
this.position = position;
this.heading = heading;
}

Point position() {
return position;
}

Turtle withPosition(final Point newPosition) {
return new Turtle(newPosition, heading);
}

double heading() {
return heading;
}

Turtle withHeading(final double newHeading) {
return new Turtle(position, newHeading);
}

@Override
public String toString() {
return String.format("Turtle{position=%s, heading=%s}", position, heading);
}

static final Lens<Turtle, Point> Position =
Lens.lens(Turtle::position, Turtle::withPosition);

static final Lens<Turtle, Double> Heading =
Lens.lens(Turtle::heading, Turtle::withHeading);

static final Lens<Turtle, Double> X =
Turtle.Position.andThen(Point.X);

static final Lens<Turtle, Double> Y =
Turtle.Position.andThen(Point.Y);

static State<Turtle, P2<Double, Double>> forward(final double dist) {
return Heading.flatMap(
heading -> X.modS(x -> x + dist * Math.cos(heading)).flatMap(
x -> Y.modS(y -> y + dist * Math.sin(heading)).map(
y -> P.p(x, y))));
}

static Turtle forward(final Turtle t, final double dist) {
return forward(dist).exec(t);
}
}

public static void main(final String[] args) {
final Turtle t0 = new Turtle(new Point(1.0, 2.0), 0.0);

System.out.println("Turtle.X.get(t0) = " + Turtle.X.get(t0));
System.out.println("Turtle.X.set(t0, 4.0) = " + Turtle.X.set(t0, 4.0));
System.out.println("Turtle.X.mod(x -> x + 3.0, t0) = " + Turtle.X.mod(x -> x + 3.0, t0));
System.out.println("Turtle.X.mod(x -> x + 3.0).f(t0) = " + Turtle.X.mod(x -> x + 3.0).f(t0));
System.out.println("Turtle.X.modS(x -> x + 3.0).exec(t0) = " + Turtle.X.modS(x -> x + 3.0).exec(t0));
System.out.println("Turtle.X.modS(x -> x + 3.0).run(t0) = " + Turtle.X.modS(x -> x + 3.0).run(t0));
System.out.println("Turtle.forward(t0, 10.0) = " + Turtle.forward(t0, 10.0));
System.out.println("Turtle.forward(13.37).eval(t0) = " + Turtle.forward(13.37).eval(t0));
}
}
8 changes: 7 additions & 1 deletion tests/src/test/scala/fj/ArbitraryP.scala
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,10 @@ object ArbitraryP {
Arbitrary(arbitrary[A].map(a => new P1[A]{
def _1 = a
}))
}

implicit def arbitraryP2[A: Arbitrary, B: Arbitrary]: Arbitrary[P2[A, B]] =
Arbitrary(arbitrary[A].flatMap(a => arbitrary[B].map(b => new P2[A, B]{
def _1 = a
def _2 = b
})))
}
1 change: 1 addition & 0 deletions tests/src/test/scala/fj/Tests.scala
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ object Tests {
fj.data.CheckArray.properties,
fj.data.CheckIO.properties,
fj.data.CheckIteratee.properties,
fj.data.CheckLens.properties,
fj.data.CheckList.properties,
fj.data.CheckStream.properties,
fj.data.CheckOption.properties,
Expand Down
17 changes: 17 additions & 0 deletions tests/src/test/scala/fj/data/ArbitraryAnyVal.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package fj
package data

import org.scalacheck.Arbitrary
import org.scalacheck.Arbitrary._
import org.scalacheck.Gen._

object ArbitraryAnyVal {
implicit val arbitraryUnit: Arbitrary[Unit] =
Arbitrary(value(Unit.unit()))

implicit val arbitraryInteger: Arbitrary[java.lang.Integer] =
Arbitrary(arbitrary[Int].map(x ⇒ x: java.lang.Integer))

implicit val arbitraryBoolean: Arbitrary[java.lang.Boolean] =
Arbitrary(arbitrary[Boolean].map(b => b: java.lang.Boolean))
}
13 changes: 13 additions & 0 deletions tests/src/test/scala/fj/data/ArbitraryEither.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package fj
package data

import fj.data.Either.{left, right}
import org.scalacheck.Arbitrary
import org.scalacheck.Arbitrary._
import org.scalacheck.Gen._


object ArbitraryEither {
implicit def arbitraryEither[A: Arbitrary, B: Arbitrary]: Arbitrary[Either[A, B]] =
Arbitrary(sized(n ⇒ if (n == 0) arbitrary[A].map(left[A, B]) else resize(n - 1, arbitrary[B].map(right[A, B]))))
}
10 changes: 10 additions & 0 deletions tests/src/test/scala/fj/data/ArbitraryLens.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package fj
package data

import org.scalacheck.Arbitrary
import org.scalacheck.Gen.value

object ArbitraryLens {
implicit def arbitratyLens: Arbitrary[Lens[Int, Int]] =
Arbitrary(value(Lens.id[Int]()))
}
54 changes: 54 additions & 0 deletions tests/src/test/scala/fj/data/CheckLens.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package fj
package data

import ArbitraryAnyVal._
import ArbitraryEither._
import ArbitraryLens._
import ArbitraryOption._
import ArbitraryP._
import ArbitrarySet._
import ArbitraryTreeMap._
import EqualInstances._
import OrdInstances._
import org.scalacheck.Arbitrary._
import org.scalacheck.Prop._
import org.scalacheck.{Arbitrary, Properties}

object CheckLens extends Properties("Lens") {

property("associative") = forAll { (ab: Lens[Int, Int], bc: Lens[Int, Int], cd: Lens[Int, Int]) ⇒
val ad1 = cd.compose(bc.compose(ab))
val ad2 = cd.compose(bc).compose(ab)
implicitly[Equal[Lens[Int, Int]]].eq(ad1, ad2)
}

property("left identity") = forAll { (ab: Lens[Int, Int]) ⇒
val ab1 = ab.compose(Lens.id())
implicitly[Equal[Lens[Int, Int]]].eq(ab, ab1)
}

property("left identity") = forAll { (ab: Lens[Int, Int]) ⇒
val ab1 = Lens.id().compose(ab)
implicitly[Equal[Lens[Int, Int]]].eq(ab, ab1)
}

include(LensLaws("id", Lens.id[Int]()))
include(LensLaws("trivial", Lens.trivial[Int]()))
include(LensLaws("codiag", Lens.codiag[Int]()))
include(LensLaws("P2.first", Lens.first[Int, Int]()))
include(LensLaws("P2.second", Lens.second[Int, Int]()))
include(LensLaws("Map.member", Lens.mapValue[Boolean, Int](true)))
include(LensLaws("Set.member", Lens.setMembership[Int](0)))
include(LensLaws("sum", Lens.first[Int, String]().sum(Lens.first[Int, String]())))

case class LensLaws[A, B](override val name: String, l: Lens[A, B])(implicit A: Arbitrary[A], B: Arbitrary[B], EA: Equal[A], EB: Equal[B]) extends Properties(name) {
property("identity") = forAll((a: A) ⇒
EA.eq(l.set(a, l.get(a)), a))

property("retention") = forAll((a: A, b: B) ⇒
EB.eq(l.get(l.set(a, b)), b))

property("doubleSet") = forAll((a: A, b1: B, b2: B) ⇒
EA.eq(l.set(l.set(a, b1), b2), l.set(a, b2)))
}
}
46 changes: 46 additions & 0 deletions tests/src/test/scala/fj/data/EqualInstances.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package fj
package data


object EqualInstances {

implicit val equalLens: Equal[Lens[Int, Int]] =
Equal.equal[Lens[Int, Int]]((a1: Lens[Int, Int]) ⇒ (a2: Lens[Int, Int]) ⇒
a1.get(0) == a2.get(0): java.lang.Boolean)

implicit val equalBoolean: Equal[java.lang.Boolean] =
Equal.booleanEqual

implicit val equalInt: Equal[Int] =
Equal.intEqual.comap[Int]((x: Int) ⇒ x: java.lang.Integer)

implicit val equalString: Equal[String] =
Equal.stringEqual

implicit val equalUnit: Equal[Unit] =
Equal.equal[Unit]((u1: Unit) ⇒ (u2: Unit) ⇒ true: java.lang.Boolean)

implicit def equalP2[A: Equal, B: Equal]: Equal[P2[A, B]] =
Equal.p2Equal(implicitly, implicitly)

implicit def equalEither[A: Equal, B: Equal]: Equal[Either[A, B]] =
Equal.eitherEqual(implicitly, implicitly)

implicit def equalOption[A: Equal]: Equal[Option[A]] =
Equal.optionEqual(implicitly)

implicit def equalSet[A: Equal]: Equal[Set[A]] =
Equal.setEqual(implicitly)

implicit def equalTreeMap[K: Ord, V: Equal]: Equal[TreeMap[K, V]] =
Equal.listEqual(Equal.p2Equal[K, V](implicitly[Ord[K]].equal(), implicitly))
.comap[TreeMap[K, V]]((m: TreeMap[K, V]) ⇒ map2list(m))

private def map2list[K, V](m: TreeMap[K, V]) = {
val buffer = List.Buffer.empty[P2[K, V]]()
val iter = m.iterator()
while(iter.hasNext)
buffer.snoc(iter.next())
buffer.toList
}
}
11 changes: 11 additions & 0 deletions tests/src/test/scala/fj/data/OrdInstances.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package fj
package data

object OrdInstances {

implicit val ordBoolean: Ord[Boolean] =
Ord.booleanOrd.comap[Boolean]((b: Boolean) ⇒ b: java.lang.Boolean)

implicit val ordInt: Ord[Int] =
Ord.intOrd.comap[Int]((i: Int) ⇒ i: java.lang.Integer)
}
8 changes: 7 additions & 1 deletion tests/src/test/scala/fj/package.scala
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,15 @@ package object fj {
def f(a: A) = g(a)
}

implicit def Function1CF[A, B, C](g: A => B => C): F[A, F[B, C]] = new F[A, F[B, C]] {
def f(a: A): F[B, C] = new F[B, C] {
def f(b: B): C = g(a)(b)
}
}

implicit def Function2F[A, B, C](g: (A, B) => C): F[A, F[B, C]] = new F[A, F[B, C]] {
def f(a: A) = new F[B, C] {
def f(b: B) = g(a, b)
}
}
}
}