Scala 3. Kind polymorphism and AnyKind type - any code example? - scala

Scala3 has support for "kind polymorphism". Docs also mention AnyKind type:
AnyKind plays a special role in Scala's subtype system: It is a supertype of all other types no matter what their kind is.
Question:
can anyone give a working code example how AnyKind generality is useful?
(surprisingly can't find any useful examples so far)
docs

For example the type member MirroredType of scala.deriving.Mirror.Product/Mirror.Sum is actually poly-kinded (although this is not written in the definition of Mirror/Mirror.Sum/Mirror.Product)
sealed trait Mirror:
type MirroredMonoType
type MirroredLabel <: String
type MirroredElemLabels <: Tuple
https://docs.scala-lang.org/scala3/reference/contextual/derivation.html#mirror
The type member MirroredMonoType has always kind *, including being existential (A[?]). But MirroredType can be *
sealed trait A
case class B() extends A
case class C() extends A
val m = summon[Mirror.Sum { type MirroredType = A }]
//scala.deriving.Mirror.Sum{
// MirroredMonoType = A; MirroredType = A;
// MirroredLabel = ("A" : String)
// ; MirroredElemTypes = (B, C);
// MirroredElemLabels = (("B" : String), ("C" : String))
//}
or * => *
sealed trait A[T]
case class B() extends A[Int]
case class C() extends A[String]
val m = summon[Mirror.Sum { type MirroredType[T] = A[T] }]
//val m = summon[Mirror.Sum { type MirroredType = [T] =>> A[T] }]
//scala.deriving.Mirror.Sum{
// MirroredMonoType = A[?]; MirroredType[T] = A[T];
// MirroredLabel = ("A" : String)
// ; MirroredElemTypes[T] = (B, C);
// MirroredElemLabels = (("B" : String), ("C" : String))
//}
etc.
Notice that MirroredElemTypes is also poly-kinded (MirroredElemTypes = (B, C), MirroredElemTypes[T] = (B, C), ...)
So if I wanted to do something further with a tuple MirroredElemTypes then the only option would be to have upper bound AnyKind
def foo[T <: AnyKind] = ???
foo[m.MirroredElemTypes]
Another example is scala.quoted.Type (thanks to #Max for pointing this out)
abstract class Type[T <: AnyKind]:
type Underlying = T
https://contributors.scala-lang.org/t/proposal-to-add-kind-polymorphism-to-the-language/2958/16
Miles Sabin. Adding kind-polymorphism to the Scala programming language https://www.youtube.com/watch?v=v6e7rYOXdcM

for those who are interested in example.
I have found simple, yet clear one in Dotty code base. See how foo takes polymorphic kind (any kind order) argument:
case class Bar[A](a: A)
trait Toto[A, B]
trait Foo[T <: AnyKind] {
type Out;
def id(t: Out): Out = t
}
object Foo {
implicit def foo0[T]: Foo[T] {type Out = T} = new Foo[T] {
type Out = T
}
implicit def foo1[T[_]]: Foo[T] {type Out = T[Any]} = new Foo[T] {
type Out = T[Any]
}
implicit def foo2[T[_, _]]: Foo[T] {type Out = T[Any, Any]} = new Foo[T] {
type Out = T[Any, Any]
}
}
def foo[T <: AnyKind](implicit f: Foo[T]): f.type = f
foo[Int].id(23) == 23
foo[List].id(List[Any](1, 2, 3)) ==
List(1, 2, 3)
foo[Map].id(Map[Any, Any](
1 -> "toto",
2 -> "tata",
3 -> "tutu")) ==
Map(
1 -> "toto",
2 -> "tata",
3 -> "tutu")

Related

Using a type constructor in a type refinement

I'm having a problem which is probably best expressed in code - a simplified example below:
abstract class MainTC[A] {
type E
// A type constructor for the implementing type:
type CN[_]
implicit val ev: CN[A] =:= A // check that CN works as a type constructor for A
def get(self: A): E
def set[B](self: A, other: B): CN[B] { type E = B }
def convert[B](self: A)(implicit conv: Convert[A, E, B]) = conv.convert(self)(this)
}
abstract class Convert[A, _E, B] {
type Out
def convert(self: A)(implicit isMain: MainTC[A] { type E = _E }): Out
}
object Convert {
implicit def convertDoubleToInt[A, _CN[_]](implicit
isMain: MainTC[A] { type E = Double; type CN[_] = _CN[_] },
): Convert[A, Double, Int] = new Convert[A, Double, Int] {
type Out = _CN[Int] { type E = Int }
def convert(self: A): Out = {
val toInt = isMain.get(self).toInt
isMain.set[Int](self, toInt)
// type mismatch -
// found: isMain.CN[Int]{type E = Int} (which expands to) _CN[_]{type E = Int}
// required: this.Out (which expands to) _CN[Int] {type E = Int}
}
}
}
The basic situation here is quite simple - I am using a typeclass to implement the polymorphic convert function. The tricky part is that I am storing a type constructor as an abstract type within the MainTC typeclass. When converting in the Convert typeclass, I would then like to use that type constructor to create a new type as the output type (eg, CN[Int]). I am trying to use something like the Aux pattern to achieve this, with _CN[_] being created as a type alias for isMain.CN[_]. However, it's not working (error message in the code). If anyone could lend me a hand I'd be most grateful.
do you mean type CN[_] = _CN[_] or type CN[X] = CN[X]? If you change it to the latter, you run into the issue that
def convert(self: A): Out
can't implement
def convert(self: A)(implicit isMain: MainTC[A] { type E = _E }): Out
because it's missing the implicit parameter. Keep in mind that Scala implicits don't have to be coherent: convertDoubleToInt's (isMain: MainTC[A] {type E = _ E}).TC isn't the same as convert's (isMain: MainTC[A] {type E = _ E}).TC

How to translate type projections into PDTs in implicits?

Here is one idiomatic scala 2 example:
trait Box {
type Content
val content :Content
}
implicit class BoxExtension[B <: Box](private val self :B) extends AnyVal {
def map[O](f :self.Content => O)(implicit mapper :Mapper[B, O]) :mapper.Res =
mapper(self)(f)
}
trait Mapper[B <: Box, O] {
type Res
def apply(box :B)(f :box.Content => O) :Res
}
As you see, it has already been partly converted to the usage of path dependent types. But how to create an implicit value of Mapper for an O type which references self.Content (such as O =:= self.Content itself)?. Ideally a solution which would have straightforward equivalence between Scala 2 and Scala 3.
You can make an implicit def for this. Since Dotty doesn't allow type projections on abstract types, you'll need an extra type parameter. Also, I had to make self public because it was used in the signature of map.
object Mapper {
implicit def uselessMapper[B <: Box{type Content = C}, C]: Mapper[B, C] { type Res = AllPurposeBox[C] } =
new Mapper[B, C] {
type Res = AllPurposeBox[C]
def apply(box: B)(f: box.Content => C) =
new AllPurposeBox(f(box.content))
}
}
class AllPurposeBox[T](override val content: T) extends Box {
type Content = T
}
Full example
I would normally suggest using type parameters for Box and (and an extra one for Mapper) instead, but it gets a little complicated. Perhaps you could turn BoxExtension into something like this, with C as an extra parameter:
implicit class BoxExtension[B <: Box {type Content = C}, C](private val self: B) extends AnyVal {
def map[O](f: C => O)(implicit mapper: Mapper[B, O]): mapper.Res =
mapper(self)(f)
}
If you're open to using just Dotty and not cross-compiling, you can do this
trait Mapper[B <: Box, O] {
type Res
def apply(box: B)(f: box.Content => O): Res
extension (self: B) def map(f: self.Content => O): Res = apply(self)(f)
}
object Mapper {
given uselessMapper[B <: Box{type Content = C}, C] as Mapper[B, C] {
type Res = AllPurposeBox[C]
def apply(box: B)(f: box.Content => C) = new AllPurposeBox(f(box.content))
}
}

How to combine type parameter bounds and functors using Cats?

I am encountering a number of use cases where I am end of attempting to write Functor, Applicative, Monad, etc instances in contexts where I am also using type parameter bounds.
For example...
import cats._
trait Preference[A] extends Order[A]
trait SocialWelfareFunction[-CC <: Iterable[P], +P <: Preference[_]]
extends (CC => P)
object SocialWelfareFunction {
def functor: Functor[({ type F[P <: Preference[_]] = SocialWelfareFunction[Iterable[P], P] })#F] = {
???
}
...when I try to compile this I will get the following error.
kinds of the type arguments ([P <: Playground.this.Preference[_]]Playground.this.SocialWelfareFunction[Iterable[P],P]) do not conform to the expected kinds of the type parameters (type F) in trait Monad.
[P <: Playground.this.Preference[_]]Playground.this.SocialWelfareFunction[Iterable[P],P]'s type parameters do not match type F's expected parameters:
type P's bounds <: Playground.this.Preference[_] are stricter than type _'s declared bounds >: Nothing <: Any
How can I write Functor, Applicative, Monad , etc instances for contexts in which I am also using type parameters? Is it even possible? Is there a more appropriate way forward?
(Not a full solution, just a hint requested by OP to further clarify the question)
This example shows how to define Functor instances for type constructors that look almost as if they could be functors, but have several restrictions:
Upper type bound <: UB on type parameter
Require instances of typeclass TC
Here is a way around these two restrictions:
import scala.language.higherKinds
// Your favorite flavour of `Functor`,
// e.g. from `scalaz` or `cats`
trait Functor[F[_]] {
def map[A, B](x: F[A], f: A => B): F[B]
}
// an upper bound
trait UB
// a typeclass
trait TC[X]
// A type constructor that is not a
// functor, because it imposes upper bounds
// on the parameter, and because it requires
// a typeclass `TC` for `X`.
class Foo[X <: UB : TC] {
def map[Y <: UB : TC](f: X => Y): Foo[Y] = ??? /*
some very clever implementation making use of `UB` and `TC`
*/
}
// A Functor that approximates `Foo[X]` for *arbitrary* `X`,
// without any restrictions.
abstract class WrappedFoo[X] { outer =>
type Base <: UB
val base: Foo[Base]
protected val path: Base => X
def map[Y](f: X => Y): WrappedFoo[Y] = new WrappedFoo[Y] {
type Base = outer.Base
val base = outer.base
val path: Base => Y = outer.path andThen f
}
def unwrap[Y <: UB](
implicit
xIsY: X =:= Y,
yTC: TC[Y]
): Foo[Y] = base.map(outer.path andThen xIsY)
}
// Functor instance for `WrappedFoo`
object WrappedFooFunctor extends Functor[WrappedFoo] {
def map[A, B](x: WrappedFoo[A], f: A => B): WrappedFoo[B] = {
x map f
}
def wrap[A <: UB](foo: Foo[A]): WrappedFoo[A] = new WrappedFoo[A] {
type Base = A
val base = foo
val path = identity[A]
}
}
object Example {
// two "good" classes that conform to
// the upper bound and have instances
// of the typeclass
class Good1 extends UB
class Good2(i: Int) extends UB
implicit object Good1TC extends TC[Good1]
implicit object Good2TC extends TC[Good2]
val x1 = new Foo[Good1] // start with "Foo[Good1]"
val f: Good1 => Int = _.toString.length
val g: Int => Good2 = i => new Good2(i)
// Now we would like to go like this:
//
// Foo[Good1] ---f---> Foo[Int] ---g---> Foo[Good2]
//
// The problem is: `Int` does not conform to `UB`,
// and has no `TC` instance.
// Solution:
val x1w = WrappedFooFunctor.wrap(x1)
val intermediate = x1w.map(f) // approximates "Foo[Int]"
val x2w = intermediate.map(g) // wraps "Foo[Good2]"
val x2 = x2w.unwrap // only "Foo[Good2]"
}

Scala: higher-kinded types, type projections and type mismatch error

I have the following code:
trait M[Type[_]]{
type T[X] = Type[X]
def from[A](f: T[A]): A
}
class ListM extends M[List]{ def from[A](f: T[A]) = f.head }
class Trans[A, X[_], B <: M[X]](val r: X[Option[A]])
trait CurriedTrans[X[_], B <: M[X]]{ type Type[A] = Trans[A, X, B] }
class TransM[X[_], B <: M[X]](val b: B) extends M[CurriedTrans[X, B]#Type]{
def from[A] = (f: T[A]) => b.from(f.r).get
}
and I can instantiate variables of type TransM in two ways:
val x1 = new TransM[List, ListM](new ListM)
val x2 = new TransM[ListM#T, ListM](new ListM)
I think ListM#T is redundant type parameter, so I'm trying to eliminate it:
trait M{
type T[X]
def from[A](f: T[A]): A
}
class ListM extends M {
type T[X] = List[X]
def from[A](f: T[A]) = f.head
}
class Trans[A, B <: M](val r: B#T[Option[A]])
class TransM[B <: M](val b: B) extends M {
type T[X] = Trans[X, B]
def from[Y] = (f: T[Y]) => b.from(f.r).get
}
to instantiate variable as
val x = new TransM[ListM](new ListM)
Unfortunately, the second implementation can't be compiled because of a type mismatch error:
type mismatch;
found : f.r.type (with underlying type B#T[Option[Y]])
required: TransM.this.b.T[?]
def from[Y] = (f: T[Y]) => b.from(f.r).get
^
Can I fix this issue and simplify my code or should I write boilerplate ListM#T everywhere?
#ziggystar says it: Drop the bound B and use M[X] directly:
class TransM[X[_]](val b: M[X]) extends M[CurriedTrans[X, M[X]]#Type] {
def from[A](f: T[A]) = b.from(f.r).get
}
val x1 = new TransM(new ListM)
You could consider to do the same for Trans and CurriedTrans. If you need the inner type of M, you can always expose it through a type member of Trans and CurriedTrans.

Adding a pairwise difference to generic collections - implicit resolution doesn't kick in

Ok, so I have this:
implicit final class RichIterableLike[A, Repr <: IterableLike[A, Repr]](val it: Repr)
extends AnyVal {
def pairDiff[To](implicit num: Numeric[A], cbf: CanBuildFrom[Repr, A, To]): To = {
val b = cbf(it)
val iter = it.iterator
if (iter.hasNext) {
var pred = iter.next()
while (iter.hasNext) {
import num.mkNumericOps
val succ = iter.next()
b += succ - pred
pred = succ
}
}
b.result()
}
}
This compiles, but doesn't kick in:
val stabs = IndexedSeq(1.0, 2.0, 3.0)
stabs.pairDiff
Gives: value pairDiff is not a member of IndexedSeq[Double]
Explicit conversion works:
new RichIterableLike[Double, IndexedSeq[Double]](stabs).pairDiff
... how to fix this?
EDIT
If I apply the approach of this answer, it works:
implicit final class RichIterableLike[A, CC[~] <: Iterable[~]](val it: CC[A])
extends AnyVal {
def pairDiff[To](implicit num: Numeric[A], cbf: CanBuildFrom[CC[A], A, To]): To = {
...
}
But the question remains, what is the crucial difference that makes the implicit lookup kick in in the latter case.
In order for the implicit lookup to work it needs a link between A and Repr (IterableLike demands that link). You pass it in as an argument, so that argument should be typed as Repr[A]. That means you need to modify your signature so it will look something like this:
RichIterableLike[A, Repr[X] <: IterableLike[X, Repr[X]]](val it: Repr[A])
With the above signature you say:
I have an object that accepts a type parameter, I will name that object Repr and when you pass it in I would like to capture the type parameter as well. I will name that type parameter A. As an extra condition I want the type of Repr to conform to the signature of IterableLike