¿Deshabilitar selectivamente la subsunción en Scala? (escriba correctamente List.contains)

List("a").contains(5)

Porque unInt nunca puede estar contenido en una lista deString, esta debería generar un error en tiempo de compilación, pero no lo hace.

Prueba inútil y silenciosamente cadaString contenido en la lista para igualdad a5, que nunca puede ser cierto "5" nunca es igual a5 en Scala).

Esto ha sido nombrado "el problema 'contiene' ". Yalgunos han implicado que si un sistema de tipos no puede escribir correctamente esa semántica, entonces ¿por qué hacer un esfuerzo adicional para hacer cumplir los tipos? Así que considero que es un problema importante para resolver.

La parametrización de tipoB >: A deList.contains ingresa cualquier tipo que sea un supertipo del tipoA (el tipo de los elementos contenidos en la lista).

trait List[+A] {
   def contains[B >: A](x: B): Boolean
}

Este tipo de parametrización es necesaria porque el+A declara que la lista es covariante en el tipoA, por lo tantoA no se puede usar en la posición contravariante, es decir, como el tipo de un parámetro de entrada. Las listas covariantes (que deben ser inmutables) son mucho más potente para la extensión que las listas invariables (que pueden ser mutables).

A es unString en el ejemplo problemático anterior, peroInt no es un supertipo deString, ¿entonces qué pasó? Los subsunción implícita en Scala, decidió queAny es un supertipo mutuo de ambosString yInt.

El creador de Scala, Martin Odersky,sugiri que una solución sería limitar el tipo de entradaB solo a aquellos tipos que tienen un método igual queAny no tiene.

trait List[+A] {
   def contains[B >: A : Eq](x: B): Boolean
}

Pero eso no resuelve el problema, porque dos tipos (donde el tipo de entrada no es el supertipo del tipo de los elementos de la lista) podrían tener un supertipo mutuo que es un subtipo deAny, es decir, también un subtipo deEq. Por lo tanto, se compilaría sin error y la semántica escrita incorrectamente permanecería.

Desactivar la subsunción implícita en todas partesno es una solución ideal tampoco, porque la subsunción implícita es la razón por la cual el siguiente ejemplo de subsunción aAny trabajos. Y no querríamos que nos obliguen a usar conversiones de tipo cuando el sitio receptor (por ejemplo, pasar como argumento de función) ha tipeado correctamente la semántica para un supertipo mutuo (que podría ni siquiera serAny).

trait List[+A] {
   def ::[B >: A](x: B): List[B]
}

val x : List[Any] = List("a", 5) // see[1]

[1] List.apply llama al :: operador.

¿Entonces mi pregunta es cuál es la mejor solución para este problema?

Mi conclusión tentativa es que la subsunción implícita debe desactivarse en el sitio de definición donde la semántica no se escribe correctamente. Proporcionaré una respuesta que muestre cómo desactivar la subsunción implícita en el sitio de definición del método. ¿Hay soluciones alternativas?

Tenga en cuenta que este problema es general y no está aislado de las listas.

ACTUALIZA: Yo tengo presentó una solicitud de mejora y comenzó unscala hilo de discusión sobre este. También he agregado comentarios en las respuestas de Kim Stebel y Peter Schmitz que muestran que sus respuestas tienen una funcionalidad errónea. Por lo tanto no hay solución. También en el hilo de discusión mencionado anteriormente, expliqué por qué creo que la respuesta de soc no es correcta.

Respuestas a la pregunta(12)

Su respuesta a la pregunta