Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Deprecate Monoid and Semigroup in Optics #2985

Merged
merged 10 commits into from
Mar 24, 2023
Merged

Conversation

serras
Copy link
Member

@serras serras commented Mar 17, 2023

Fixes #2975

This PR also includes some additional changes in the code to remove calls to deprecated methods like bimap and orElse.

@serras serras requested review from nomisRev and a team March 17, 2023 11:00
@@ -100,7 +104,7 @@ public abstract interface class arrow/optics/Getter : arrow/optics/Fold {
public abstract fun choice (Larrow/optics/Getter;)Larrow/optics/Getter;
public abstract fun compose (Larrow/optics/Getter;)Larrow/optics/Getter;
public abstract fun first ()Larrow/optics/Getter;
public abstract fun foldMap (Larrow/typeclasses/Monoid;Ljava/lang/Object;Lkotlin/jvm/functions/Function1;)Ljava/lang/Object;
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unfortunately the change of the empty / combine method being the "main" one in Fold is breaking binary compatibility. But I don't know how to make these changes otherwise; any help is welcome.

@serras serras added the 1.2.0 Tickets belonging to 1.1.2 label Mar 17, 2023
Copy link
Member

@nomisRev nomisRev left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good to me overall. I left a suggestion of how we can work around the binary compatibility problem, and put it off to 2.0

Comment on lines 32 to 39
public fun <R> foldMap(empty: R, combine: (R, R) -> R, source: S, map: (focus: A) -> R): R

/**
* Map each target to a type R and use a Monoid to fold the results
*/
public fun <R> foldMap(M: Monoid<R>, source: S, map: (focus: A) -> R): R
@Deprecated(MonoidDeprecation, ReplaceWith("foldMap(M.empty(), M::combine, source, map)", "arrow.typeclasses.combine"))
public fun <R> foldMap(M: Monoid<R>, source: S, map: (focus: A) -> R): R =
foldMap(M.empty(), M::combine, source, map)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the only way to do this in a non-binary breaking change is to:

  • Deprecate foldMap with Monoid but keep it abstract
  • Implement the new foldMap through the abstract method. (This would require creating an anonymous Monoid)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This would meant that someone who implements a custom Fold needs to refactor a breaking change in 2.0, but a consumer would not face a breaking change when not relying on the deprecated code.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I've managed to keep ABI compatibility by:

  • Keep the Fold interface as it was, using Monoid;
  • Define the new fold and foldMap as extension functions, so they don't become part of the interface.

@@ -50,6 +50,12 @@ public interface Monoid<A> : Semigroup<A> {
public fun fold(elems: List<A>): A = elems.fold()

public companion object {
@JvmStatic
public fun <A> of(empty: A, combine: (A, A) -> A): Monoid<A> = object : Monoid<A> {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should all these constructors not be deprecated?

Copy link
Collaborator

@franciscodr franciscodr left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good to me, @serras!

/**
* Fold using the given [empty] element and [combine].
*/
public fun <S, A> Fold<S, A>.fold(empty: A, combine: (A, A) -> A, source: S): A =
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can this be added as a method on Fold?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I made this change (and the one below) for ABI compatibility. If I include it in the interface, it becomes part of it, so in theory previous users are not binary-compatible with it.

If we prefer not requiring the additional imports, we just need to revert the last two commits in this PR.

/**
* Map each target to a type [R] and combine the results as a fold.
*/
public fun <S, A, R> Fold<S, A>.foldMap(empty: R, combine: (R, R) -> R, source: S, map: (focus: A) -> R): R =
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this an extension because it was a conflicting API? and @JvmName doesn't work on interface?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As discussed above, this is an extension so that the method is not added to the interface, which would break binary compatibility.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Adding methods is not a problem, only removal, since this would break compatibility for users relying on dependencies with older Arrow versions of the 1.x.x series.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, I misunderstood the binary compatibility then. I've moved the functions back to Fold, where they should logically belong.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's really a JVM thing I guess, the problem is the following:

  • Library A has a dependency on Arrow 1.1.5 relies on Fold.foldMap
  • Project B has dependency on Library A & has dependency on Arrow 1.1.5
  • Arrow 1.2.0 removes Fold.foldMap
  • Project B bumps dependency to Arrow 1.2.0 => Fold.foldMap: MethodNotFoundException at runtime.

So adding methods is non-problematic.

@serras serras merged commit 61e10d8 into main Mar 24, 2023
@serras serras deleted the as-deprecate-semigroup-optics branch July 3, 2023 08:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
1.2.0 Tickets belonging to 1.1.2
Projects
None yet
Development

Successfully merging this pull request may close these issues.

["Request"] Update arrow-optics to work without Monoid & Semigroup
3 participants