0

I read existing questions about how type definitions with parameterized bounds are illegal inside blocks (or something to that effect), but it doesn't help me in my problem:

type Cons[X]

class Higher[C[X] <: Cons[X]]
type AnyHigher = Higher[C] forSome { type C[X] <: Cons[X] }

Seq[AnyHigher]().map { h => h }

compiler:

can't existentially abstract over parameterized type C
Seq[AnyHigher]().map { h => h }

The element type of the input collection is irrelevant, the problem lies only in the return type of the mapping function. Is there any way around this? I tried various refactors: making the mapping function a method, cheating by making the code generic and executing parameterized with AnyHigher, writing my own recursion, but nothing helps.

Dmytro Mitin
  • 48,194
  • 3
  • 28
  • 66
Turin
  • 2,208
  • 15
  • 23
  • 1
    Are you sure you need existentials? they are notoriously difficult to understand and use and there was even an attempt to remove them from the language. – Luis Miguel Mejía Suárez Nov 19 '20 at 15:13
  • Yes, they aren't there in Scala 3. But unfortunately, I don't think there is another way to define `LUB` of all `Higher` instances. And the whole 'parameterized with a type constructor' is absolutely central to the project, there is no way I can really change the definition. At worst, I'll introduce a sealed, non-parameterized type with a casting method down to the existential type. – Turin Nov 19 '20 at 15:27
  • why exactly do you want to abstract over all higher? have you looked at a **typeclass** instead? – Luis Miguel Mejía Suárez Nov 19 '20 at 15:35
  • Type classes are for types. Here I don't have a type only a type constructor, because I use phantom types for type-level indexing of `Higher` Also, type class would make sense if there where may different types of `Higher` while there is only one (when, a couple, but it is `Const` which is an actual variable and the payload of the whole design. – Turin Nov 23 '20 at 10:55
  • so **Monad** is not a typeclass because it works on type constructors _(which btw are also types)_? - If there is only one **Cons** what is the point of abstracting? - Finally, I am not saying that a typeclass would be the solution I just asked why you need this since it is not clear from the question and just said that maybe a typeclass would work. – Luis Miguel Mejía Suárez Nov 23 '20 at 12:57

2 Answers2

2

A workaround is

Seq[AnyHigher]().map(new (AnyHigher => AnyHigher) {
  override def apply(h: AnyHigher): AnyHigher = h
})

"can't existentially abstract over parameterized type..."

Another workaround is to make C[X] a type member rather than type parameter

type Cons[X]

class Higher {
  type C[X] <: Cons[X]
}
object Higher {
  type Aux[C0[X0] <: Cons[X0]] = Higher { type C[X] = C0[X] }
}

type AnyHigher = Higher

Seq[AnyHigher]().map(h => h)

and use Higher.Aux[C] instead of Higher[C] and Higher instead of Higher[C] forSome { type C[X] <: Cons[X] }.

http://dotty.epfl.ch/docs/reference/dropped-features/existential-types.html

https://scalacenter.github.io/scala-3-migration-guide/docs/incompatibilities/dropped-features.html#existential-type

Existential type is a dropped feature, which makes the following code illegal.

def foo: List[Class[T]] forSome { type T }

The proposed solution is to introduce an enclosing type that carries a dependent type:

trait Bar {   
  type T   
  val value: List[Class[T]] 
}

def foo: Bar
Dmytro Mitin
  • 48,194
  • 3
  • 28
  • 66
  • Thanks! Quick as ever :) I know they are being dropped, but in Scala 3 this works without exitentials, the wildcard stands for any kind. I am trying to code with mind of planned changes, but unfortunately too often scala 3 style doesn't compile in `2.13` – Turin Nov 19 '20 at 15:34
0

Why @Dmytro Mitin's answer is the correct one as both type safe, and offering a local solution not needing changes to the types, it is a bit of a mouthful. So, at least temporarily, I opted for introducing a super type and casting:

    sealed trait AnyHigher {
        def apply() :Higher[T] forSome { type T[O] <: Cons[O] } =
            this.asInstanceOf[Higher[T] forSome { type T[O] <: Cons[O] }]
    }
    
    class Higher[T[O] <: Cons[O]] extends AnyHigher

    Seq[AnyHigher]().map(t => t)

It's ugly, but a bit less and it's temoporary until Scala 3, which has wildcard types covering any kinds.

Turin
  • 2,208
  • 15
  • 23