¿Qué es un tipo de clase superior en Scala?

275

Puede encontrar lo siguiente en la web:

  1. ¿Tipo más alto == constructor de tipo?

    class AClass[T]{...} // For example, class List[T]

    Algunos dicen que este es un tipo de tipo superior, ya que abstrae sobre tipos que cumplirían con la definición.

    Los tipos de tipo superior son tipos que toman otros tipos y construyen un nuevo tipo.

    Sin embargo, estos también se conocen como constructor de tipos . (Por ejemplo, en Programación en Scala ).

  2. ¿Tipo de tipo superior == constructor de tipo que toma el constructor de tipo como parámetro de tipo?

    En el artículo Generics of a Higher Kind , puedes leer

    ... tipos que se resumen sobre tipos que se resumen sobre tipos ('tipos de tipo superior') ... "

    lo que sugiere que

    class XClass[M[T]]{...} // or
    
    trait YTrait[N[_]]{...} // e.g. trait Functor[F[_]]

    Es un tipo de tipo superior.

Entonces, con esto en mente, es difícil distinguir entre constructor de tipos , tipo de tipo más alto y constructor de tipos que toma los constructores de tipos como parámetro de tipo , por lo tanto, la pregunta anterior.

Lutz
fuente
Se agregó el Functor de Landei como ejemplo.
Lutz

Respuestas:

284

Permítanme compensar el comienzo de algo de esta confusión lanzando algo de desambiguación. Me gusta usar la analogía con el nivel de valor para explicar esto, ya que las personas tienden a estar más familiarizadas con él.

Un constructor de tipos es un tipo que puede aplicar a argumentos de tipo para "construir" un tipo.

Un constructor de valores es un valor que puede aplicar a argumentos de valor para "construir" un valor.

Los constructores de valores generalmente se denominan "funciones" o "métodos". También se dice que estos "constructores" son "polimórficos" (porque pueden usarse para construir "cosas" de "formas" variables) o "abstracciones" (ya que abstraen sobre lo que varía entre las diferentes instancias polimórficas).

En el contexto de la abstracción / polimorfismo, el primer orden se refiere al "uso único" de la abstracción: abstrae sobre un tipo una vez, pero ese tipo en sí mismo no puede abstraer sobre nada. Los genéricos de Java 5 son de primer orden.

La interpretación de primer orden de las caracterizaciones anteriores de abstracciones son:

Un constructor de tipos es un tipo que puede aplicar a los argumentos de tipo adecuados para "construir" un tipo adecuado.

Un constructor de valores es un valor que puede aplicar a argumentos de valor adecuados para "construir" un valor adecuado.

Para enfatizar que no hay abstracción involucrada (supongo que se podría llamar a esto "orden cero", pero no he visto esto usado en ningún lado), como el valor 1o el tipo String, generalmente decimos que algo es un valor o tipo "adecuado".

Un valor apropiado es "inmediatamente utilizable" en el sentido de que no está esperando argumentos (no los abstrae). Piense en ellos como valores que puede imprimir / inspeccionar fácilmente (¡serializar una función es hacer trampa!).

Un tipo adecuado es un tipo que clasifica los valores (incluidos los constructores de valores), los constructores de tipos no clasifican ningún valor (primero deben aplicarse a los argumentos de tipo correctos para producir un tipo adecuado). Para crear una instancia de un tipo, es necesario (pero no suficiente) que sea un tipo adecuado. (Puede ser una clase abstracta o una clase a la que no tiene acceso).

"Orden superior" es simplemente un término genérico que significa el uso repetido de polimorfismo / abstracción. Significa lo mismo para los tipos y valores polimórficos. Concretamente, una abstracción de orden superior abstrae sobre algo que abstrae sobre algo. Para los tipos, el término "superior" es una versión de propósito especial del "orden superior" más general.

Por lo tanto, la versión de orden superior de nuestra caracterización se convierte en:

Un constructor de tipos es un tipo que puede aplicar a argumentos de tipo (tipos propios o constructores de tipos) para "construir" un tipo adecuado (constructor).

Un constructor de valores es un valor que puede aplicar a argumentos de valores (valores propios o constructores de valores) para "construir" un valor adecuado (constructor).

Por lo tanto, "orden superior" simplemente significa que cuando dices "abstraer sobre X", ¡lo dices en serio! Lo Xque se abstrae no pierde sus propios "derechos de abstracción": puede abstraer todo lo que quiera. (Por cierto, utilizo el verbo "abstracto" aquí para significar: dejar de lado algo que no es esencial para la definición de un valor o tipo, de modo que pueda ser variado / proporcionado por el usuario de la abstracción como argumento .)

Estos son algunos ejemplos (inspirados en las preguntas de Lutz por correo electrónico) de valores y tipos adecuados, de primer orden y de orden superior:

                   proper    first-order           higher-order

values             10        (x: Int) => x         (f: (Int => Int)) => f(10)
types (classes)    String    List                  Functor
types              String    ({type λ[x] = x})#λ   ({type λ[F[x]] = F[String]})#λ

Donde las clases utilizadas se definieron como:

class String
class List[T]
class Functor[F[_]]

Para evitar la indirección a través de la definición de clases, debe expresar de alguna manera funciones de tipo anónimo, que no se pueden expresar directamente en Scala, pero puede usar tipos estructurales sin demasiada sobrecarga sintáctica (el estilo se debe a https://stackoverflow.com / users / 160378 / retronym afaik):

En alguna versión hipotética futura de Scala que admita funciones de tipo anónimo, puede acortar esa última línea de los ejemplos a:

types (informally) String    [x] => x              [F[x]] => F[String]) // I repeat, this is not valid Scala, and might never be

(En una nota personal, lamento haber hablado alguna vez de "tipos de tipo superior", ¡son solo tipos después de todo! Cuando necesite absolutamente desambiguar, sugiero decir cosas como "parámetro de constructor de tipos", "miembro de constructor de tipos" , o "alias de constructor de tipos", para enfatizar que no se trata solo de los tipos adecuados).

ps: para complicar aún más las cosas, "polimórfico" es ambiguo de una manera diferente, ya que un tipo polimórfico a veces significa un tipo cuantificado universalmente, como Forall T, T => T, que es un tipo apropiado, ya que clasifica los valores polimórficos (en Scala, este valor puede ser escrito como el tipo estructural {def apply[T](x: T): T = x})

Adriaan Moors
fuente
1
ver también: adriaanm.github.com/research/2010/10/06/…
Adriaan Moors
55
El artículo de Adriaan "Polimorfismo de constructor de tipos" ahora en adriaanm.github.com/research/2010/10/06/…
Steven Shaw
Lo sigo leyendo como un pariente superior e imaginando un espíritu afín
Janac Meena
110

(Esta respuesta es un intento de decorar la respuesta de Adriaan Moors con información gráfica e histórica).

Los tipos de tipo superior son parte de Scala desde 2.5.

  • Antes de eso, Scala, como Java hasta ahora, no permitía usar el constructor de tipos ("genéricos" en Java) como parámetro de tipo para un constructor de tipos. p.ej

     trait Monad [M[_]]

    No fue posible.

    En Scala 2.5, el sistema de tipos se había ampliado por la capacidad de clasificar los tipos en un nivel superior (conocido como polimorfismo de constructor de tipos ). Estas clasificaciones se conocen como clases.

    Tipo y clase real, ** derivada ** de "Genéricos de un tipo superior" (Imagen derivada de genéricos de un tipo superior )

    La consecuencia es que ese constructor de tipos (p List. Ej. ) Podría usarse como otros tipos en la posición del parámetro de tipo de los constructores de tipos y, por lo tanto, se convirtieron en tipos de primera clase desde Scala 2.5. (Similar a las funciones que son valores de primera clase en Scala).

    En el contexto de un sistema de tipos que admite tipos superiores, podemos distinguir los tipos adecuados , tipos similares Into List[Int]de tipos de primer orden como Listy tipos de tipo superior como Functoro Monad(tipos que resumen sobre tipos que resumen sobre tipos).

    El sistema de tipos de Java en el otro lado no admite tipos y, por lo tanto, no tiene tipos de "tipo superior".

    Por lo tanto, esto debe verse en el contexto del sistema de tipos de soporte.

  • En el caso de Scala, a menudo ves ejemplos de un constructor de tipos como

     trait Iterable[A, Container[_]]

    con el título "Tipos más amables", por ejemplo, en Scala para programadores genéricos, sección 4.3

    Esto a veces es engañoso, porque muchos se refieren Containercomo un tipo de tipo superior y no Iterable, pero lo que es más preciso es,

    el uso de Containercomo parámetro constructor de tipo de un tipo de tipo superior (orden superior) aquí Iterable.

Lutz
fuente
80

El tipo de tipos ordinarios como Inty Char, cuyas instancias son valores, es *. El tipo de constructores de tipo unario como Maybees * -> *; constructores de tipo binario como Eitherhave kind ( curry ) kind * -> * -> *, y así sucesivamente. Puede ver los tipos como Maybey Eithercomo funciones de nivel de tipo: toman uno o más tipos y devuelven un tipo.

Una función es de orden superior si tiene un orden mayor que 1, donde el orden es (informalmente) la profundidad de anidación, a la izquierda, de las flechas de función:

  • Orden 0: 1 :: Int
  • Orden 1: chr :: Int -> Char
  • Orden de 2: fix :: (a -> a) -> a,map :: (a -> b) -> [a] -> [b]
  • Orden 3: ((A -> B) -> C) -> D
  • Orden 4: (((A -> B) -> C) -> D) -> E

Entonces, para resumir , un tipo de tipo superior es solo una función de orden superior de nivel de tipo.

  • Orden 0: Int :: *
  • Orden 1: Maybe :: * -> *
  • Orden 2: Functor :: (* -> *) -> Constraint—higher-kinded: convierte constructores de tipo unario en restricciones de tipo de clase
Jon Purdy
fuente
Ok, lo tengo, ¿cuáles serían los ejemplos en Scala para (* ⇒ *) ⇒ * y (* ⇒ *) ⇒ (* ⇒ *)? ¿Funcionaría el Functor de Landei en la primera categoría o más bien en la segunda?
Lutz
1
@lutz: Estaría en la primera categoría: Functorproduce un tipo apropiado (bueno, rasgo, pero la misma idea) a Functor[F[_]]partir de un constructor de tipos F.
Jon Purdy
1
@ Jon: Una publicación muy perspicaz, gracias. ¿Se (* => *) => (* => *)puede expresar el convertidor de tipos en Scala? Si no, en cualquier otro idioma?
Eugen Labun
@JonPurdy La comparación entre * ⇒ * ⇒ *curry es muy útil. ¡Gracias!
Lifu Huang
(* ⇒ *) ⇒ (* ⇒ *)También se puede deletrear (* ⇒ *) ⇒ * ⇒ *. Se puede expresar en Scala como Foo[F[_], T]. Este es el tipo de tipo como (en Haskell) newtype Twice f a = Twice (f (f a))(por ejemplo, Twice Maybe IntMaybe (Maybe Int), Twice [] Char[[Char]]) o algo más interesante como la mónada libre data Free f a = Pure a | Free (f (Free f a)).
Jon Purdy
37

Yo diría: un tipo de tipo superior se resume sobre un constructor de tipos. Por ejemplo, considere

trait Functor [F[_]] {
   def map[A,B] (fn: A=>B)(fa: F[A]): F[B]
}

Aquí Functorhay un "tipo de tipo superior" (como se usa en el "Genéricos de un tipo superior" ). No es un tipo de constructor concreto ("de primer orden") List(que abstrae solo sobre los tipos adecuados). Resume sobre todos los constructores de tipo unario ("primer orden") (como se indica con F[_]).

O para decirlo de otra manera: en Java, tenemos constructores de tipo claro (p. Ej. List<T> ), pero no tenemos "tipos de tipo superior", porque no podemos abstraer sobre ellos (por ejemplo, no podemos escribir la Functorinterfaz definida anteriormente - al menos no directamente ).

El término "polimorfismo de orden superior (constructor de tipos)" se utiliza para describir sistemas que admiten "tipos de tipo superior".

Landei
fuente
Esto es lo que pensé, pero parece contradecir la respuesta de Jon, donde un "constructor de tipo concreto" ya es un "tipo de tipo superior".
Lutz
3
Si. Según la respuesta de Jon (según tengo entendido) List<T>en Java sería un constructor de tipo unario, ya que tiene claramente el tipo * -> *. Lo que falta en la respuesta de Jon es que debe ser capaz de abstraer sobre "todo" (y no solo sobre el segundo *como en Java) para llamarlo un tipo de tipo superior.
Landei
@Landai: El Scala en papel para programadores genéricos en la sección 4.3 sugiere que el rasgo Iterable [A, Container [_]] sea de un tipo de tipo más alto (aunque no está claro si se quiere decir Iterator o Container) en el otro lado vero690 en la sección 2.3.1 usa el término constructor de tipo de tipo superior para algo como (* -> *) -> * (operador de tipo parametrizado con un constructor de tipo de orden superior) que se parece al Iterator o al rasgo Functor.
Lutz
1
Esto probablemente sea correcto, pero creo que estamos comenzando a dividir los pelos aquí. El punto importante con respecto a los tipos de tipo superior es que no solo están involucrados los constructores de tipos (ordenar un polimorfismo de constructor de tipo), sino que podemos abstraer sobre el tipo concreto de los constructores de ese tipo (polimorfismo de constructor de tipo de orden superior). Tenemos la capacidad de abstraer lo que queramos sin restricciones (en relación con los tipos y los constructores de tipos), lo que hace que sea menos interesante nombrar todas las versiones posibles de esa característica. Y me duele el cerebro.
Landei
2
En general, es importante distinguir las definiciones y referencias aquí. La definición def succ(x: Int) = x+1introduce el "constructor de valores" (vea mi otra respuesta para lo que quiero decir con esto) succ(nadie se referiría a este valor como succ (x: Int)). Por analogía, Functores el tipo (de hecho, más amable) definido en su respuesta. Una vez más, no debe referirse a ella como Functor[F[_]](¿qué es Flo que es? _Que no están en su alcance por desgracia, el azúcar sintáctica para los existenciales enturbia las aguas aquí haciendo?! F[_]Abreviatura de F[T forSome {type T}])
Adriaan Moros
1

Scala REPL proporciona un :kindcomando que

scala> :help kind

:kind [-v] <type>
Displays the kind of a given type.

Por ejemplo,

scala> trait Foo[A]
trait Foo

scala> trait Bar[F[_]]
trait Bar

scala> :kind -v Foo
Foo's kind is F[A]
* -> *
This is a type constructor: a 1st-order-kinded type.

scala> :kind -v Foo[Int]
Foo[Int]'s kind is A
*
This is a proper type.

scala> :kind -v Bar
Bar's kind is X[F[A]]
(* -> *) -> *
This is a type constructor that takes type constructor(s): a higher-kinded type.

scala> :kind -v Bar[Foo]
Bar[Foo]'s kind is A
*
This is a proper type.

El :helpproporciona definiciones claras, así que creo que vale la pena publicarlo aquí en su totalidad (Scala 2.13.2)

scala> :help kind

:kind [-v] <type>
Displays the kind of a given type.

    -v      Displays verbose info.

"Kind" is a word used to classify types and type constructors
according to their level of abstractness.

Concrete, fully specified types such as `Int` and `Option[Int]`
are called "proper types" and denoted as `A` using Scala
notation, or with the `*` symbol.

    scala> :kind Option[Int]
    Option[Int]'s kind is A

In the above, `Option` is an example of a first-order type
constructor, which is denoted as `F[A]` using Scala notation, or
* -> * using the star notation. `:kind` also includes variance
information in its output, so if we ask for the kind of `Option`,
we actually see `F[+A]`:

    scala> :k -v Option
    Option's kind is F[+A]
    * -(+)-> *
    This is a type constructor: a 1st-order-kinded type.

When you have more complicated types, `:kind` can be used to find
out what you need to pass in.

    scala> trait ~>[-F1[_], +F2[_]] {}
    scala> :kind ~>
    ~>'s kind is X[-F1[A1],+F2[A2]]

This shows that `~>` accepts something of `F[A]` kind, such as
`List` or `Vector`. It's an example of a type constructor that
abstracts over type constructors, also known as a higher-order
type constructor or a higher-kinded type.
Mario Galic
fuente