Skip to content

Commit

Permalink
CU-g52kvp - Fix Java API for typeclasses in Arrow Optics (#2336)
Browse files Browse the repository at this point in the history
* Move std.list functions into each typeclass companion object with JvmStatic annotation and remove imports from deprecations

* Move std.either functions into each typeclass companion object with JvmStatic annotation and remove imports from deprecations

* Move std.map functions into each typeclass companion object with JvmStatic annotation and remove imports from deprecations

* Move std.nonemptylist functions into each typeclass companion object with JvmStatic annotation and remove imports from deprecations

* Move std.option functions into each typeclass companion object with JvmStatic annotation and remove imports from deprecations

* Move std.sequence functions into each typeclass companion object with JvmStatic annotation and remove imports from deprecations

* Move std.set functions into each typeclass companion object with JvmStatic annotation and remove imports from deprecations

* Move std.string functions into each typeclass companion object with JvmStatic annotation and remove imports from deprecations

* Move std.validated functions into each typeclass companion object with JvmStatic annotation and remove imports from deprecations

* Move std.tuple functions into each typeclass companion object with JvmStatic annotation and remove imports from deprecations

* Fix docs

* Double check 0.11.0.1 and address review comments

* Fix imports and tests

* Fix docs

* Apply comments

* update tests

* Update arrow-libs/optics/arrow-optics/src/main/kotlin/arrow/optics/std/either.kt

Co-authored-by: Simon Vergauwen <nomisRev@users.noreply.github.com>

Co-authored-by: Simon Vergauwen <nomisRev@users.noreply.github.com>
  • Loading branch information
danimontoya and nomisRev committed Mar 26, 2021
1 parent 0f83b12 commit 0af226b
Show file tree
Hide file tree
Showing 80 changed files with 1,365 additions and 609 deletions.
126 changes: 121 additions & 5 deletions arrow-libs/optics/arrow-optics/src/main/kotlin/arrow/optics/Iso.kt
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,15 @@ package arrow.optics
import arrow.Kind
import arrow.KindDeprecation
import arrow.core.Either
import arrow.core.NonEmptyList
import arrow.core.None
import arrow.core.Option
import arrow.core.Right
import arrow.core.Some
import arrow.core.Tuple2
import arrow.core.Validated
import arrow.core.Validated.Invalid
import arrow.core.Validated.Valid
import arrow.core.compose
import arrow.core.identity
import arrow.core.toT
Expand All @@ -15,13 +20,16 @@ import arrow.typeclasses.Functor
import arrow.typeclasses.Monoid

@Deprecated(KindDeprecation)
class ForPIso private constructor() { companion object }
class ForPIso private constructor() {
companion object
}
@Deprecated(KindDeprecation)
typealias PIsoOf<S, T, A, B> = arrow.Kind4<ForPIso, S, T, A, B>
@Deprecated(KindDeprecation)
typealias PIsoPartialOf<S, T, A> = arrow.Kind3<ForPIso, S, T, A>
@Deprecated(KindDeprecation)
typealias PIsoKindedJ<S, T, A, B> = arrow.HkJ4<ForPIso, S, T, A, B>

@Suppress("UNCHECKED_CAST", "NOTHING_TO_INLINE")
@Deprecated(KindDeprecation)
inline fun <S, T, A, B> PIsoOf<S, T, A, B>.fix(): PIso<S, T, A, B> =
Expand All @@ -38,6 +46,12 @@ typealias IsoOf<S, A> = PIsoOf<S, S, A, A>
typealias IsoPartialOf<S> = Kind<ForIso, S>
typealias IsoKindedJ<S, A> = PIsoKindedJ<S, S, A, A>

private val stringToList: Iso<String, List<Char>> =
Iso(
get = CharSequence::toList,
reverseGet = { it.joinToString(separator = "") }
)

/**
* An [Iso] is a loss less invertible optic that defines an isomorphism between a type [S] and [A]
* i.e. a data class and its properties represented by TupleN
Expand Down Expand Up @@ -83,6 +97,107 @@ interface PIso<S, T, A, B> : PIsoOf<S, T, A, B> {

override fun reverseGet(b: B): T = reverseGet(b)
}

/**
* [PIso] that defines equality between a [List] and [Option] [NonEmptyList]
*/
@JvmStatic
fun <A, B> listToPOptionNel(): PIso<List<A>, List<B>, Option<NonEmptyList<A>>, Option<NonEmptyList<B>>> =
PIso(
get = { aas -> if (aas.isEmpty()) None else Some(NonEmptyList(aas.first(), aas.drop(1))) },
reverseGet = { optNel -> optNel.fold({ emptyList() }, NonEmptyList<B>::all) }
)

/**
* [Iso] that defines equality between a [List] and [Option] [NonEmptyList]
*/
@JvmStatic
fun <A> listToOptionNel(): Iso<List<A>, Option<NonEmptyList<A>>> =
listToPOptionNel()

/**
* [PIso] that defines the equality between [Either] and [Validated]
*/
@JvmStatic
fun <A1, A2, B1, B2> eitherToPValidated(): PIso<Either<A1, B1>, Either<A2, B2>, Validated<A1, B1>, Validated<A2, B2>> =
PIso(
get = { it.fold(::Invalid, ::Valid) },
reverseGet = Validated<A2, B2>::toEither
)

/**
* [Iso] that defines the equality between [Either] and [Validated]
*/
@JvmStatic
fun <A, B> eitherToValidated(): Iso<Either<A, B>, Validated<A, B>> =
eitherToPValidated()

/**
* [Iso] that defines the equality between a Unit value [Map] and a [Set] with its keys
*/
@JvmStatic
fun <K> mapToSet(): Iso<Map<K, Unit>, Set<K>> =
Iso(
get = { it.keys },
reverseGet = { keys -> keys.map { it to Unit }.toMap() }
)

/**
* [PIso] that defines the equality between [Option] and the nullable platform type.
*/
@JvmStatic
fun <A, B> optionToPNullable(): PIso<Option<A>, Option<B>, A?, B?> =
PIso(
get = { it.fold({ null }, ::identity) },
reverseGet = Option.Companion::fromNullable
)

/**
* [PIso] that defines the isomorphic relationship between [Option] and the nullable platform type.
*/
@JvmStatic
fun <A> optionToNullable(): Iso<Option<A>, A?> = optionToPNullable()

/**
* [Iso] that defines the equality between and [arrow.core.Option] and [arrow.core.Either]
*/
@JvmStatic
fun <A, B> optionToPEither(): PIso<Option<A>, Option<B>, Either<Unit, A>, Either<Unit, B>> =
PIso(
get = { opt -> opt.fold({ Either.Left(Unit) }, ::Right) },
reverseGet = { either -> either.fold({ None }, ::Some) }
)

/**
* [Iso] that defines the equality between and [arrow.core.Option] and [arrow.core.Either]
*/
@JvmStatic
fun <A> optionToEither(): Iso<Option<A>, Either<Unit, A>> =
optionToPEither()

/**
* [Iso] that defines equality between String and [List] of [Char]
*/
@JvmStatic
fun stringToList(): Iso<String, List<Char>> =
stringToList

/**
* [PIso] that defines equality between [Validated] and [Either]
*/
@JvmStatic
fun <A1, A2, B1, B2> validatedToPEither(): PIso<Validated<A1, B1>, Validated<A2, B2>, Either<A1, B1>, Either<A2, B2>> =
PIso(
get = Validated<A1, B1>::toEither,
reverseGet = Validated.Companion::fromEither
)

/**
* [Iso] that defines equality between [Validated] and [Either]
*/
@JvmStatic
fun <A, B> validatedToEither(): Iso<Validated<A, B>, Either<A, B>> =
validatedToPEither()
}

/**
Expand Down Expand Up @@ -129,10 +244,11 @@ interface PIso<S, T, A, B> : PIsoOf<S, T, A, B> {
/**
* Pair two disjoint [PIso]
*/
infix fun <S1, T1, A1, B1> split(other: PIso<S1, T1, A1, B1>): PIso<Tuple2<S, S1>, Tuple2<T, T1>, Tuple2<A, A1>, Tuple2<B, B1>> = PIso(
{ (a, c) -> get(a) toT other.get(c) },
{ (b, d) -> reverseGet(b) toT other.reverseGet(d) }
)
infix fun <S1, T1, A1, B1> split(other: PIso<S1, T1, A1, B1>): PIso<Tuple2<S, S1>, Tuple2<T, T1>, Tuple2<A, A1>, Tuple2<B, B1>> =
PIso(
{ (a, c) -> get(a) toT other.get(c) },
{ (b, d) -> reverseGet(b) toT other.reverseGet(d) }
)

/**
* Create a pair of the [PIso] and a type [C]
Expand Down
111 changes: 110 additions & 1 deletion arrow-libs/optics/arrow-optics/src/main/kotlin/arrow/optics/Lens.kt
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package arrow.optics
import arrow.Kind
import arrow.KindDeprecation
import arrow.core.Either
import arrow.core.NonEmptyList
import arrow.core.None
import arrow.core.Option
import arrow.core.Some
Expand All @@ -14,13 +15,16 @@ import arrow.typeclasses.Functor
import arrow.typeclasses.Monoid

@Deprecated(KindDeprecation)
class ForPLens private constructor() { companion object }
class ForPLens private constructor() {
companion object
}
@Deprecated(KindDeprecation)
typealias PLensOf<S, T, A, B> = arrow.Kind4<ForPLens, S, T, A, B>
@Deprecated(KindDeprecation)
typealias PLensPartialOf<S, T, A> = arrow.Kind3<ForPLens, S, T, A>
@Deprecated(KindDeprecation)
typealias PLensKindedJ<S, T, A, B> = arrow.HkJ4<ForPLens, S, T, A, B>

@Suppress("UNCHECKED_CAST", "NOTHING_TO_INLINE")
@Deprecated(KindDeprecation)
inline fun <S, T, A, B> PLensOf<S, T, A, B>.fix(): PLens<S, T, A, B> =
Expand Down Expand Up @@ -79,6 +83,111 @@ interface PLens<S, T, A, B> : PLensOf<S, T, A, B> {

override fun set(s: S, b: B): T = set(s, b)
}

/**
* [Lens] to operate on the head of a [NonEmptyList]
*/
@JvmStatic
fun <A> nonEmptyListHead(): Lens<NonEmptyList<A>, A> =
Lens(
get = NonEmptyList<A>::head,
set = { nel, newHead -> NonEmptyList(newHead, nel.tail) }
)

/**
* [Lens] to operate on the tail of a [NonEmptyList]
*/
@JvmStatic
fun <A> nonEmptyListTail(): Lens<NonEmptyList<A>, List<A>> =
Lens(
get = NonEmptyList<A>::tail,
set = { nel, newTail -> NonEmptyList(nel.head, newTail) }
)

/**
* [PLens] to focus into the first value of a [Pair]
*/
@JvmStatic
fun <A, B, R> pairPFirst(): PLens<Pair<A, B>, Pair<R, B>, A, R> =
PLens(
get = { it.first },
set = { (_, b), r -> r to b }
)

/**
* [Lens] to focus into the first value of a [Pair]
*/
@JvmStatic
fun <A, B> pairFirst(): Lens<Pair<A, B>, A> =
pairPFirst()

/**
* [PLens] to focus into the second value of a [Pair]
*/
@JvmStatic
fun <A, B, R> pairPSecond(): PLens<Pair<A, B>, Pair<A, R>, B, R> =
PLens(
get = { it.second },
set = { (a, _), r -> a to r }
)

/**
* [Lens] to focus into the second value of a [Pair]
*/
@JvmStatic
fun <A, B> pairSecond(): Lens<Pair<A, B>, B> =
pairPSecond()

/**
* [PLens] to focus into the first value of a [Triple]
*/
@JvmStatic
fun <A, B, C, R> triplePFirst(): PLens<Triple<A, B, C>, Triple<R, B, C>, A, R> =
PLens(
get = { it.first },
set = { (_, b, c), r -> Triple(r, b, c) }
)

/**
* [Lens] to focus into the first value of a [Triple]
*/
@JvmStatic
fun <A, B, C> tripleFirst(): Lens<Triple<A, B, C>, A> =
triplePFirst()

/**
* [PLens] to focus into the second value of a [Triple]
*/
@JvmStatic
fun <A, B, C, R> triplePSecond(): PLens<Triple<A, B, C>, Triple<A, R, C>, B, R> =
PLens(
get = { it.second },
set = { (a, _, c), r -> Triple(a, r, c) }
)

/**
* [Lens] to focus into the second value of a [Triple]
*/
@JvmStatic
fun <A, B, C> tripleSecond(): Lens<Triple<A, B, C>, B> =
triplePSecond()

/**
* [PLens] to focus into the third value of a [Triple]
*/
@JvmStatic
fun <A, B, C, R> triplePThird(): PLens<Triple<A, B, C>, Triple<A, B, R>, C, R> =
PLens(
get = { it.third },
set = { (a, b, _), r -> Triple(a, b, r) }
)

/**
* [Lens] to focus into the third value of a [Triple]
*/
@JvmStatic
fun <A, B, C> tripleThird(): Lens<Triple<A, B, C>, C> =
triplePThird()
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,16 @@ import arrow.typeclasses.Applicative
import arrow.typeclasses.Monoid

@Deprecated(KindDeprecation)
class ForPOptional private constructor() { companion object }
class ForPOptional private constructor() {
companion object
}
@Deprecated(KindDeprecation)
typealias POptionalOf<S, T, A, B> = arrow.Kind4<ForPOptional, S, T, A, B>
@Deprecated(KindDeprecation)
typealias POptionalPartialOf<S, T, A> = arrow.Kind3<ForPOptional, S, T, A>
@Deprecated(KindDeprecation)
typealias POptionalKindedJ<S, T, A, B> = arrow.HkJ4<ForPOptional, S, T, A, B>

@Suppress("UNCHECKED_CAST", "NOTHING_TO_INLINE")
@Deprecated(KindDeprecation)
inline fun <S, T, A, B> POptionalOf<S, T, A, B>.fix(): POptional<S, T, A, B> =
Expand Down Expand Up @@ -108,7 +111,10 @@ interface POptional<S, T, A, B> : POptionalOf<S, T, A, B> {
* Invoke operator overload to create a [POptional] of type `S` with focus `A`.
* Can also be used to construct [Optional]
*/
operator fun <S, T, A, B> invoke(getOrModify: (source: S) -> Either<T, A>, set: (source: S, focus: B) -> T): POptional<S, T, A, B> = object : POptional<S, T, A, B> {
operator fun <S, T, A, B> invoke(
getOrModify: (source: S) -> Either<T, A>,
set: (source: S, focus: B) -> T
): POptional<S, T, A, B> = object : POptional<S, T, A, B> {
override fun getOrModify(source: S): Either<T, A> = getOrModify(source)

override fun set(source: S, focus: B): T = set(source, focus)
Expand All @@ -121,6 +127,28 @@ interface POptional<S, T, A, B> : POptionalOf<S, T, A, B> {
{ Either.Left(it) },
{ source, _ -> source }
)

/**
* [Optional] to safely operate on the head of a list
*/
@JvmStatic
fun <A> listHead(): Optional<List<A>, A> = Optional(
getOption = { Option.fromNullable(it.firstOrNull()) },
set = { list, newHead -> list.mapIndexed { index, value -> if (index == 0) newHead else value } }
)

/**
* [Optional] to safely operate on the tail of a list
*/
@JvmStatic
fun <A> listTail(): Optional<List<A>, List<A>> = Optional(
getOption = { if (it.isEmpty()) None else Some(it.drop(1)) },
set = { list, newTail ->
list.firstOrNull()?.let {
listOf(it) + newTail
} ?: emptyList()
}
)
}

/**
Expand Down

0 comments on commit 0af226b

Please sign in to comment.