diff --git a/CHANGELOG.md b/CHANGELOG.md index 296107a18..c61bcb6c0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,17 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/). ## [Unreleased] +### Added +- `IterableLens#mapping`, an `Iso` that maps values + +### Changed +- `TypeSafeKey.Simple` now has a default `#apply` implementation + +### Fixed +- mapped `TypeSafeKey` instances can be used for initial put in an `HMap`, and the base key can be used to retrieve them +- Merged pull request fixing issue storing values at mapped `TypeSafeKey` in `singletonHMap` + +## [3.0.1] - 2018-05-13 ### Changed - `ToMap` accepts an `Iterable` covariant in `Map.Entry` - `RecursiveResult#invert` is also a `RecursiveResult` @@ -312,7 +323,8 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/). - `Monadic/Dyadic/TriadicFunction`, `Predicate`, `Tuple2`, `Tuple3` - `Functor`, `BiFunctor`, `ProFunctor` -[Unreleased]: https://github.com/palatable/lambda/compare/lambda-3.0.0...HEAD +[Unreleased]: https://github.com/palatable/lambda/compare/lambda-3.0.1...HEAD +[3.0.1]: https://github.com/palatable/lambda/compare/lambda-3.0.0...lambda-3.0.1 [3.0.0]: https://github.com/palatable/lambda/compare/lambda-2.1.1...lambda-3.0.0 [2.1.1]: https://github.com/palatable/lambda/compare/lambda-2.1.0...lambda-2.1.1 [2.1.0]: https://github.com/palatable/lambda/compare/lambda-2.0.0...lambda-2.1.0 diff --git a/README.md b/README.md index 5d8eff2e7..a08d8dcc8 100644 --- a/README.md +++ b/README.md @@ -57,14 +57,14 @@ Add the following dependency to your: com.jnape.palatable lambda - 3.0.0 + 3.0.1 ``` `build.gradle` ([Gradle](https://docs.gradle.org/current/userguide/dependency_management.html)): ```gradle -compile group: 'com.jnape.palatable', name: 'lambda', version: '3.0.0' +compile group: 'com.jnape.palatable', name: 'lambda', version: '3.0.1' ``` Examples diff --git a/pom.xml b/pom.xml index 21483d5fe..ac48a3a91 100644 --- a/pom.xml +++ b/pom.xml @@ -9,7 +9,7 @@ lambda - 3.0.1 + 3.0.2 jar Lambda diff --git a/src/main/java/com/jnape/palatable/lambda/adt/hmap/HMap.java b/src/main/java/com/jnape/palatable/lambda/adt/hmap/HMap.java index 83fc47f11..e2e3fd3c1 100644 --- a/src/main/java/com/jnape/palatable/lambda/adt/hmap/HMap.java +++ b/src/main/java/com/jnape/palatable/lambda/adt/hmap/HMap.java @@ -196,7 +196,7 @@ public static HMap emptyHMap() { * @return a singleton HMap */ public static HMap singletonHMap(TypeSafeKey key, V value) { - return new HMap(singletonMap(key, value)); + return emptyHMap().put(key, value); } /** diff --git a/src/main/java/com/jnape/palatable/lambda/adt/hmap/TypeSafeKey.java b/src/main/java/com/jnape/palatable/lambda/adt/hmap/TypeSafeKey.java index a71e4c04c..8bae70949 100644 --- a/src/main/java/com/jnape/palatable/lambda/adt/hmap/TypeSafeKey.java +++ b/src/main/java/com/jnape/palatable/lambda/adt/hmap/TypeSafeKey.java @@ -6,6 +6,8 @@ import com.jnape.palatable.lambda.lens.Iso; import com.jnape.palatable.lambda.lens.LensLike; +import java.util.Objects; + /** * An interface representing a parametrized key for use in {@link HMap}s. Additionally, every {@link TypeSafeKey} is an * {@link Iso} from the type the value is stored as to the type it's viewed and set as (on the way in / on the way out). @@ -88,10 +90,8 @@ public boolean equals(Object obj) { static Simple typeSafeKey() { return new TypeSafeKey.Simple() { @Override - @SuppressWarnings("unchecked") - public

, FT extends Functor, PAFB extends Profunctor, PSFT extends Profunctor> PSFT apply( - PAFB pafb) { - return (PSFT) pafb; + public boolean equals(Object obj) { + return obj instanceof Simple ? this == obj : Objects.equals(obj, this); } }; } @@ -102,5 +102,12 @@ public

, FT ex * @param The type of the value that this key maps to inside an {@link HMap} */ interface Simple extends TypeSafeKey { + + @Override + @SuppressWarnings("unchecked") + default

, FT extends Functor, PAFB extends Profunctor, PSFT extends Profunctor> PSFT apply( + PAFB pafb) { + return (PSFT) pafb; + } } } \ No newline at end of file diff --git a/src/main/java/com/jnape/palatable/lambda/lens/lenses/IterableLens.java b/src/main/java/com/jnape/palatable/lambda/lens/lenses/IterableLens.java index 0c765505d..cccf68730 100644 --- a/src/main/java/com/jnape/palatable/lambda/lens/lenses/IterableLens.java +++ b/src/main/java/com/jnape/palatable/lambda/lens/lenses/IterableLens.java @@ -3,12 +3,16 @@ import com.jnape.palatable.lambda.adt.Maybe; import com.jnape.palatable.lambda.functions.builtin.fn1.Head; import com.jnape.palatable.lambda.functions.builtin.fn1.Tail; +import com.jnape.palatable.lambda.lens.Iso; import com.jnape.palatable.lambda.lens.Lens; import static com.jnape.palatable.lambda.functions.Fn2.fn2; import static com.jnape.palatable.lambda.functions.builtin.fn1.Id.id; import static com.jnape.palatable.lambda.functions.builtin.fn2.Cons.cons; +import static com.jnape.palatable.lambda.functions.builtin.fn2.Map.map; +import static com.jnape.palatable.lambda.lens.Iso.simpleIso; import static com.jnape.palatable.lambda.lens.Lens.simpleLens; +import static com.jnape.palatable.lambda.lens.functions.View.view; /** * Lenses that operate on {@link Iterable}s. @@ -43,4 +47,16 @@ public static Lens.Simple, Maybe> head() { public static Lens.Simple, Iterable> tail() { return simpleLens(Tail::tail, fn2(Head.head().andThen(o -> o.fmap(cons()).orElse(id()))).toBiFunction()); } + + /** + * An iso focusing on the mapped values of an {@link Iterable}. + * + * @param abIso the iso from A to B + * @param the unmapped {@link Iterable} element type + * @param the mapped {@link Iterable} element type + * @return an iso that maps {@link Iterable}<A> to {@link Iterable}<B> + */ + public static Iso.Simple, Iterable> mapping(Iso abIso) { + return simpleIso(map(view(abIso)), map(view(abIso.mirror()))); + } } diff --git a/src/test/java/com/jnape/palatable/lambda/adt/hmap/HMapTest.java b/src/test/java/com/jnape/palatable/lambda/adt/hmap/HMapTest.java index 8495b059f..65db8ee50 100644 --- a/src/test/java/com/jnape/palatable/lambda/adt/hmap/HMapTest.java +++ b/src/test/java/com/jnape/palatable/lambda/adt/hmap/HMapTest.java @@ -2,6 +2,7 @@ import org.junit.Test; +import java.math.BigInteger; import java.util.HashMap; import java.util.NoSuchElementException; @@ -12,6 +13,8 @@ import static com.jnape.palatable.lambda.adt.hmap.HMap.hMap; import static com.jnape.palatable.lambda.adt.hmap.HMap.singletonHMap; import static com.jnape.palatable.lambda.adt.hmap.TypeSafeKey.typeSafeKey; +import static com.jnape.palatable.lambda.lens.Iso.simpleIso; +import static java.math.BigInteger.ONE; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotEquals; @@ -36,6 +39,28 @@ public void getForAbsentKey() { .get(typeSafeKey())); } + @Test + public void storesTypeSafeKeyBaseValue() { + TypeSafeKey.Simple stringKey = typeSafeKey(); + TypeSafeKey longKey = stringKey.andThen(simpleIso(Long::parseLong, String::valueOf)); + TypeSafeKey bigIntegerKey = longKey.andThen(simpleIso(BigInteger::valueOf, BigInteger::longValue)); + + HMap hMap = singletonHMap(stringKey, "1"); + assertEquals(just("1"), hMap.get(stringKey)); + assertEquals(just(1L), hMap.get(longKey)); + assertEquals(just(ONE), hMap.get(bigIntegerKey)); + + assertNotEquals(typeSafeKey(), typeSafeKey()); + + assertEquals(emptyHMap().put(longKey, 1L).get(longKey), emptyHMap().put(stringKey, "1").get(longKey)); + assertEquals(emptyHMap().put(stringKey, "1").get(stringKey), emptyHMap().put(longKey, 1L).get(stringKey)); + assertEquals(emptyHMap().put(stringKey, "1").get(stringKey), emptyHMap().put(bigIntegerKey, ONE).get(stringKey)); + + assertEquals(singletonHMap(stringKey, "1"), singletonHMap(longKey, 1L)); + assertEquals(singletonHMap(stringKey, "1"), singletonHMap(bigIntegerKey, ONE)); + assertEquals(singletonHMap(longKey, 1L), singletonHMap(bigIntegerKey, ONE)); + } + @Test public void getForPresentKeyWithNullValue() { TypeSafeKey stringKey = typeSafeKey(); diff --git a/src/test/java/com/jnape/palatable/lambda/adt/hmap/TypeSafeKeyTest.java b/src/test/java/com/jnape/palatable/lambda/adt/hmap/TypeSafeKeyTest.java index 577902c8c..b9bb7e802 100644 --- a/src/test/java/com/jnape/palatable/lambda/adt/hmap/TypeSafeKeyTest.java +++ b/src/test/java/com/jnape/palatable/lambda/adt/hmap/TypeSafeKeyTest.java @@ -9,6 +9,7 @@ import static com.jnape.palatable.lambda.lens.Iso.simpleIso; import static java.util.Arrays.asList; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotEquals; import static testsupport.assertion.LensAssert.assertLensLawfulness; @@ -45,4 +46,29 @@ public void discardRPreservesTypeSafeKey() { assertEquals(just("123"), map.get(discardedKey)); } + + @Test + public void defaultEquality() { + TypeSafeKey.Simple keyA = typeSafeKey(); + TypeSafeKey mappedKeyA = keyA.andThen(simpleIso(id(), id())); + + assertEquals(keyA, keyA); + assertEquals(keyA, mappedKeyA); + assertEquals(mappedKeyA, keyA); + assertEquals(keyA.hashCode(), mappedKeyA.hashCode()); + + TypeSafeKey.Simple keyB = typeSafeKey(); + assertNotEquals(keyA, keyB); + assertNotEquals(keyB, keyA); + assertNotEquals(keyB, mappedKeyA); + assertNotEquals(mappedKeyA, keyB); + + TypeSafeKey differentMappedKeyA = keyA.andThen(simpleIso(id(), id())); + assertEquals(keyA, differentMappedKeyA); + assertEquals(differentMappedKeyA, keyA); + assertEquals(mappedKeyA, differentMappedKeyA); + assertEquals(differentMappedKeyA, mappedKeyA); + assertEquals(keyA.hashCode(), differentMappedKeyA.hashCode()); + assertEquals(mappedKeyA.hashCode(), differentMappedKeyA.hashCode()); + } } \ No newline at end of file diff --git a/src/test/java/com/jnape/palatable/lambda/lens/lenses/IterableLensTest.java b/src/test/java/com/jnape/palatable/lambda/lens/lenses/IterableLensTest.java index ff5e30327..561bc4d99 100644 --- a/src/test/java/com/jnape/palatable/lambda/lens/lenses/IterableLensTest.java +++ b/src/test/java/com/jnape/palatable/lambda/lens/lenses/IterableLensTest.java @@ -1,12 +1,14 @@ package com.jnape.palatable.lambda.lens.lenses; import com.jnape.palatable.lambda.adt.Maybe; +import com.jnape.palatable.lambda.lens.Iso; import com.jnape.palatable.lambda.lens.Lens; import org.junit.Test; import static com.jnape.palatable.lambda.adt.Maybe.just; import static com.jnape.palatable.lambda.adt.Maybe.nothing; import static com.jnape.palatable.lambda.functions.builtin.fn2.Map.map; +import static com.jnape.palatable.lambda.lens.Iso.simpleIso; import static com.jnape.palatable.lambda.lens.functions.Over.over; import static com.jnape.palatable.lambda.lens.functions.Set.set; import static com.jnape.palatable.lambda.lens.functions.View.view; @@ -51,4 +53,19 @@ public void tail() { assertThat(over(tail, map(x -> x + 1), emptyList()), isEmpty()); assertThat(over(tail, map(x -> x + 1), asList(1, 2, 3)), iterates(1, 3, 4)); } + + @Test + public void mapping() { + Iso.Simple, Iterable> iso = IterableLens.mapping(simpleIso(Integer::parseInt, Object::toString)); + + assertThat(view(iso, emptyList()), isEmpty()); + assertThat(view(iso, singletonList("1")), iterates(1)); + assertThat(view(iso, asList("1", "2", "3")), iterates(1, 2, 3)); + + assertThat(set(iso, emptyList(), emptyList()), isEmpty()); + assertThat(set(iso, singletonList(1), emptyList()), iterates("1")); + assertThat(set(iso, singletonList(2), singletonList("1")), iterates("2")); + assertThat(set(iso, asList(1, 2, 3), singletonList("1")), iterates("1", "2", "3")); + assertThat(set(iso, emptyList(), singletonList("1")), isEmpty()); + } } \ No newline at end of file