2011-12-30 36 views
7

Tôi đang mở rộng câu trả lời cho Haskell setting record field based on field name string? để thêm một số chung là getField. Tôi đang sử dụng gmapQi và tôi muốn tạo lỗi nếu loại phần tử phụ gặp phải không khớp với loại mong muốn. Tôi muốn thông báo lỗi bao gồm tên của loại đã gặp phải, cũng như tên của loại được mong đợi. Chức năng trông như thế này:typeOf trên loại trả lại

{-# 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 

Vấn đề là, tôi không biết phải đặt ở vị trí của "???" để có được những kiểu trả về của getField chức năng (ví dụ: làm thế nào để cụ thể hóa v từ chữ ký loại).

Trả lời

5

typeOf không bao giờ đánh giá đối số của nó, vì vậy bạn có thể sử dụng bất kỳ biểu thức nào miễn là nó thuộc loại chính xác. Trong trường hợp này, loại kết quả giống với kiểu trả về của e, vì vậy bạn chỉ có thể sử dụng 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) 

này cung cấp cho các đầu ra mong đợi khi chạy:

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

rực rỡ! Cảm ơn – pat

1

Bạn có thể sử dụng (show . typeOf $ (undefined :: v)) nếu bạn thêm forall r v. vào đầu chữ ký loại và bật tiện ích mở rộng ScopedTypeVariables. Nó sẽ có thể đạt được mà không có điều đó - thường nó liên quan đến các chức năng trợ giúp giả để ép buộc các loại vào mẫu bạn muốn - nhưng nó sẽ rất xấu xí và tôi không chắc chắn như thế nào.

Các vấn đề liên quan