Skip to content

Try redesign using Into instead of explicit conversions.#23

Merged
bishabosha merged 4 commits intomainfrom
use-into
Jan 23, 2026
Merged

Try redesign using Into instead of explicit conversions.#23
bishabosha merged 4 commits intomainfrom
use-into

Conversation

@bishabosha
Copy link
Member

With into, there is no need for the 'Compat' conversion because when subtyping is enough then no conversion is needed.

With into, there is no need for the 'Compat' conversion because when
subtyping is enough then no conversion is needed.
Copy link
Contributor

@natsukagami natsukagami left a comment

Choose a reason for hiding this comment

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

Seems that implementing it this way would break scoping:

  test("scoping") {
    Result[Int, String]: l1 ?=>
      Result[String, Int]: l2 ?=>
        val r: Result[Int, Nothing] = Result.Ok(1)
        r.ok
  }

Here the compiler will immediately pick l2 for the Label and tries to apply a conversion (and fail) without retrying with l1.

Comment on lines 65 to 66
@deprecated("No longer needed with redesign using Conversion.into")
inline given [T]: Conversion[T, T] = identity
Copy link
Contributor

Choose a reason for hiding this comment

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

I think we can just remove this, no need for deprecation

@bishabosha
Copy link
Member Author

bishabosha commented Jan 19, 2026

Seems that implementing it this way would break scoping:

  test("scoping") {
    Result[Int, String]: l1 ?=>
      Result[String, Int]: l2 ?=>
        val r: Result[Int, Nothing] = Result.Ok(1)
        r.ok
  }

Here the compiler will immediately pick l2 for the Label and tries to apply a conversion (and fail) without retrying with l1.

perhaps you could pick a better example because in any case i would expect r.ok is going to return an Int so wouldnt be correctly typed anyway - Edit: and this doesnt work with the old design either.

If i try the following would you expect err to jump to the outer label?

given Conversion[Int, Bar] = Bar(_)
class Bar(x: Int)
class Foo
Result[Int, Bar]: l1 ?=>
  Result[String, Foo]: l2 ?=>
    val r: Result[String, Int] = Result.Err(1)
    r.ok
  .ok.length

Edit 2: as a follow up - it doesnt seem possible to infer l1 but I could make it work for r.ok(using l1) - but is that really something worth supporting? (Edit 3: perhaps for tight code where you really want the inlining of jumps)

@natsukagami
Copy link
Contributor

If i try the following would you expect err to jump to the outer label?

No I don't think so. On second thought I think it's actually good that it doesn't try to jump to the outer label, it could be quite confusing that way.

Edit 2: as a follow up - it doesnt seem possible to infer l1 but I could make it work for r.ok(using l1) - but is that really something worth supporting? (Edit 3: perhaps for tight code where you really want the inlining of jumps)

I think we should be able to do this yeah.

@bishabosha
Copy link
Member Author

bishabosha commented Jan 22, 2026

newer design to let you explicitly pass in the label to .ok, while also erasing any conversion with .ok for when error channel is a subtype

Copy link
Contributor

@natsukagami natsukagami left a comment

Choose a reason for hiding this comment

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

I like this design a lot :D

"`.ok` cannot be used outside of the `Result.apply` scope."
)
inline label: boundary.Label[Err[E1]]
)(using inline conv: ConvErr[E, E1]): T =
Copy link
Contributor

Choose a reason for hiding this comment

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

Is having ConvErr a convenient way to have identity conversion without it polluting the user's Conversion search space? It seems pretty neat 🤔

Copy link
Member Author

Choose a reason for hiding this comment

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

Yeah I think the goal was mainly to avoid needing the Compat import and also delay the conversion until the error case

)(using
@implicitNotFound(
"""`.ok` cannot be used here, as the error types of this Result (${E}) and `Result.apply` (${E1}) are incompatible. Consider changing the error type of `Result.apply`, or provide a conversion between the error types through an instance of the `Conversion` trait:
opaque type ConvErr[E, E1] = Conversion[Err[E], Err[E1]]
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
opaque type ConvErr[E, E1] = Conversion[Err[E], Err[E1]]
/** Conversion from error type `E` to error type `E1`.
* An instance is provided for every `E1 <: E`, as well as for every existing implicit [[scala.Conversion Conversion]] instance from `E` to `E1`. */
opaque type ConvErr[E, E1] = Conversion[Err[E], Err[E1]]

Copy link
Member Author

Choose a reason for hiding this comment

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

Ultimately i backtracked on this design (its still there commented) in favor of eval.ok(using l1)(r) if that control is needed.

@bishabosha
Copy link
Member Author

bishabosha commented Jan 23, 2026

for now i went back to ok with into, but its true that with the happy path but with error conversion required it would do an extra isInstanceOf check (i.e. convert ok -> ok) - of course if the error channel lines up with the label's type then no conversion is applied

its hard to decide what to design to keep just because it does add this extra type, but the more complex design is lower runtime cost in all cases

the old design is still there as a comment

@bishabosha bishabosha merged commit 3f7d686 into main Jan 23, 2026
1 check passed
@bishabosha bishabosha deleted the use-into branch January 23, 2026 15:43
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.

2 participants