2012-02-29 32 views
6

tôi thấy mình viết rất nhiều mã nhưIn và thực hiện một chuỗi

 
putStr "foo (bar 1) (bar 2) =" 
print $ foo (bar 1) (bar 2) 

Vấn đề là, thông điệp được in có thể nhận ra đồng bộ với mã thực thi thực tế. Giải pháp hiển nhiên là tự động tạo mã này.

Một cách để làm điều đó là đặt tất cả văn bản vào một tệp và viết một chương trình nhỏ đọc tệp và tạo mã nguồn Haskell từ đó. Nhưng một cách khác là sử dụng mẫu Haskell.

Có ai biết làm thế nào tôi sẽ viết về một hàm cần có một String và tạo mã ở trên không? Tôi đoán nó sẽ khá dễ dàng, nhưng TH không được tài liệu tốt.

+3

Tôi muốn sử dụng CPP. Thô lỗ nhưng hiệu quả cho những thứ này. – augustss

+0

CPP hoạt động - cho đến khi văn bản bạn muốn báo giá mở rộng đến nhiều dòng ... – MathematicalOrchid

+3

"Tôi thấy mình viết rất nhiều mã như [this]" ... tại sao? –

Trả lời

8

Bạn có thể phân tích mã Haskell sử dụng gói haskell-src-meta. Dưới đây là ví dụ nhanh về cách bạn có thể kết hợp điều này với Mẫu Haskell.

{-# LANGUAGE TemplateHaskell #-} 

import Language.Haskell.TH 
import Language.Haskell.TH.Quote 
import Language.Haskell.Meta 

runShow = QuasiQuoter 
    { quoteExp = runShowQQ 
    , quotePat = undefined 
    , quoteType = undefined 
    , quoteDec = undefined 
    } 

runShowQQ :: String -> Q Exp 
runShowQQ s = do 
    let s'   = s ++ " = " 
     Right exp = parseExp s 
     printExp = appE [|print|] (return exp) 
    infixApp [|putStr s'|] [|(>>)|] printExp 

Và bạn sẽ sử dụng nó như

{-# LANGUAGE QuasiQuotes #-} 

[runShow|foo (bar 1) (bar 2)|] 
+3

Vì vậy, những gì bạn đang nói là, ai đó đã thực hiện một trình phân tích cú pháp Haskell như một quasi-quoter, và bạn có thể sử dụng nó? (Gotta yêu nó như thế nào nói "chưa hoàn thành" trên tất cả nó ...) Có vẻ như một điều đáng tiếc để không thể đối xử với phân tích cú pháp _existing_ Haskell của GHC như một quasi-quoter ở nơi đầu tiên ... Tôi tự hỏi tại sao họ don Không hỗ trợ điều đó? – MathematicalOrchid

+0

Điều này rõ ràng là tốt nhất có thể được thực hiện với TH, vì vậy tôi sẽ chấp nhận câu trả lời này. – MathematicalOrchid

1

Có ví dụ về eval giống như mã Haskell sử dụng GHC API here.

4

Mẫu Haskell không cung cấp phương tiện đơn giản để phân tích các chuỗi tùy ý, vì vậy giải pháp đơn giản nhất có lẽ là sử dụng bộ tiền xử lý C. Tuy nhiên, trình tích hợp sẵn trong GHC không hỗ trợ xâu chuỗi, vì vậy chúng tôi cần chuyển các tùy chọn bổ sung để sử dụng tùy chọn "thực" thay thế.

{-# LANGUAGE CPP #-} 
{-# OPTIONS_GHC -pgmP cpp #-} 

#define PRINT_EXP(x) (putStr #x >> putStr " = " >> print (x)) 

Sau đó bạn có thể sử dụng nó như thế này:

PRINT_EXP(foo (bar 1) (bar 2)) 
+0

Tôi đã mong đợi TH để hỗ trợ phân tích từ một chuỗi, nhưng như bạn nói, nó không. Bất ngờ nhất. Tôi nhận thấy bạn thấy sử dụng CPP "thực"; bạn nhận ra rằng có _isn't_ một trên Windows, phải không? – MathematicalOrchid

0

Ouch này. Tôi suy nghĩ điều này sẽ dễ dàng, nhưng theo như tôi có thể nói, nó thực sự là không thể.

Tôi đã mong đợi có một chức năng biến một chuỗi thành một biểu thức, nhưng dường như không có chức năng nào tồn tại. Thậm chí không có chức năng tải thêm mã nguồn từ đĩa. Vì vậy, có vẻ như nhiệm vụ này thực sự là không thể! Tôi khá ngạc nhiên về điều đó.

Điều gần nhất tôi có thể làm là trích dẫn biểu thức mà tôi muốn chạy, sau đó xây dựng một mối nối có in các biểu thức được trích dẫn trước khi chạy nó. Tuy nhiên, điều đó đặt tôi vào lòng thương xót của biểu hiện máy in khá của GHC. Nhãn không xuất hiện chính xác khi tôi nhập nhãn. (Đặc biệt, dường như thay thế các toán tử có tên đầy đủ, điều này chỉ gây đau đớn.)

Bạn có thể nghĩ rằng một tính năng như thế này sẽ khá tầm thường để thực hiện. Do đó, thực tế là nó không được triển khai do đó chỉ có thể được quy cho một trong hai điều sau:

  1. Không ai thực sự cần tính năng này. (Vâng, ngoại trừ tôi, rõ ràng.)

  2. Nó không phải là tầm thường như nó trông. (Ví dụ: có thể tìm ra bối cảnh nào để phân tích biểu thức trong một cách khó hiểu bằng cách nào đó?)

+1

"nó thực sự là không thể" - sai. "không có chức năng như vậy tồn tại [mà biến một chuỗi thành một biểu thức]" - sai. câu trả lời của shang thực hiện cả hai. TH của anh ta xuất ra chuỗi được trích dẫn * chính xác * vì nó là đầu vào. Trong thực tế, ông đã thực hiện * chính xác * điều bạn yêu cầu, sản xuất chính xác mã đó. Nó chỉ hoạt động cho các biểu thức thuần khiết, có thể hiển thị và biến chúng thành một 'IO()', nhưng có thể dễ dàng sửa đổi: http://hpaste.org/64551 –

+1

Nó chỉ hoạt động cho các biểu thức Haskell mà trình phân tích cú pháp Haskell bên thứ ba làm việc cho, không cho bất kỳ biểu thức nào mà GHC có thể phân tích cú pháp. – MathematicalOrchid

0

Bạn cũng có thể sử dụng gói dump được viết để xử lý use-case này chính xác:

{-# language QuasiQuotes #-} 
import Debug.Dump 

main = putStrLn [d| foo (bar 1) (bar 2) |] 

foo = (+) 
bar = (+1) 

nào in: (foo (bar 1) (bar 2)) = 5

Nó cũng xử lý nhiều hơn một biểu hiện ngăn cách bởi dấu phẩy:

putStrLn [d| foo (bar 1) (bar 2), map bar [1, 2] |] 

Bản in nào: (foo (bar 1) (bar 2)) = 5 (map bar [1, 2]) = [2,3]

Bonus: Nếu bạn có nix vỏ cài đặt (một phần của nix package manager), bạn thậm chí có thể thử nó ra một cách nhanh chóng với điều này "one-liner":

$ nix-shell -p "nix-shell -p "haskellPackages.ghcWithPackages (p: [p.dump])" --run "echo '{-# language QuasiQuotes #-}; import Debug.Dump; foo = (+); bar = (+1); main = putStrLn [d| foo (bar 1) (bar 2), map bar [1, 2] |]' | runhaskell"

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