2011-12-30 23 views
7

Extiendo una respuesta a Haskell setting record field based on field name string? para agregar un getField genérico. Estoy usando gmapQi, y quiero generar un error si el tipo del subelemento encontrado no coincide con el tipo esperado. Quiero que el mensaje de error incluya el nombre del tipo que se encontró, así como el nombre del tipo esperado. La función tiene el siguiente aspecto:typeOf on return type

{-# LANGUAGE DeriveDataTypeable #-} 

import Data.Generics 
import Prelude hiding (catch) 
import Control.Exception 

getField :: (Data r, Typeable v) => Int -> r -> v 
getField i r = gmapQi i (e `extQ` id) r 
    where 
    e x = error $ "Type mismatch: field " ++ (show i) ++ 
        " :: " ++ (show . typeOf $ x) ++ 
        ", not " ++ (show . typeOf $ "???") 

--------------------------------------------------------------------------------- 

data Foo = Foo Int String 
    deriving(Data, Typeable) 

handleErr (ErrorCall msg) = putStrLn $ "Error -- " ++ msg 

main = do 
    let r = Foo 10 "Hello" 
    catch (print (getField 0 r :: Int)) handleErr 
    catch (print (getField 0 r :: String)) handleErr 
    catch (print (getField 1 r :: Int)) handleErr 
    catch (print (getField 1 r :: String)) handleErr 

El problema es que no sé qué poner en su lugar de "???" para obtener el tipo de retorno de la función getField (es decir, la manera de cosificar v de la firma de tipo).

Respuesta

5

typeOf nunca evalúa su argumento, por lo que puede usar cualquier expresión, siempre que sea del tipo correcto. En este caso, el tipo de resultado es el mismo que el tipo de devolución de e, por lo que puede usar e x.

getField :: (Data r, Typeable v) => Int -> r -> v 
getField i r = gmapQi i (e `extQ` id) r 
    where 
    e x = error $ "Type mismatch: field " ++ (show i) ++ 
        " :: " ++ (show . typeOf $ x) ++ 
        ", not " ++ (show . typeOf $ e x) 

Esto da el resultado esperado cuando se ejecuta:

10 
"Error -- Type mismatch: field 0 :: Int, not [Char] 
Error -- Type mismatch: field 1 :: [Char], not Int 
"Hello" 
+0

brillante! Gracias – pat

1

Puede utilizar (show . typeOf $ (undefined :: v)) si se agrega forall r v. del inicio de sus firmas de tipos y encienda la extensión ScopedTypeVariables.

Será posible lograrlo sin eso, generalmente implica funciones auxiliares ficticias para forzar los tipos en la forma que desee, pero será muy feo y no estoy seguro de cómo.