Skip to content

Update Coproducts #1291

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

Merged
merged 20 commits into from
Feb 12, 2019
Merged

Update Coproducts #1291

merged 20 commits into from
Feb 12, 2019

Conversation

abergfeld
Copy link
Contributor

@abergfeld abergfeld commented Feb 6, 2019

Fixes #1284

  • Deprecates cop and coproductOf methods as they always need generic types declared due to ambiguity
  • Add first, second, third etc constructor methods that don't require generic types declared unless they cannot be inferred
  • Make the data classes public as an alternative constructor
  • Update documentation to include runnable code examples
  • Update documentation to include example of flattening hierarchies
  • Update code generation to include class level and method level documentation

@abergfeld
Copy link
Contributor Author

@JorgeCastilloPrz @raulraja Just some questions:

  • Should we use the generic letter for extension method names "String".a() or the name of the data class "String".first()?

  • Should coproductOf also be deprecated? It should suffer from the safe inference issue

  • Should I add something to the docs about the data classes being a valid way to construct a Coproduct?

  • Is there something more specific we should use for the deprecation message?

@raulraja
Copy link
Member

raulraja commented Feb 7, 2019

I'd make the extensions the same name as the data class constructors.
coproductOf should also be deprecated as you noted.
It'd be nice if the docs showcased both construction styles. ADT constructors and syntax.
The docs should be moved to kdocs and relinked in the menu to the generated api doc, ideally with runnable snippets as it is done in other places like functor, Bracket, etc.

@abergfeld
Copy link
Contributor Author

@raulraja Updated to use the first, second etc naming. Also deprecated the coproductOf functions as well in favor of just using the data classes.

I added the ADT examples to the docs and added kdocs for all the non-deprecated things.

I have a couple things I'm not sure of:

  1. Given that there's so many functions related to Coproducts (2-22 sealed classes, etc) I just added runnable snippets to the current docs page. I assume it's not worth linking the same kind of runnable snippets over and over if it's generated off the kdocs. Just wanted to validate that it's fine to leave that out of the kdocs.

  2. I'm having some trouble building the documentation locally. The code snippets all show up correctly but when I run them, it says that they don't compile on things that didn't break the build. Does the code snippet running happen with the current master version of Arrow? I had compilation failures running Ank locally and fixed them but the snippets still failed after I ran the jeykll command

@JorgeCastilloPrz
Copy link
Member

Are you following the steps described here?
(dokka + ank + deploy)

Copy link
Member

@JorgeCastilloPrz JorgeCastilloPrz left a comment

Choose a reason for hiding this comment

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

Wow, you were fast @abergfeld :). Thanks for your help 🎉

All the kdocs about public APi should be moved to the kotlin generated files as kdocs, if possible, as mentioned by @raulraja. The only doubt I have is whether that's possible since the complete Coproducts code is generated at compile time. I think it is, but not sure if we'd want to generate for each one of the 22 generated coproduct.kt files.

Also, I've noted that the IntellIJ IDEA autocomplete suggestions gets horribly slow when working with coproducts on my machine. Not sure if it's just my machine or an overall problem caused by the amount of methods that appear when you try to use the ext funs like cop(). (Since you get all the 22 suggestions there). I guess that's not a problem anymore since we've removed those, but it's something I'd keep an eye on.

val secondValue = 100L
val thirdValue = none<String>()

firstValue.cop<String, Long, Option<String>>() shouldBe firstValue.first<String, Long, Option<String>>()
Copy link
Member

Choose a reason for hiding this comment

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

didn't we remove cop ext funs?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

No I think the intent is to deprecate them and remove them in a later version? #1284 (comment)

I'm not sure what the next Arrow version will be I guess, I may have misunderstood

Copy link
Member

Choose a reason for hiding this comment

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

that is correct @abergfeld they will be deprecated in 0.9.0 coming out now and will be removed sometime before 1.0 final.

@JorgeCastilloPrz
Copy link
Member

@raulraja don't you think the blogpost style would work good for some ideas here like what coproducts provide versus nested sealed classes? or would you completely move everything to Kdoc?

@raulraja
Copy link
Member

raulraja commented Feb 7, 2019

Yes but that also goes well in the class header unless it's tutorial style in which case is fine in the regular docs and should be linked from the class kdoc headers

@abergfeld
Copy link
Contributor Author

@JorgeCastilloPrz @raulraja This PR currently has docs being generated with the code:

/**
 * Runs the function related to the actual value of the Coproduct and returns the result
 *
 * Parameters: A function for every possible value the Coproduct could be.
 * Returns: RESULT generated by the input functions */
fun <A, B, C, RESULT> Coproduct3<A, B, C>.fold(
    a: (A) -> RESULT,
    b: (B) -> RESULT,
    c: (C) -> RESULT
): RESULT = when (this) {
    is First -> a(this.a)
    is Second -> b(this.b)
    is Third -> c(this.c)
}

Is that sufficient to auto link to the API DOCs section?

@raulraja Does the runnable code examples in the docs run using only the latest master version of Arrow? Even compiling the docs locally seems like it should work but they don't run correctly as if they're using the current master version of Coproduct

@abergfeld
Copy link
Contributor Author

@JorgeCastilloPrz I think I addressed all your comments except removing the extension functions. I'm not sure if we should just remove those functions now or not.

I've updated the kdocs to be more inlined with what Option is doing and the locally generated docs look like it's all linking up correctly and showing descriptions / params / returns.

The only part I'm still not sure works is the runnable code examples but it seems like those are just based off the master version of arrow and the examples compile so I'd assume they'll work once merged?

Here's the generated output for Coproduct2 since it's generated and doesn't show as part of the diff:

package arrow.generic.coproduct2

import arrow.core.Option
import arrow.core.toOption
import kotlin.Deprecated
import kotlin.Suppress
import kotlin.Unit

/**
 * Represents a sealed hierarchy of 2 types where only one of the types is actually present.
 */
sealed class Coproduct2<A, B>

/**
 * Represents the first type of a Coproduct2
 */
data class First<A, B>(val a: A) : Coproduct2<A, B>()

/**
 * Represents the second type of a Coproduct2
 */
data class Second<A, B>(val b: B) : Coproduct2<A, B>()

@Deprecated(message = "This has issues with type inference, use First() instead",
replaceWith = ReplaceWith("First<A, B>(a)", "arrow.generic.coproduct2.First")
)
fun <A, B> coproductOf(a: A): Coproduct2<A, B> = First(a)

@Deprecated(message = "This has issues with type inference, use Second() instead",
replaceWith = ReplaceWith("Second<A, B>(b)", "arrow.generic.coproduct2.Second")
)
@Suppress("UNUSED_PARAMETER")
fun <A, B> coproductOf(b: B, dummy0: Unit = Unit): Coproduct2<A, B> = Second(b)

@Deprecated(message = "This has issues with type inference, use .first() instead",
replaceWith = ReplaceWith("this.first<A, B>()")
)
fun <A, B> A.cop(): Coproduct2<A, B> = coproductOf<A, B>(this)

@Deprecated(message = "This has issues with type inference, use .second() instead",
replaceWith = ReplaceWith("this.second<A, B>()")
)
@Suppress("UNUSED_PARAMETER")
fun <A, B> B.cop(dummy0: Unit = Unit): Coproduct2<A, B> = coproductOf<A, B>(this)

/**
 * Creates a Coproduct from the A type
 *
 * @return A Coproduct2<A, B> where the receiver is the A
 */
fun <A, B> A.first(): Coproduct2<A, B> = First(this)

/**
 * Creates a Coproduct from the B type
 *
 * @return A Coproduct2<A, B> where the receiver is the B
 */
fun <A, B> B.second(): Coproduct2<A, B> = Second(this)

/**
 * Transforms the Coproduct into an Option based on the actual value of the Coproduct
 *
 * @return None if the Coproduct was not the specified type, Some if it was the specified type
 */
fun <A> Coproduct2<A, *>.select(): Option<A> = (this as? First)?.a.toOption()

/**
 * Transforms the Coproduct into an Option based on the actual value of the Coproduct
 *
 * @return None if the Coproduct was not the specified type, Some if it was the specified type
 */
@Suppress("UNUSED_PARAMETER")
fun <B> Coproduct2<*, B>.select(dummy0: Unit = Unit): Option<B> = (this as? Second)?.b.toOption()

/**
 * Runs the function related to the actual value of the Coproduct and returns the result
 *
 * @param a The function used to map A to the RESULT type
 * @param b The function used to map B to the RESULT type
 *
 * @return RESULT generated by one of the input functions
 */
fun <A, B, RESULT> Coproduct2<A, B>.fold(a: (A) -> RESULT, b: (B) -> RESULT): RESULT = when (this) {
    is First -> a(this.a)
    is Second -> b(this.b)
}

@JorgeCastilloPrz
Copy link
Member

Deprecating the cop() functions makes sense 👍

Copy link
Member

@raulraja raulraja left a comment

Choose a reason for hiding this comment

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

@abergfeld this looks great, thanks so much!. We just need to make some little fixes in the docs and this is good to go.

@abergfeld
Copy link
Contributor Author

@raulraja I think I addressed all your comments 🎉 Let me know if I should update anything else!

@JorgeCastilloPrz
Copy link
Member

So no plans on generating runnable snippets with dokka anymore? Can we just merge like this @raulraja? (coproduct code is compile time generated, we'd get those snippets generated 26 times, which might be too much)

@raulraja
Copy link
Member

it's fine to get the snippets generated N times. All pages need to documented and some users may land straight in coproduct5 for example.

Copy link
Member

@raulraja raulraja left a comment

Choose a reason for hiding this comment

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

Great work @abergfeld ! 👏

@raulraja raulraja merged commit 245ad1b into arrow-kt:master Feb 12, 2019
@JorgeCastilloPrz
Copy link
Member

JorgeCastilloPrz commented Feb 12, 2019

Can we get this in 0.9.0 or 0.9.1 or something?

@raulraja
Copy link
Member

This is coming out in 0.9.0. @nomisRev and I are making some final touches to Fx and then we will release. Thanks for your patience.

@pakoito
Copy link
Member

pakoito commented Feb 12, 2019

@raulraja make sure to double-check the crash!

@abergfeld abergfeld deleted the update-coproducts branch February 12, 2019 21:22
@raulraja
Copy link
Member

any way to repro on tests or does it require an Android runtime? I may need help with this once fx is ready. I'll ping one of you guys that would not have to download the internet to test it on Android.

@JorgeCastilloPrz
Copy link
Member

What crash are we talking about?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants