In Scala 2 or 3, is there a higher kind argument extractor without using match type?
Asked Answered
Z

2

3

Here is a short example in Scala 3:

  type Ext[S <: Seq[_]] = S match {
    case Seq[t] => t
  }
  
trait XX[A, B <: Seq[A]]

trait XX1[B <: Seq[_]] extends XX[Ext[B], B]

So far it appears to be working, but when combining with type class the mask started to peel off

implicitly[Ext[Seq[Int]] =:= Int] // e.scala: Cannot prove that e.Ext[Seq[Int]] =:= Int

It may be caused by a bug in compatibility between Scala type class & match types. At this moment, the only way to circumvent this appears to be not using match type. Is it possible in Scala 2 or Scala 3?

UPDATE 1: I have tried the following alternatives:

  type Ext[S] = S match {
    case Seq[t] => t
  } // success!

  type Ext[S <: Any] = S match {
    case Seq[t] => t
  } // success!

  type Ext[S <: Seq[Any]] = S match {
    case Seq[t] => t
  } // same error

So I'm fairly certain this is a bug. Again, the problem is how to avoid using match type from the beginning?

Zanazander answered 24/3, 2023 at 20:9 Comment(0)
A
1

Looks like a bug.

There are bugs in match types with type bounds:

https://github.com/lampepfl/dotty/issues/11205

https://github.com/lampepfl/dotty/issues/16058

https://github.com/lampepfl/dotty/issues/16504

Some of them are fixed:

https://github.com/lampepfl/dotty/issues/6758

https://github.com/lampepfl/dotty/issues/6697

https://github.com/lampepfl/dotty/issues/14151

https://github.com/lampepfl/dotty/issues/14477

https://github.com/lampepfl/dotty/issues?page=1&q=is%3Aissue+is%3Aopen+label%3Aarea%3Amatch-types

An alternative to match types is type classes

trait ExtTC[S]:
  type Out
object ExtTC:
  type Aux[S, Out0] = ExtTC[S] { type Out = Out0 }

  given [S <: Seq[T], T]: Aux[S, T] = null

val ext = summon[ExtTC[Seq[Int]]]
summon[ext.Out =:= Int] // compiles

This can't help with type inference (In scala 3, is it possible to make covariant/contravariant type constructor to honour coercive subtyping?)

trait XX[A, B <: Seq[A]]

// doesn't compile: Type argument B does not conform to upper bound Seq[XX1.this.ext.Out]
trait XX1[B <: Seq[_]](using val ext: ExtTC[B]) extends XX[ext.Out, B] 

but can help with implicit resolution

trait XX[A, B](using B <:< Seq[A])

trait XX1[B <: Seq[_]](using val ext: ExtTC[B]) extends XX[ext.Out, B] // compiles
Ahmednagar answered 25/3, 2023 at 15:0 Comment(5)
Wow, looks like it really can't be simplified any further. I'll wait for a few days before accepting it as the canonical answerZanazander
github.com/lampepfl/dotty/issues/17149Ahmednagar
Yeah I filed it and saw no triage in the weekend. This is already my 3rd despite not an active user, they start to erode my confidence of its engineering quality. I may need to prioritise solving #75292278 firstZanazander
You expect people to triage your issues on the weekend?Casuist
@SethTisue Whom do you ask? :)Ahmednagar
E
1

I don't have much experience in Scala3, but I guess what you're looking for is this:

type Ext[S] = S match {
  case Seq[t] => t
}

You don't need to specify that S is a Seq, since you don't provide selectors for other kinds, you cannot also use other types than Seq in compile-time for Ext:

implicitly[Ext[Option[String]] =:= String] // compile-time error
// selector Option[String] matches none of the cases

Not sure if this is a bug, at the first glance I was like, ok, you're matching S (which is a kind), against a type (namely Seq[t]), so the compiler cannot prove that for you. But it can still match against the kinds though:

type Ext[S <: Seq[_]] = S match {
  case Vector[t] => t
  case List[_] => String
}

implicitly[Ext[Vector[Float]] =:= Float]
implicitly[Ext[List[_]] =:= String]
Exclamatory answered 24/3, 2023 at 21:43 Comment(4)
interesting ... so case Vector[t] works but case Seq[t] doesn't? What's the difference?Zanazander
Also, it would be interesting to submit it and argue over how buggy it is on the issue tracker, but here I'm merely asking for a short-term circumvention, one that works immediately for this particular simple caseZanazander
@Zanazander "so case Vector[t] works but case Seq[t] doesn't?" No, I didn't mean it, there's a difference about S in the first and last code block. If you take a look at the first code block, it would work as you expect it to. The last code block demonstrates how matching against kinds and types might be different, in the last code block, S is a kind, which is a subtype of Seq, so it can be a List, or a concrete Vector of any type. In the first code block, S is a type (note the difference between kind and type here), so it can be any generic Seq, or a normal class, basically any type.Exclamatory
Thanks a lot I have updated my question. I'm skeptical of the conjecture that S can be a kind/2-type in any case, as Seq[Any] is obviously a 1-type, unlike Seq[?]Zanazander
A
1

Looks like a bug.

There are bugs in match types with type bounds:

https://github.com/lampepfl/dotty/issues/11205

https://github.com/lampepfl/dotty/issues/16058

https://github.com/lampepfl/dotty/issues/16504

Some of them are fixed:

https://github.com/lampepfl/dotty/issues/6758

https://github.com/lampepfl/dotty/issues/6697

https://github.com/lampepfl/dotty/issues/14151

https://github.com/lampepfl/dotty/issues/14477

https://github.com/lampepfl/dotty/issues?page=1&q=is%3Aissue+is%3Aopen+label%3Aarea%3Amatch-types

An alternative to match types is type classes

trait ExtTC[S]:
  type Out
object ExtTC:
  type Aux[S, Out0] = ExtTC[S] { type Out = Out0 }

  given [S <: Seq[T], T]: Aux[S, T] = null

val ext = summon[ExtTC[Seq[Int]]]
summon[ext.Out =:= Int] // compiles

This can't help with type inference (In scala 3, is it possible to make covariant/contravariant type constructor to honour coercive subtyping?)

trait XX[A, B <: Seq[A]]

// doesn't compile: Type argument B does not conform to upper bound Seq[XX1.this.ext.Out]
trait XX1[B <: Seq[_]](using val ext: ExtTC[B]) extends XX[ext.Out, B] 

but can help with implicit resolution

trait XX[A, B](using B <:< Seq[A])

trait XX1[B <: Seq[_]](using val ext: ExtTC[B]) extends XX[ext.Out, B] // compiles
Ahmednagar answered 25/3, 2023 at 15:0 Comment(5)
Wow, looks like it really can't be simplified any further. I'll wait for a few days before accepting it as the canonical answerZanazander
github.com/lampepfl/dotty/issues/17149Ahmednagar
Yeah I filed it and saw no triage in the weekend. This is already my 3rd despite not an active user, they start to erode my confidence of its engineering quality. I may need to prioritise solving #75292278 firstZanazander
You expect people to triage your issues on the weekend?Casuist
@SethTisue Whom do you ask? :)Ahmednagar

© 2022 - 2024 — McMap. All rights reserved.