Closed farnoy closed 2 weeks ago
This seems to work as expected, but I don't know enough to say if an extra type parameter affects the implicit search in practice (would it succeed in all cases you'd expect?)
given [T, K](using first: First[T] { type AssociatedFirst = K }): Second[T] with
type AssociatedSecond = first.AssociatedFirst
This workaround uses a refinement type (?), but why does it help?
That's very clever. I can make a few banal observations which may be obvious.
The given First
is a singleton, so its member type is known
val first: given_First_Test1.type = given_First_Test1
The given Second
is a def
of result type
given class given_Second_T[T >: Nothing <: Any](using first: First[T])
extends Object(), Second[given_Second_T.this.T] {
T
protected given val first: First[T]
type AssociatedSecond = given_Second_T.this.first.AssociatedFirst
}
where the First
is not constrained. Its AssociatedFirst
could be anything.
The second, constrained version is
given class given_Second_T[T >: Nothing <: Any, K >: Nothing <: Any](using
first:
First[T]
{
type AssociatedFirst = K
}
) extends Object(), Second[given_Second_T.this.T] {
T
K
protected given val first: First[T]{type AssociatedFirst = K}
type AssociatedSecond = given_Second_T.this.first.AssociatedFirst
}
where the K
is happily inferred
val second: given_Second_T[Test1, String] = ???
because AssociatedFirst
is known. I don't know if you intend that the given First
is always a singleton (unparameterized).
My experience is limited, but I expected this to be solvable by inlining. Is inlining not permitted for parameterized givens?
I don't know if you intend that the given First is always a singleton (unparameterized).
That's right. In my real code, I generate a lot of types with scalameta and one given each, targeting the same trait/typeclass.
Is inlining not permitted for parameterized givens?
This wouldn't be practical in my case because I have thousands of auto-generated types and inlining is slow. And it doesn't seem to help:
inline given [T](using first: First[T]): Second[T] = new Second[T]:
type AssociatedSecond = first.AssociatedFirst
inline given First[Test1] = new First[Test1]:
type AssociatedFirst = String
Yeah, this is working as expected. You need to preserve more type information (the type of the returned given) if you want to rely on it.
Isn't it strange that a non-implicit definition infers all of this automatically?
scala> def secondFromFirst[T](using first: First[T]) = new Second[T]:
|
| type AssociatedSecond = first.AssociatedFirst
|
def secondFromFirst
[T]
(using first: First[T]):
Second[T]{type AssociatedSecond = first.AssociatedFirst}
Feels like a footgun
Perhaps you can get your auto-generated types to add those associated types to the given type:
given [T](using first: First[T]): (Second[T] {
type AssociatedSecond = first.AssociatedFirst }) =
new Second[T]:
type AssociatedSecond = first.AssociatedFirst
I did figure it out now with refinement types and it's been working fine. Just bothers me a little that it's not the default and you need to be aware of it.
In my case, the refinement is needed for the using
parameter, so given [T, AssocFirst](using first: First[T] { type AssociatedFirst = AssocFirst }): Second[T]
I didn't need to refine the type of my given (so far, at least)
Yeah, type members don't need to be specified, unlike type arguments (for type parameters).
Compiler version
3.4.2, 3.5.1-RC2
Minimized code
Output
Expectation
I expected the compiler to be able to resolve
second.AssociatedSecond
asString
but it seems that the associated type does not reduce.