2011-09-13 17 views
10

Estoy mirando el paquete de Cloud Haskell Encoding.hs, y encontré un código extraño que esperaba que alguien podría ayudarme a comprender mejor. Incluido es el código necesario:haskell código mágico, ¿qué está pasando aquí?

class (Binary a,Typeable a) => Serializable a 
instance (Binary a,Typeable a) => Serializable a 

data Payload = Payload 
    { 
     payloadType :: !ByteString, 
     payloadContent :: !ByteString 
    } deriving (Typeable) 

serialDecodePure :: (Serializable a) => Payload -> Maybe a 
serialDecodePure a = (\id -> 
    let pc = payloadContent a 
    in pc `seq` 
     if (decode $! payloadType a) == show (typeOf $ id undefined) 
     then Just (id $! decode pc) 
     else Nothing) id 

Solo tengo curiosidad acerca de qué $! lo hace (supongo que solo evalúa estrictamente), y también por qué necesitamos el truco de identificación (¿algo con evaluación perezosa?). También estoy teniendo problemas específicamente con esta línea:

if (decode $! payloadType a) == show (typeOf $ id undefined) 

supongo esto es ver si el payloadType es inválida por cualquier razón, pero si ese es el caso no debería entonces y demás conmutarse cláusulas, es decir, el cambio:

if (decode $! payloadType a) == show (typeOf $ id undefined) 
    then Just (id $! decode pc) 
    else Nothing 

a

if (decode $! payloadType a) == show (typeOf $ id undefined) 
    then Nothing 
    else Just (id $! decode pc) 

Gracias por cualquier ayuda que puede proporcionar.

+0

Sí, 'f $! x' es una aplicación estricta. Puede encontrar [este artículo] (http://neilmitchell.blogspot.com/2008/05/bad-strictness.html) esclarecedor sobre cómo funciona el rigor de adición. –

+0

Vea también http://stackoverflow.com/q/2787543/246886 –

Respuesta

12

Tiene la razón que $! es un evaluador estricto. Su tipo es idéntico al $, y la única diferencia semántica es que el segundo argumento es seq 'd antes de pasarse a la función.

Creo que el id está realmente allí para ayudar a escribir la inferencia. Dentro del bloque de función (\id -> ...), la función id es forzada a tener el tipo a -> a, donde a no es cualquier tipo de variable, pero la misma a como en

serialDecodePure :: (Serializable a) => Payload -> Maybe a 

Esto es debido a esta línea:

Just (id $! decode pc) 

ya que tiene el tipo Maybe a, id tiene el tipo inferido a -> a.Como consecuencia, en la línea que está viendo,

if (decode $! payloadType a) == show (typeOf $ id undefined) 

id undefined :: a, donde a vuelve a ser el mismo que el de salida.

Ahora podemos acceder a la comprobación del tipo. Como esta función es polimórfica y decodificará a cualquier tipo de, debe verificar que los datos codificados sean compatibles con el tipo de decodificación. ¿Qué pasa si codificó un String y está intentando decodificar a un Int? El LHS decodificaría a "[Char]", que es la representación TypeRep de una cadena. El RHS sería en cambio "Int", el tipo al que intenta decodificar. Como no son iguales, la ruta "else" es la que devuelve None.

En lugar de esta restricción de tipo de función de identificación, puede lograr lo mismo con la extensión ScopedTypeVariables.

+1

oh hombre esa cosa de identificación es muy inteligente –

4

¡Guau, ese es un código de aspecto extraño! Como has adivinado, se trata de ($!) rigor:

f $! x = x `seq` f x 

El truco es id más disimulados, y es todo acerca de restricción de tipo. Notará que id se usa dos veces en el cuerpo de la función. La segunda vez se usa como id $! decode pc; esto corrige el tipo de id para operar en cualquier tipo de cosa decode salidas. El primer uso es como typeOf $! id undefined; ya que el tipo de id ya se ha corregido, esto corrige el tipo de undefined, por lo que typeOf se aplica a un argumento monomórfico (y no se obtienen errores de "tipo ambiguo"). Este tipo de cosas a menudo se hace con la extensión ScopedTypeVariables en lugar de este truco, pero tal vez querían evitar extensiones siempre que fuera posible. En cuanto al significado de esto:

(decode $! payloadType a) == show (typeOf $ id undefined) 

... que me parece que está comprobando que la carga útil coincide con el tipo de lo que devuelve la llamada a decode en la rama then. Parece tener sentido tener un valor de Just (es decir, éxito) cuando coinciden, y un valor Nothing (es decir, error) cuando no coinciden.

Cuestiones relacionadas