2010-10-15 9 views
5

Estoy tratando de definir una excepción en OCaml que acepte un par de listas tuplas como argumento. Sin embargo, esta situación no funciona?Definición de excepciones con tupla como argumento

# exception Foo of string list * string list;; 
exception Foo of string list * string list 
# let bar = (["a"], ["b"; "c"; "d"]);; 
val bar : string list * string list = (["a"], ["b"; "c"; "d"]) 
# raise(Foo bar);; 
Error: The constructor Foo expects 2 argument(s), 
     but is applied here to 1 argument(s) 

Sin embargo, si hago esto, funciona

# raise (Foo (["a"], ["b"; "c"; "d"]));; 
Exception: Foo (["a"], ["b"; "c"; "d"]). 

Cuál es el problema? ¡Gracias!

Respuesta

11

Estás viendo esto mal (aunque no te culpo: es bastante sorprendente al principio). Puede parecerle que los constructores siguen la sintaxis Name of type, donde la parte de tipo sigue la sintaxis de tipo normal (que le permite contener tuplas).

En realidad, tuplas y constructores siguen la misma sintaxis exacta: un constructor no es más que una tupla con un nombre delante de él:

tuple/constructor == [name of] type [* type] [* type] ... 

Así, el * en una definición de constructor no son parte de la sintaxis de tupla, son parte de la sintaxis del constructor. Está literalmente definiendo un constructor como este nombre, seguido de N argumentos en oposición a este nombre, seguido de un argumento que es una tupla.

La razón detrás de esta sutil diferencia en el comportamiento es la de rendimiento. En este momento, tuplas y constructores están representados en la memoria como tal:

[TYPE] [POINTER] [POINTER] [POINTER] 

Esta es una representación bastante compacto y eficiente.Si se pudieran acceder a los múltiples argumentos de un constructor como una tupla, esto requeriría que el tiempo de ejecución represente esa tupla independientemente del constructor (para que sea direccionable independientemente) y así sería:

[TYPE] [POINTER] 
      | 
      v 
      [TYPE] [POINTER] [POINTER] [POINTER] 

Esto usaría marginalmente más memoria, requeriría el doble de asignaciones cuando se usa un constructor, y reduciría el rendimiento de las tuplas de coincidencia de patrones (debido a una desreferencia adicional). Para conservar el máximo rendimiento, el name of type * type se representa con el primer patrón, y debe escribir explícitamente name of (type * type) para cortar el * del of y volver al segundo patrón.

Tenga en cuenta que se accede a ambos patrones a través de la misma sintaxis de coincidencia de patrones y construcción: name (arg,arg). Esto significa que la inferencia de tipo no puede deducir el patrón en función del uso. Esto no es un problema para los constructores normales, que siempre se definen en una definición de tipo, pero causa variantes (que no necesitan una definición preliminar) para volver automáticamente a la segunda versión.

Lectura adicional sobre la representación de memoria de los tipos here.

+0

¡Guau! Gracias por la larga explicación! Su comprensión de OCaml hace que parezca que usted es de INRIA – axsuul

4

Foo es un constructor de una excepción con 2 parámetros. Tendría que descomponer la tupla y pasar cada parte en ella.

# exception Foo of string list * string list;; 
exception Foo of string list * string list 
# let bar = (["a"], ["b"; "c"; "d"]);; 
val bar : string list * string list = (["a"], ["b"; "c"; "d"]) 
# let a, b = bar in raise (Foo (a, b));; 
Exception: Foo (["a"], ["b"; "c"; "d"]). 

Si desea utilizar una tupla como el único parámetro, debe definir la excepción usando parens y pasar la tupla en.

# exception Foo of (string list * string list);; 
exception Foo of (string list * string list) 
# let bar = (["a"], ["b"; "c"; "d"]);; 
val bar : string list * string list = (["a"], ["b"; "c"; "d"]) 
# raise (Foo bar);; 
Exception: Foo (["a"], ["b"; "c"; "d"]). 
+0

Gracias :) Esto funciona como quiero – axsuul

5

En este sentido, los constructores de excepción de OCaml son como ordinaria constructores:

Constr(1,2,3) es una construcción sintáctica especial en la que no se produce triple. Por otro lado, un triple ocurre en Constr((1,2,3)). La implementación coincide con este comportamiento, con Constr(1,2,3) asignado como un solo bloque, y Constr((1,2,3)) como dos bloques, uno que contiene un puntero al otro (el triple). En la representación en tiempo de ejecución de Constr(1,2,3), no hay un triple para obtener un puntero, y si necesita uno, debe asignar uno nuevo.

Nota: Constr(((1,2,3))) es equivalente a Constr((1,2,3)). En Constr(((1,2,3))), los paréntesis del medio se interpretan como ir alrededor de la expresión (1,2,3), y los paréntesis alrededor de una expresión se olvidan en el árbol de sintaxis abstracta.

Cuestiones relacionadas