Home Ciencia y Tecnología Cálculo secuente vs CPS: la perspectiva de un compilador sobre los consumidores...

Cálculo secuente vs CPS: la perspectiva de un compilador sobre los consumidores y las estrategias de evaluación

38
0

  1. Introducción

  2. Traduciendo al cálculo secuente

    2.1 Expresiones aritméticas

    2.2 Leting Bindings

    2.3 Definiciones de nivel superior

    2.4 Datos algebraicos y tipos de Codata

    2.5 Funciones de primera clase

    2.6 Operadores de management

  3. Evaluación dentro de un contexto

    3.1 Contextos de evaluación para la diversión

    3.2 Centrarse en la evaluación en el núcleo

  4. Reglas de escritura

    4.1 Reglas de escritura para la diversión

    4.2 Reglas de escritura para el núcleo

    4.3 Tipo de solidez

  5. Perspectivas

    5.1 Los contextos de evaluación son de primera clase

    5.2 Los datos son duales a Codata

    5.3 Los vínculos de alquiler son duales para los operadores de management

    5.4 La transformación del caso del caso

    5.5 consumidores directos e indirectos

    5.6 Llamado por valor, llamado por nombre y eta-laws

    5.7 Lógica lineal y la dualidad de las excepciones

  6. Trabajo relacionado

  7. Conclusión, declaración de disponibilidad de datos y reconocimientos

A. La relación con el cálculo secuente

B. Reglas de escritura para la diversión

C. Semántica operativa de la etiqueta/goto

Referencias

5.5 consumidores directos e indirectos

Como se menciona en la introducción, un competidor pure del cálculo secuente como una representación intermedia es el estilo de aprobación de continuación (CPS). En CPS, los contextos de evaluación REIFIGE están representados por funciones. Esto hace que los tipos resultantes de programas en CPS posiblemente sean más difíciles de entender. Sin embargo, existe otra ventaja del cálculo secuente sobre CPS según lo descrito por Downen et al. [2016]. La representación de primera clase de los consumidores en el cálculo secuente nos permite distinguir entre dos tipos diferentes de consumidores: consumidores directos, es decir, destructores y consumidores indirectos. En explicit, esto permite encadenar a los consumidores directos en el núcleo de manera comparable a la diversión.

Supongamos que tenemos un tipo de Codata con destructores Get y establecidos para obtener y establecer el valor de una referencia. Ahora considere la siguiente cadena de llamadas de Destructor en una referencia 𝑟 en diversión

𝑟 .set (3) .set (4) .get ()

Un compilador podría usar una regla de reescritura personalizada definida por el usuario para reescribir dos llamadas posteriores para establecer solo la segunda llamada. En el núcleo, el ejemplo anterior se ve de la siguiente manera:

𝜇𝛼.⟨𝑟 | set (3; set (4; get (𝛼)⟩

Todavía podemos ver inmediatamente el encadenamiento directo de destructores y, por lo tanto, aplicar esencialmente la misma regla de reescritura. En CPS, sin embargo, el ejemplo preferiría convertirse en

𝜆𝑘. 𝑟 .set (3; 𝜆𝑠. 𝑠.set (4; 𝜆𝑡. 𝑡 .get (𝑘))))

El encadenamiento de los destructores queda ofuscado por las indirecciones introducidas al representar las continuaciones para cada destructor en función. Para aplicar la regla de reescritura personalizada mencionada anteriormente, es necesario ver a través de Lambdas, es decir, la regla de reescritura personalizada debe transformarse para ser aplicable.

5.6 Llamado por valor, llamado por nombre y eta-laws

En la Sección 2.2 ya señalamos la existencia de declaraciones ⟨𝜇𝛼.𝑠1 | 𝜇𝑥.𝑠 ˜ 2⟩ que se llaman pares críticos porque se pueden reducir a priori a cualquiera de [𝜇𝑥.𝑠 ˜ 2/𝛼] OR𝑠2 [𝜇𝛼.𝑠1/𝑥]. Curien y Herbelin ya discutieron a estos pares críticos. [2000] cuando introdujeron el 𝜆𝜇𝜇˜calculus. Una solución es elegir una orden de evaluación, ya sea llamada por valor (CBV) o llamado por nombre (CBN), que decide cuáles de las dos declaraciones debemos evaluar, y en este documento elegimos usar siempre la orden de evaluación de llamadas por valor. Wadler también ha discutido la diferencia entre estas dos opciones [2003]. Tenga en cuenta que esta libertad para la estrategia de evaluación es otra ventaja del cálculo secuente sobre el estilo de aprobación de continuación, ya que este último siempre fija una estrategia de evaluación.

El orden de evaluación que elegimos tiene una consecuencia importante para las optimizaciones que podemos realizar en el compilador. Si elegimos llamar por valor, no se nos permite usar todas las ecuaciones 𝜂 para los tipos de Codata, y si usamos llamadas por nombre, entonces no se nos permite usar todas las ecuaciones 𝜂 para los tipos de datos. Ilustramos el problema en el caso de los tipos de Codata con el siguiente ejemplo:

⟨COCASE ap (𝑥; 𝛼)⟩ | 𝜇𝑥.𝑠 ˜ 2⟩ ≡𝜂 ⟨𝜇𝛽.𝑠1 | 𝜇𝑥.𝑠 ˜ 2⟩

Suponemos que 𝑥 y 𝛼 no parecen libres en 𝑠1. La transformación 𝜂 es solo la ley 𝜂 ordinaria para las funciones, pero se aplica a la representación de funciones como tipos de codata. La declaración en el lado izquierdo cut back el 𝜇˜ primero bajo la orden de evaluación de llamadas por valor y llamado por nombre, es decir

Sin embargo, el lado de la derecha de la igualdad 𝜂 cut back el 𝜇 Primero bajo orden de evaluación de llamadas por valor, es decir

Por lo tanto, la igualdad de 𝜂 solo es válida bajo orden de evaluación de llamadas por nombre. Este ejemplo muestra que la validez de aplicar esta regla 𝜂 como optimización depende de si el lenguaje usa llamadas por valor o llamado por nombre. Si en su lugar usamos un tipo de datos como el par, una reducción 𝜂 comparable solo daría el mismo resultado que la declaración unique cuando se usa el valor de llamada.

5.7 Lógica lineal y la dualidad de las excepciones

Hemos introducido el par de tipos de datos (𝜎, 𝜏) y el tipo de Codata lPair (𝜎, 𝜏) como dos formas diferentes de formalizar tuplas. El par de tipo de datos (𝜎, 𝜏) está definido por el TUP del constructor cuyos argumentos se evalúan con entusiasmo, por lo que este tipo corresponde a tuplas estrictas en idiomas como ML u OCAML. El tipo Codata LPair (𝜎, 𝜏) es un par perezoso que se outline por sus dos proyecciones FST y SND, y solo cuando invocamos la primera o segunda proyección, comenzamos a calcular su contenido. Esto está más cerca de cómo se comportan las tuplas en un lenguaje perezoso como Haskell.

Lógica lineal [Girard 1987; Wadler 1990] agrega otra diferencia a estos tipos. En lógica lineal consideramos argumentos como recursos que no pueden duplicarse o descartarse arbitrariamente; Cada argumento a una función debe usarse exactamente una vez. Si seguimos esta disciplina más estricta, entonces tenemos que distinguir entre dos tipos diferentes de pares: para usar un par 𝜎 ⊗ 𝜏 𝜏 (pronunciado “tiempos” o “tensor”), tenemos que usar tanto el 𝜎 como el 𝜏, pero si queremos usar un par 𝜎 & 𝜏 (pronunciado “con”), debemos elegir usar el 𝜎 o el 𝜏. Ahora es fácil ver que el tipo 𝜎 ⊗ 𝜏 de la lógica lineal corresponde al par de tipos de datos (𝜎, 𝜏), ya que cuando coinciden en este tipo obtenemos dos variables en el contexto, una para 𝜎 y otra para 𝜏. El tipo 𝜎 & 𝜏 corresponde de manera comparable al tipo lPair (𝜎, 𝜏) que usamos invocando la primera o la segunda proyección, consumiendo todo el par.

Autores:

(1) David Binder, Universidad de Tübingen, Alemania;

(2) Marco Tzschentke, Universidad de Tübingen, Alemania;

(3) Marius Muller, Universidad de Tübingen, Alemania;

(4) Klaus Ostermann, Universidad de Tübingen, Alemania.


fuente