Prueba de diseño de la semántica de valor / referencia de Haskell


8

En lenguajes imperativos, es trivial diseñar una prueba de programación del uso del lenguaje de "semántica de valor" o "semántica de referencia". Se podría hacer lo siguiente y verificar el valor de a(where Vertex {one, two, three :: Integer}):

a := Vertex 3 4 5
b := a
one b   :=  6
two b   :=  8
three b := 10

Sin embargo, dado que las variables son inmutables en lenguajes funcionales, esta prueba no funcionará en dichos lenguajes.

Sé muy poco sobre Haskell (y la programación funcional en general), pero entiendo que usa semántica de valor. ¿Es posible idear un experimento de programación que distinga entre un "registro de referencia" y un "registro val" en Haskell?


1
En la segunda oración, ¿quiso decir "verificar el valor de a"?


Haskell es referencialmente transparente , [1] lo que significa que 'semántica de valor' y 'semántica de referencia' son equivalentes en Haskell. [1] Los geeks del lenguaje de programación tendrán un largo debate sobre esa declaración, pero es bastante tradicional usarlo en el sentido que acabo de hacer.
Jonathan Cast el

Respuestas:


9

No existe tal prueba, porque, sin mutabilidad, la distinción no es significativa (como demostró).


3
Vale la pena señalar que algunas partes de Haskell ( IORef, MVar, etc.) son mutables y se comportan como referencias ( hpaste.org/81192 )
Daniel Gratzer

9

Haskell no tiene referencias (una referencia es un objeto mutable y Haskell no tiene objetos mutables (directamente accesibles)). Por lo tanto, las llamadas a funciones usan semántica de valor, como por defecto. De hecho, esta es una propiedad importante de los lenguajes funcionales puros: una función no puede modificar su argumento.

La semántica del valor no implica que la copia ocurra debajo del capó. Solo necesita copiar la parte de un valor que la función modifica, lo que en un lenguaje puro significa que nunca necesita copiar nada.

Sin embargo, esta no es toda la historia. En cierto sentido, Haskell tiene semántica de referencia.

Si bien no tiene sentido probar si una función modifica su argumento (nunca lo hace), puede probar si una función usa (parte de) su argumento. Proporcione un argumento que no termine. Si la llamada a la función termina, usted sabe que la función no usó su argumento.

let bottom = bottom
let ignore x = 1
ignore bottom

Si evalúa bottom, no termina: se bottomexpande a sí mismo, hasta la saciedad. El término bottomno puede tener un valor. Pero si evalúa ignore bottom, el valor es 1. Esto muestra que llamar a la función ignoreno requiere calcular el valor de su argumento. En este sentido, Haskell tiene una semántica de referencia: lo que recibe una función no es un valor, sino algo que permite encontrar este valor. El término técnico es llamar por nombre (en oposición a llamar por valor ).

(Más precisamente, las implementaciones de Haskell usan llamada por necesidad . En llamada por valor, el argumento de una función se evalúa exactamente una vez, justo antes de llamar a la función. En llamada por nombre, el argumento se evalúa cada vez que se usa, lo que puede variar desde nunca hasta tantas veces como lo desee la función. En caso de necesidad, el argumento se evalúa como máximo una vez: se evalúa la primera vez que se usa, o nunca si no se usa).


2
Por supuesto, de nuevo, en un lenguaje puro, la llamada por necesidad y la llamada por nombre son indistinguibles. La llamada por necesidad simplemente se convierte en una estrategia de optimización.
Jörg W Mittag el

2
@ JörgWMittag Buen punto, olvidé mencionar eso. (Por cierto, para llamar la atención, la llamada por necesidad no siempre es más eficiente que la llamada por nombre cuando se opera en memoria finita).
Gilles 'SO- deja de ser malvado'

1
Sí, pero ¿quién tiene solo memoria finita? Todos estamos usando máquinas de Turing, ¿verdad? En serio: supongo que se debe a que usted tiene ciertos gastos generales de contabilidad con respecto a si "necesita" o no el valor, ¿verdad? Por ejemplo, ¿thunks o algo así?
Jörg W Mittag el

3
@ JörgWMittag Pero incluso en una máquina Turing, la memoria es una limitación, ya que si mantiene más cosas alrededor, alcanzar las cosas útiles requiere más ida y vuelta de la cinta. En computadoras reales, esto se llama localidad de caché pobre.
Gilles 'SO- deja de ser malvado'

7

Es imposible distinguir entre ellos. Lo que esto significa es que el compilador es libre de elegir usar la semántica que considere adecuada para un rendimiento óptimo.

En particular, los lenguajes funcionales se describen típicamente como que tienen una semántica de valor, porque eso coincide con nuestro modelo conceptual de ellos, pero a menudo se implementan usando semántica de referencia porque es más eficiente. (¡No es necesario copiar algo que no se puede cambiar!)

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.