2012-04-02 38 views
9

Tôi đang viết trình tạo mã có đầu ra phụ thuộc vào mô tả trường dữ liệu được lưu trữ trong các phiên bản lớp của chúng. Tuy nhiên, tôi không thể tìm thấy cách chạy một hàm với đối số được tạo ra TH.Làm thế nào để vượt qua giới hạn giai đoạn GHC?

{-# LANGUAGE TemplateHaskell, ScopedTypeVariables #-} 
module Generator where 
import Language.Haskell.TH 
import Language.Haskell.TH.Syntax 

data Description = Description String [Description] deriving Show 

class HasDescription a where 
    getDescription :: a -> Description 

instance HasDescription Int where 
    getDescription _ = Description "Int" [] 

instance (HasDescription a, HasDescription b) => HasDescription (a, b) where 
    getDescription (_ :: (a, b)) = Description "Tuple2" [getDescription (undefined :: a), getDescription (undefined :: b)] 

-- | creates instance of HasDescription for the passed datatype changing descriptions of its fields 
mkHasDescription :: Name -> Q [Dec] 
mkHasDescription dName = do 
    reify dName >>= runIO . print 
    TyConI (DataD cxt name tyVarBndr [NormalC cName types] derives) <- reify dName 
    -- Attempt to get description of data to modify it. 
    let mkSubDesc t = let Description desc ds = getDescription (undefined :: $(return t)) in [| Description $(lift $ desC++ "Modified") $(lift ds) |] 

    let body = [| Description $(lift $ nameBase dName) $(listE $ map (mkSubDesc . snd) types) |] 
    getDescription' <- funD 'getDescription [clause [wildP] (normalB body) []] 
    return [ InstanceD [] (AppT (ConT ''HasDescription) (ConT dName)) [getDescription'] ] 

Khi mô-đun khác cố gắng sử dụng Generator

{-# LANGUAGE TemplateHaskell, ScopedTypeVariables #-} 
import Generator 

data MyData = MyData Int Int 

mkHasDescription ''MyData 

{- the code I want to generate 
instance HasDescription MyData where 
    getDescription _ = Description "MyData" [Description "IntModified" [], Description "IntModified" []] 
-} 

dường như một lỗi

Generator.hs:23:85: 
GHC stage restriction: `t' 
    is used in a top-level splice or annotation, 
    and must be imported, not defined locally 
In the first argument of `return', namely `t' 
In the expression: return t 
In an expression type signature: $(return t) 

chỉnh sửa:

Khi yêu cầu tôi nghĩ rằng vấn đề này xuất hiện chỉ vì tôi chỉ đã không nắm bắt một cái gì đó rất quan trọng trong TH và nó có thể được giải quyết bằng cách di chuyển một số chức năng đến các mô-đun khác.

Nếu không thể tạo dữ liệu được xác định trước như ví dụ từ câu hỏi, tôi muốn tìm hiểu thêm về các hạn chế về lý thuyết của TH.

+1

Tôi thấy rằng ... đáng ngạc nhiên, điều đó sẽ không hoạt động. Có lẽ bạn cũng cần bật QuasiQuotes? –

Trả lời

4

Đây thực sự là vấn đề với giới hạn giai đoạn. Vấn đề, như hammar chỉ ra, nằm với các cuộc gọi đến getDescription.

let mkSubDesc t = ... getDescription (undefined :: $(return t)) ... 

Hàm getDescription bị quá tải và trình biên dịch chọn triển khai dựa trên loại đối số của nó.

class HasDescription a where 
    getDescription :: a -> Description 

Loại lớp quá tải dựa trên loại. Cách duy nhất để chuyển đổi t thành một loại là biên dịch nó. Nhưng biên dịch nó đặt loại trong chương trình đã biên dịch. Cuộc gọi đến getDescription chạy tại thời điểm biên dịch, do đó, nó không có quyền truy cập vào loại đó.

Nếu bạn thực sự muốn đánh giá getDescription trong Mẫu Haskell, bạn phải viết triển khai của riêng mình là getDescription đọc cấu trúc dữ liệu Mẫu Haskell có sẵn tại thời gian biên dịch.

getDescription2 :: Type -> Q Description 
getDescription2 t = cases con [ ([t| Int |], "Int") 
           , (return (TupleT 2), "Tuple") 
           ] 
    where 
    (con, ts) = fromApp t 
    fromApp (AppT t1 t2) = let (c, ts) = fromApp t1 in (c, ts ++ [t2]) 
    fromApp t = (t, []) 
    cases x ((make_y, name):ys) = do y <- make_y 
            if x == y 
             then do ds <- mapM getDescription2 ts 
               return $ Description name ds 
             else cases x ys 
    cases x [] = error "getDescription: Unrecognized type" 
7

Bạn có thể sửa chữa nó bằng cách di chuyển let ràng buộc bên trong dấu ngoặc Oxford:

let mkSubDesc t = [| let Description desc ds = getDescription (undefined :: $(return t)) 
        in Description (desC++ "Modified") ds |] 

Tất nhiên, điều này có nghĩa rằng đây sẽ là một phần của mã được tạo ra, nhưng ít nhất cho trường hợp này, shouldn rằng' t vấn đề.

+1

Cảm ơn lời khuyên. Tôi nghĩ về việc di chuyển để cho bên trong các dấu ngoặc trước đó, nhưng mã sẽ được gọi là thường xuyên vì vậy nó phải được nhanh chóng. Tiêu chí chuẩn cho thấy rằng getDescription với let là chậm hơn so với mô tả đã được sửa đổi (thực sự tôi đã thử nó trên các chức năng khác và datatypes - HasDescription chỉ là một đơn giản hóa). – Boris

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