Skip to content

Change into scheme to be fully type-based #23014

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 16 commits into from
May 30, 2025
Merged

Conversation

odersky
Copy link
Contributor

@odersky odersky commented Apr 17, 2025

A new version of the into scheme to allow certain implicit conversions without requiring a language import.

This is a lot simpler than previous schemes since it makes use of the power of the type system instead of building up a parallel structure based on modifiers. It is also considerably more flexible than the previous scheme. One open question might be whether it's too flexible.

```
This inserts the given conversion on the `ys` argument in `xs ++ ys`. It typechecks without a feature warning since the formal parameter of `++` is of type `into[IterableOnce]`, which is also the expected type of `ys`.

The
Copy link
Contributor

Choose a reason for hiding this comment

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

Looks like this paragraph went astray.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thanks. I fixed it.

@jducoeur
Copy link
Contributor

At first glance, I really like this -- it feels like it strikes a nice practical balance, and I suspect I'd use it moderately often.

I agree that it's quite flexible, but IMO requiring the into at the definition site is probably enough of a speedbump to avoid the sort of implicit-conversion chaos we've occasionally seen in Scala 2.

Since `into` is a regular type constructor, it can be used anywhere, including in type aliases and type parameters. This gives a lot of flexibility to enable implicit conversions for user-visible types. For instance, the Laminar framework
defined a type `Modifier` that is commonly used as a parameter type of user-defined methods and that should support implicit conversions into it. Pattern like this can be supported by defining a type alias such as
```scala
type Modifier = into[ModifierClass]
Copy link
Member

Choose a reason for hiding this comment

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

allowing abstraction like this seems to lose the benefit of keeping it clear that a method argument might be implicitly converted - perhaps tooling™️ can fix this if scaladoc could track if argument types can be converted to

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, maybe. But it was a concern raised by @sjrd, that it would be too cumbersome to require explicit into in user-defined Laminar functions. We can now address that concern. But we should do it only if there's a clear DSL-like pattern, IMO. Laminar modifiers might meet that requirement, but maybe not many other scenarios do.

Copy link
Member

Choose a reason for hiding this comment

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

Personally i think this is fine, at least it communicates at the definition of the type that its a target

odersky added 16 commits May 25, 2025 13:22
We now make into be just a type alias that the compiler knows about.
No syntax changes are necessary.
Refactor dealias to make it easier to add further keep conditions
Drop the restriction that instantiated TypeVars cannot be conversion targets
And drop duplicate test into-inferred.scala
Instead use a hack to convert the Into flag into the $into annotation for pickling.
This means we can merge the scheme without waiting for the next minor version.

When the scheme is stabilized we should revert this commit again to get the cleaner
treatment where the Into fag maps to the INTO tag.
Erase all covariant and invariant into occurrences in parameter types
@odersky odersky requested a review from noti0na1 May 25, 2025 11:37
@odersky
Copy link
Contributor Author

odersky commented May 25, 2025

This is best reviewed commit by commit.

@odersky
Copy link
Contributor Author

odersky commented May 25, 2025

SIP 71 was accepted for implementation in the last SIP meeting on May 23rd. So this PR can now be merged.


We can support implicit conversions to `Modifier`s simply by making `Modifier` an `into` trait:
```scala
into trait Modifier ...
Copy link
Member

Choose a reason for hiding this comment

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

how would Laminar maintainers preview integrating the feature without having to make typical users set experimental flag? I guess create a new branch with the mod and release under special version

Copy link
Contributor Author

Choose a reason for hiding this comment

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

That would be a way to do it. I agree our experimental scheme does not lend itself well to multi-group experiments. Of course Laminar could turn the flag on for themselves and use it in their code base and tests. That would already tell them something.


/** The position of the modifier associated with given flag in this definition. */
def flagSourcePos(mdef: DefTree, flag: FlagSet): SrcPos =
mdef.mods.mods.find(_.flags == flag).getOrElse(mdef).srcPos
Copy link
Member

Choose a reason for hiding this comment

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

I was always confused by the mods.mods. Do you know why we have two sets of modes with similar items?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I believe it's historical. The first mods is the Modifier structure which contains the actual modifier set as a mods field, and also contains annotations and an optional private-within name.

Copy link
Member

@noti0na1 noti0na1 left a comment

Choose a reason for hiding this comment

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

LGTM

@noti0na1 noti0na1 assigned odersky and unassigned noti0na1 May 29, 2025
@odersky odersky merged commit db65435 into scala:main May 30, 2025
30 checks passed
@odersky odersky deleted the change-into-2 branch May 30, 2025 07:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants