sinonimo significado rigurosidad rigor que logico investigacion filosofia falta definicion cientifico ciencia academico haskell lazy-evaluation evaluation strictness

haskell - significado - rigor sinonimo



¿Cuál es la relación entre los tipos sin clasificar y el rigor? (3)

Los tipos no compartidos, como Int# , y las funciones estrictas, como f (!x) = ... , son algo diferente, pero veo similitud conceptual: no permiten rachas / holgazanería de alguna manera. Si Haskell era un lenguaje estricto como Ocaml, cada función sería estricta y cada tipo no incluido. ¿Cuál es la relación entre los tipos sin caja y la exigencia de rigor?


Los argumentos de cualquier tipo se pueden hacer "estrictos", pero los únicos tipos de unboxed que tienen los tipos de recuadro correspondientes son Char# , Int# , Word# , Double# y Float# .

Si conoces idiomas de bajo nivel como C, es más fácil de explicar. Los tipos no compartidos son como int , double , etc., y los tipos encuadrados son como int* , double* , etc. Cuando tienes un int , ya sabes el valor completo tal como está representado en el patrón de bits, por lo tanto, no es perezoso También debe ser estricto, ya que todos los valores de int son válidos y no ⊥.

Sin embargo, dado un int* puede elegir desreferenciar el puntero más tarde para obtener el valor real (por lo tanto, perezoso), y es posible tener punteros no válidos (contiene ⊥, es decir, no estricto).


Los tipos no compartidos son necesariamente estrictos, pero no todos los valores estrictos están necesariamente desempaquetados.

data Foo a = Foo !a !a

tiene dos campos estrictos

data Bar a = Bar {-# UNPACK #-} !Int !a

tiene dos campos estrictos, pero el primero está desagrupado.

En última instancia, la razón por la cual los tipos de unboxed son (necesariamente) estrictos es que no hay lugar para almacenar el thunk, ya que son datos simples y tontos en ese punto.


Datos no empacados vs en caja

Para admitir el polimorfismo paramétrico y la laziness , por defecto, los tipos de datos Haskell se representan uniformemente como un puntero a un closure en el montón , con una estructura como esta:

Estos son valores "encasillados". Un objeto no empacado se representa por el valor mismo directamente, sin indirección o cierre. Int está encuadrado, pero Int# está desagrupado.

Los valores diferidos requieren una representación encuadrada. Los valores estrictos no: pueden representarse como cierres totalmente evaluados en el montón, o como estructuras primitivas unboxed. Tenga en cuenta que el etiquetado del puntero es una optimización que podemos usar en los objetos enmarcados, para codificar el constructor en el puntero al cierre.

La relación con la estricta

Normalmente, los compiladores de lenguaje funcional generan valores no compartidos de forma ad hoc. En Haskell, sin embargo, los valores no compartidos son especiales. Ellos:

  1. tienen un tipo diferente, # ;
  2. solo se puede usar en lugares especiales; y
  3. no están elevados, por lo que no se representan como un puntero a un valor de montón.

Debido a que no están elevados, son necesariamente estrictos. La representación de la pereza no es posible.

Así que los tipos de unboxed particulares, como Int# , Double# , realmente se representan como dobles o int en la máquina (en notación C).

Análisis de Estricto

Por separado, GHC hace un análisis de rigurosidad de los tipos de Haskell regulares. Si se determina que el uso de un valor es estricto, es decir, nunca puede estar "indefinido", el optimizador puede reemplazar todos los usos del tipo regular (por ej., Int ) con un unboxed ( Int# ), ya que sabe que el uso de Int siempre es estricto, y por lo tanto, el reemplazo con el tipo más eficiente (y siempre estricto) Int# es seguro.

Por supuesto, podemos tener tipos estrictos sin tipos unboxed, por ejemplo, una lista polimórfica de elementos estrictos:

data List a = Empty | Cons !a (List a)

es estricto en sus elementos, pero no los representa como valores no compartidos.

Esto también señala el error que cometió con respecto a lenguajes estrictos, como OCaml . Todavía necesitan compatibilidad con el polimorfismo, por lo que proporcionan una representación uniforme o se especializan en tipos de datos y funciones para todos los tipos. GHC por defecto usa una representación uniforme, al igual que OCaml, aunque GHC también puede especializar tipos y funciones ahora (como plantillas de C ++).