Suppose I have an abstract class Bar that takes a type parameter:
abstract class Bar[A] { def get: A }
and I have a function that wants to instantiate some Bar objects, call their get methods and return the results:
def foo[A, B <: Bar[A]]: Seq[A]
It seems a little verbose to have to provide A as a separate type parameter, since it's implicit in B. What I would really like is to say
def foo[B <: Bar[A]]: Seq[A]
but that doesn't compile. Is there a way to make foo more compact?
What Daniel said in the comment.
Perhaps using an abstract type member will help reduce the verbosity.
abstract class Bar {
type A
def get: A
}
def foo[B <: Bar]: Seq[B#A]
def baz[B <: Bar](b: B): Seq[B#A]
def taz[B <: Bar](b: B): Seq[b.A]
Related
Let's set up issue conditions
trait Bound
trait Bound2 extends Bound
trait T1[B <: Bound]
trait T2[B <: Bound2] extends T1[B]
trait WrapperT1[Tz[B2 <: Bound] <: T1[B2]]
This code compile without problem, issue comes when trying to extend WrapperT1
trait WrapperT2[Tz[B2 <: Bound2] <: T2[B2]] extends WrapperT1[T2]
// Compilator error
kinds of the type arguments (T2) do not conform to the expected kinds of the type parameters (type Tz) in trait WrapperT1.
[error] ex.T2's type parameters do not match type Tz's expected parameters:
[error] type B's bounds <: Bound2 are stricter than type B2's declared bounds <: ex.Bound
[error] trait WrapperT2[Tz[B2 <: Bound2] <: T2[B2]] extends WrapperT1[T2]
Yes B2 <: Bound2 is stricter than B2 <: Bound but i'm not understanding why the compilator complains for this reason and i would be grateful to know more about it.
Potential solution but does it has some drawbacks ?
trait Bound
trait Bound2 extends Bound
trait T1[B] {
implicit val ev: B <:< Bound
}
trait T2[B] extends T1[B] {
// this is possible thank's to covariance of class `<:<[-From, +To]` if i'm not wrong
implicit val ev: B <:< Bound2
}
trait WrapperT1[Tz[B2] <: T1[B2]]
// Compiles
trait WrapperT2[Tz[B2] <: T2[B2]] extends WrapperT1[Tz]
This looks nice and we keep the compilation checks about B2 generic type but is there any inconveniant using it ?
To understand why the compiler complains, let's look at this piece of code (which is the same as your code, but with different names)
trait Show[A <: AnyRef] {
def show(a: A): Unit
}
trait ShowString[S <: CharSequence] extends Show[S] {
def show(s: S): Unit = println(s)
}
trait Wrapper1[S[A <: AnyRef] <: Show[A]] {
def show[A <: AnyRef](a: A)(implicit show: S[A]): Unit = show.show(a)
}
trait Wrapper2[S[C <: CharSequence] <: ShowString[C]] extends Wrapper1[S]
Here, too, the compiler emits the error type C's bounds <: CharSequence are stricter than type A's declared bounds <: AnyRef, but let's pretend you somehow get it to compile here. That would mean if someone wanted to use your wrapper class to print, say, a list, they wouldn't be able to do that, meaning that your wrapper class would be basically useless.
val w2: Wrapper[ShowString] = ???
w2.show(List(1, 2))
//Error: could not find implicit value for parameter show: ShowString[List[Int]]
In this case, it's just that the implicit isn't found. However, if you have a method in wrapper that accepts a S[_] (or Tz[_], from your example) and you try to shoehorn an object of the wrong type into your show method, you could get a runtime exception.
It fails for the same reason you can't do this
trait T1[A] {
def process(a: A): Unit
}
trait T2 extends T1[Int] {
def process(i: Int): Unit = ???
}
val t2: T2 = ???
t2.process("not an int")
T2 is just not equipped to handle anything that is not an Int, and the same applies here.
I have the following types defined:
trait Context
trait Attribute[C <: Context]
trait AttributeDefinition[A[_ <: Context] <: Attribute[C] forSome { type C <: Context }] {
def read[C <: Context]: A[C]
}
I want AttributeDefinition to return an Attribute parameterized by a context. Each definition applies to only one type of Attribute. Hypothetically:
class ConstantValueAttribute[C <: Context] extends Attribute[C]
object ConstantValueAttributeDefinition extends AttributeDefinition[ConstantValueAttribute] {
override def read[C <: Context]: ConstantValueAttribute[C] = ???
}
This totally compiles fine, but i have trouble crafting method signatures that accept an unbounded AttributeDefinition. As well as creating a collection of AttributeDefinitions. All the following fail:
def def1(attribute: AttributeDefinition): Unit = ???
def def2(attribute: AttributeDefinition[_]): Unit = ???
def def3(attribute: AttributeDefinition[_ <: Attribute[C] forSome { type C <: Context }]): Unit = ???
def def4(attribute: AttributeDefinition[A[_]] forSome { type A <: Attribute[C]; type C <: Context })
EDIT:
I am trying to find a method declaration like the above that will compile. I also am looking to figure out how to declare a collection of AttributeDefinitions.
val val1 = new mutable.ArrayBuffer[AttributeDefinition]
if that is not possible i would love to figure out how to simplify the traits above that can still capture the simple read method declaration.
Proposal with a type member
Essentially, what you want in both defN-declarations and Seq-collections of AttributeDeclaration is a higher kinded existential type for Attribute that is used as an argument for AttributeDeclaration. So, ideally, you would like to have:
def def1(attrDef: AttributeDeclaration[A] forSome {
type A[C <: Context] <: Attribute[C] }): Unit = ???
However, this does not work, and this is an unresolved issue since 2015, so chances are that it won't be fixed until dotty.
Instead of trying to force the compiler to work with higher kinded existentials so that you can throw away the unnecessary type parameter, I would propose that you don't add the annoying type parameter in the first place. Consider these definitions (compiles with 2.12.4):
trait Context
trait Attribute[C <: Context]
trait AttributeDefinition {
type A[C <: Context] <: Attribute[C]
def read[C <: Context]: A[C]
}
class ConstantValueAttribute[C <: Context] extends Attribute[C]
object ConstantValueAttributeDefinition extends AttributeDefinition {
type A[C <: Context] = ConstantValueAttribute[C]
def read[C <: Context]: ConstantValueAttribute[C] = ???
}
def def1(attrDef: AttributeDefinition): Unit = ???
val collDefs: Seq[AttributeDefinition] = List(ConstantValueAttributeDefinition)
These definitions have following properties:
Context and Attribute are left unchanged
A becomes a type member of AttributeDefinition, so it does not have to be forcefully erased by an existential later
read-signature is completely unchanged, it's literally the same as in your original code, character for character
declaration of the simplest and most intuitive def1 works immediately without changes (also essentially copied from your code 1:1)
declaration and definition of collections with AttributeDefinitions also works just as smoothly.
Recall that you don't even lose anything if you move something from type parameter to type member, because you can always refer to the type member by name (for example, when you want to impose an additional restriction on that type member). The following minimal example is supposed to demonstrate how to impose additional restrictions on type members (vaguely related to your code, uses Context):
trait Foo {
type Member
}
def restrictiveFoo(x: Foo { type Member <: Context }): Unit = ???
Here, you can force the argumen Foo to have type member that derives from Context. It would work with type Member = Context similarly. What I wanted to demonstrate by that: it's not like type members are forever hidden inside the trait, if you introduce them. You can do with them almost all the same things as with type parameter. To summarize:
Adding additional constraints on a (higher kinded) type member is easy
Erasing a superfluous type parameter with higher kinded existentials doesn't seem to work at all.
So, therefore, I would advise to use a type member.
Proposal with change of variance from A to +A
Another quick-fix to make it compile:
trait Context
trait Attribute[C <: Context]
trait AttributeDefinition[+A[X <: Context] <: Attribute[X]] {
def read[C <: Context]: A[C]
}
class ConstantValueAttribute[C <: Context] extends Attribute[C]
object ConstantValueAttributeDefinition
extends AttributeDefinition[ConstantValueAttribute] {
override def read[C <: Context]: ConstantValueAttribute[C] = ???
}
class FooValueAttribute[C <: Context] extends Attribute[C]
object FooValueAttributeDefinition
extends AttributeDefinition[FooValueAttribute] {
override def read[C <: Context]: FooValueAttribute[C] = ???
}
def def1(attrDef: AttributeDefinition[Attribute]): Unit = {}
def1(ConstantValueAttributeDefinition)
val listOfDefs: List[AttributeDefinition[Attribute]] = List(
ConstantValueAttributeDefinition,
FooValueAttributeDefinition
)
inspired by the answer to this question. Strangely enough, it's seems to be almost exactly the same constellation. It didn't solve the problem with higher kinded existentials though, instead the variance of A has been changed to +A, so that one could use Attribute as least upper bound of all concrete As.
The correct signature would be
def `def`[A[_ <: Context] <: Attribute[C] forSome { type C <: Context }](attribute: AttributeDefinition[A]) = ???
You need to use a type parameter for your function, which should have (at least) the same constraints as the one in your type.
Note: in my opinion this is a reason why you should avoid putting type constraints on the type arguments for your type, they should only be put on functions/operations instead.
trait Context
trait Attribute[+Context]
trait AttributeDefinition[Attribute] {
def read[Context]: Attribute
}
class ConstantValueAttribute extends Attribute[Context]
object ConstantValueAttributeDefinition extends AttributeDefinition[ConstantValueAttribute] {
def read[Context]: ConstantValueAttribute = ???
}
def def1[C](attribute: AttributeDefinition[C]) : Unit = ???
As a hint for simplification too.
I have a have an implicit class like this
private[this] implicit class OptionListUtil[A, B <: List[A]](option: Option[B]) {
def defaultMap[C](f: A => C): Seq[C] = option.getOrElse(Seq.empty[A]).map(f)
But when I go ahead and call it on a Option[List[A]], I get the value defaultMap is not a member of Option[List[A]]
Intellij is not giving any hints so I'm pretty lost
Generally, if a type parameter only appears once it's suspicious. In this case B is actually useless and you can simplify OptionListUtil to
private[this] implicit class OptionListUtil[A](option: Option[List[A]]) {
def defaultMap[C](f: A => C): Seq[C] = option.getOrElse(Seq.empty[A]).map(f)
}
because Option is covariant. This is much simpler for type inference to handle.
Go with this instead:
implicit class OptionListUtil[A,B](option: Option[B])(implicit ev: B <:< List[A]) {
def defaultMap[C](f: A => C): Seq[C] = option.toSeq.flatMap(_.map(f))
}
The current scala compiler can't infer the type A in your B <: List[A] quite right yet. Asking for the implicit evidence of <:< in these cases helps the compiler with type inference.I read somewher that the new dotty compiler apparently doesn't have this issue.
Looks like another way to address this is with higher kinded generics with the class signature like:
private[this] implicit class OptionListUtil[A, B[A] <: List[A]](option: Option[B[A]])
Given the following:
trait A[X] {
def foo(): X
}
class B[T <: A[_]] {
def getFoo(a:T) = a.foo()
}
class C[T <: A[Z], Z] {
def getFoo(a:T):Z = a.foo()
}
Is it possible to make B work like C? Specifically getting the result type of a.foo().
scala> var b:B[A[Int]] = new B[A[Int]]()
scala> b.getFoo(AA(1))
res0: Any = 1
scala> var c:C[A[Int],Int] = new C[A[Int],Int]()
c: C[A[Int],Int] = C#4cc36c19
scala> c.getFoo(AA(1))
res1: Int = 1
b returns an Any, but c correctly returns an Int. This is obviously a contrived example, but it would greatly simplify by code if I could extract a subtype from a Generic type. Basically, knowing "Z" (as used in C) without having to pass it in explicitly - inferring from the type of A.
Obviously, C works as needed, but the issue is my framework is more akin to:
class D[T <: A[Z1], U <: B[Z2], Z1, Z2] extends E[T,U,Z1,Z2]
which then requires users of the framework to implement with 4 type parameters, when ideally it would only be 2
class D[T <: A[_], U <: B[_]] extends E[T,U]
Not a blocker, just an attempt at simplifying the exposed API.
You can do the following which is often used in certain typeclass based libraries:
trait Foo[H[_] <: Langh[_]]{
def thing[A](ha: H[A]): A = ha.ha()
}
This pushes the resolution of the type parameter to the invocation of the method thing. It uses higher-kinded types.
If fact, if you look at libs like ScalaZ, you'll see exactly this pattern:
trait Functor[F[_]]{
def map[A, B](fa: F[A])(f: A => B): F[B]
}
Reply to Comments
There is no way to have a type of U <: T[_] and then be able to extract out the type parameter of an actual T[A] because the very definition of it loses that information. If the above does not work for you, then I would suggest type classes:
trait Foo[U]{
def doStuff(u: U)(implicit ev: MyTypeClass[U]): ev.A = //...
}
wherein you only define your MyTypeClass implicits like the following:
trait Whatever[FA]{ /*...*/ }
object Whatever{
implicit def WhateverMTC[F[_] <: Bound[_], A0] = new MyTypeClass[F[A0]]{
type A = A0
//...
}
}
Then you can place your type bound on the implicit, your implicit carries with it the constraint that your type must be higher-kinded and you can get a method that returns the inner "hidden" type at the call site declaration.
That said, this is a lot of machinery when the first suggestion is much more elegant and a cleaner approach to the problem IMHO.
What is the difference between following two declarations of sum functions?
def sum[A](xs:List[A]) = .....
def sum[A <: List[A]](xs:A) = ....
EDIT:
To elaborate more....
Lets say I want to write a method head on List.
I can write head as follow:
def head[A](xs:List[A]) = xs(0)
or else we can write
def head[A <: List[A]] (xs:A) = xs(0)
Which one to prefer and when?
Note: one difference I figured out that implicit conversions are not applied for second one
The first one takes a List[A] as parameter, the second one some A that is a subtype of List[A]. Consider the following example:
scala> trait Foo[A <: Foo[A]]
defined trait Foo
scala> class Bar extends Foo[Bar]
defined class Bar
The recursive type parameter on Foo can be useful, when you want to define a method in the trait that takes or returns the type of the subclass:
trait Foo[A <: Foo[A]] {
def f: A
}
class Bar extends Foo[Bar] {
def f: Bar = new Bar
}
class Baz extends Foo[Baz] {
def f: Baz = new Baz
}
I don't know how your specific example should work, because I don't know how you would sum on that type.