Câu trả lời này là để đáp ứng với các vấn đề được đưa ra bởi illissius, từng điểm một:
- Thật là xấu khi sử dụng. $ (fooBar '' Asdf) trông không đẹp mắt. Hời hợt, chắc chắn, nhưng nó góp phần.
Tôi đồng ý. Tôi cảm thấy như $ () đã được chọn để trông giống như nó là một phần của ngôn ngữ - sử dụng biểu tượng pallet quen thuộc của Haskell. Tuy nhiên, đó chính xác là những gì bạn / không / muốn trong các biểu tượng được sử dụng cho ghép nối macro của bạn. Họ chắc chắn pha trộn quá nhiều, và khía cạnh mỹ phẩm này là khá quan trọng. Tôi thích giao diện của {{}} cho các mối nối, vì chúng khá khác biệt về mặt trực quan.
- Nó thậm chí còn xấu hơn để viết. Báo giá đôi khi hoạt động, nhưng rất nhiều thời gian bạn phải thực hiện ghép AST thủ công và hệ thống ống nước. [API] [1] rất lớn và khó sử dụng, luôn có rất nhiều trường hợp bạn không quan tâm nhưng vẫn cần gửi đi và các trường hợp bạn quan tâm có xu hướng xuất hiện ở nhiều dạng tương tự nhưng không giống nhau (dữ liệu so với newtype, kiểu ghi so với các nhà xây dựng bình thường, v.v.). Thật nhàm chán và lặp đi lặp lại để viết và đủ phức tạp để không bị máy móc. [Đề xuất cải cách] [2] đề cập đến một số điều này (làm cho trích dẫn được áp dụng rộng rãi hơn).
Tuy nhiên, tôi cũng đồng ý với điều này, vì một số ý kiến trong "Hướng đi mới cho TH" quan sát, việc thiếu trích dẫn AST ngoài luồng tốt không phải là một lỗ hổng nghiêm trọng. Trong gói WIP này, tôi tìm cách giải quyết những vấn đề này ở dạng thư viện: https://github.com/mgsloan/quasi-extras . Cho đến nay tôi cho phép ghép nối ở một vài nơi hơn bình thường và có thể khớp mẫu trên AST.
- Giới hạn sân khấu là địa ngục. Không thể ghép các hàm được xác định trong cùng một mô-đun là phần nhỏ hơn của nó: hậu quả khác là nếu bạn có một mối nối cấp cao nhất, mọi thứ sau mô-đun sẽ nằm ngoài phạm vi của bất kỳ thứ gì trước nó. Các ngôn ngữ khác có thuộc tính này (C, C ++) làm cho nó hoạt động được bằng cách cho phép bạn chuyển tiếp khai báo mọi thứ, nhưng Haskell thì không. Nếu bạn cần tham chiếu theo chu kỳ giữa các khai báo được ghép hoặc các phụ thuộc và phụ thuộc của chúng, bạn thường chỉ bị vặn.
Tôi đã gặp phải vấn đề định nghĩa TH tuần hoàn là không thể trước đây ... Nó khá khó chịu. Có một giải pháp, nhưng nó thật xấu xí - bao bọc những thứ liên quan đến sự phụ thuộc theo chu kỳ trong một biểu thức TH kết hợp tất cả các khai báo được tạo. Một trong những trình tạo khai báo này có thể chỉ là một bộ trích dẫn chấp nhận mã Haskell.
- Nó không được cung cấp. Điều tôi muốn nói là điều này là hầu hết thời gian khi bạn thể hiện một sự trừu tượng, có một số loại nguyên tắc hoặc khái niệm đằng sau sự trừu tượng đó. Đối với nhiều khái niệm trừu tượng, nguyên tắc đằng sau chúng có thể được thể hiện trong các loại của chúng. Khi bạn định nghĩa một lớp loại, bạn thường có thể xây dựng luật mà các trường hợp nên tuân theo và khách hàng có thể giả định. Nếu bạn sử dụng [tính năng tổng quát mới] của GHC để trừu tượng hóa hình thức khai báo cá thể trên bất kỳ kiểu dữ liệu nào (trong giới hạn), bạn có thể nói "đối với các loại tổng, nó hoạt động như thế này, đối với các loại sản phẩm, nó hoạt động như thế ". Nhưng Template Haskell chỉ là macro ngu ngốc. Nó không trừu tượng ở cấp độ ý tưởng, nhưng trừu tượng hóa ở cấp độ AST, tốt hơn, nhưng chỉ khiêm tốn, hơn là trừu tượng ở cấp độ văn bản thuần túy.
Nó chỉ là không có đối thủ nếu bạn làm những điều không được thực hiện với nó. Sự khác biệt duy nhất là với trình biên dịch triển khai các cơ chế trừu tượng hóa, bạn có thêm niềm tin rằng sự trừu tượng hóa không bị rò rỉ. Có lẽ thiết kế ngôn ngữ dân chủ hóa nghe có vẻ hơi đáng sợ! Người tạo thư viện TH cần ghi chép tốt và xác định rõ ràng ý nghĩa và kết quả của các công cụ họ cung cấp. Một ví dụ điển hình của TH nguyên tắc là gói phái sinh: http://hackage.haskell.org/package/derive - nó sử dụng DSL sao cho ví dụ về nhiều đạo hàm / chỉ định / đạo hàm thực tế.
- Nó ràng buộc bạn với GHC. Về lý thuyết, một trình biên dịch khác có thể thực hiện nó, nhưng trong thực tế tôi nghi ngờ điều này sẽ xảy ra. (Điều này trái ngược với các phần mở rộng hệ thống loại khác nhau, mặc dù chúng chỉ có thể được GHC triển khai vào lúc này, tôi có thể dễ dàng tưởng tượng việc được các trình biên dịch khác sử dụng trên đường và cuối cùng được chuẩn hóa.)
Đó là một điểm khá tốt - API TH khá lớn và cồng kềnh. Thực hiện lại có vẻ như nó có thể khó khăn. Tuy nhiên, thực sự chỉ có một vài cách để giải quyết vấn đề đại diện cho Haskell ASTs. Tôi tưởng tượng rằng việc sao chép TH ADT và viết một trình chuyển đổi sang biểu diễn AST bên trong sẽ giúp bạn giải quyết tốt vấn đề đó. Điều này sẽ tương đương với nỗ lực (không đáng kể) của việc tạo haskell-src-meta. Nó cũng có thể được thực hiện lại một cách đơn giản bằng cách in đẹp TH AST và sử dụng trình phân tích cú pháp nội bộ của trình biên dịch.
Mặc dù tôi có thể sai, tôi không thấy TH là phức tạp của phần mở rộng trình biên dịch, từ góc độ thực hiện. Đây thực sự là một trong những lợi ích của việc "giữ cho nó đơn giản" và không có lớp cơ bản là một hệ thống tạo khuôn mẫu có thể kiểm chứng tĩnh, hấp dẫn về mặt lý thuyết.
- API không ổn định. Khi các tính năng ngôn ngữ mới được thêm vào GHC và gói template-haskell được cập nhật để hỗ trợ chúng, điều này thường liên quan đến các thay đổi không tương thích ngược với các kiểu dữ liệu TH. Nếu bạn muốn mã TH của bạn tương thích với nhiều hơn một phiên bản GHC, bạn cần phải rất cẩn thận và có thể sử dụng
CPP
.
Đây cũng là một điểm tốt, nhưng hơi kịch tính. Mặc dù gần đây đã có các bổ sung API, nhưng chúng vẫn chưa bị phá vỡ gây ra. Ngoài ra, tôi nghĩ rằng với trích dẫn AST vượt trội mà tôi đã đề cập trước đó, API thực sự cần được sử dụng có thể được giảm đáng kể. Nếu không có cấu trúc / kết hợp nào cần các hàm riêng biệt và thay vào đó được biểu thị bằng chữ, thì hầu hết các API sẽ biến mất. Hơn nữa, mã bạn viết sẽ chuyển dễ dàng hơn tới các biểu diễn AST cho các ngôn ngữ tương tự như Haskell.
Tóm lại, tôi nghĩ rằng TH là một công cụ mạnh mẽ, bị bỏ quên. Ít ghét hơn có thể dẫn đến một hệ thống thư viện sinh thái sống động hơn, khuyến khích thực hiện các nguyên mẫu tính năng ngôn ngữ nhiều hơn. Người ta đã quan sát thấy TH là một công cụ quá sức, có thể cho phép bạn / làm / hầu hết mọi thứ. Tình trạng hỗn loạn! Vâng, theo ý kiến của tôi, sức mạnh này có thể cho phép bạn vượt qua hầu hết các hạn chế của nó và xây dựng các hệ thống có khả năng tiếp cận lập trình meta khá nguyên tắc. Thật đáng để sử dụng các bản hack xấu xí để mô phỏng việc thực hiện "đúng", vì cách này thiết kế của việc thực hiện "đúng" sẽ dần trở nên rõ ràng.
Trong phiên bản lý tưởng cá nhân của tôi về niết bàn, phần lớn ngôn ngữ sẽ thực sự chuyển ra khỏi trình biên dịch, vào các thư viện thuộc loại này. Thực tế là các tính năng được triển khai như các thư viện không ảnh hưởng nhiều đến khả năng trừu tượng trung thực của chúng.
Câu trả lời điển hình của Haskell cho mã soạn sẵn là gì? Trừu tượng. Trừu tượng yêu thích của chúng tôi là gì? Chức năng và kiểu chữ!
Máy đánh chữ cho phép chúng ta định nghĩa một tập hợp các phương thức, sau đó có thể được sử dụng theo tất cả các cách của các hàm chung trên lớp đó. Tuy nhiên, ngoài điều này, cách duy nhất các lớp giúp tránh nồi hơi là bằng cách cung cấp "định nghĩa mặc định". Bây giờ đây là một ví dụ về một tính năng chưa được cung cấp!
Bộ ràng buộc tối thiểu không thể khai báo / trình biên dịch có thể kiểm tra. Điều này có thể dẫn đến các định nghĩa vô tình mang lại đáy do đệ quy lẫn nhau.
Mặc dù sự tiện lợi và sức mạnh này sẽ mang lại, bạn không thể chỉ định mặc định của siêu lớp, do trường hợp mồ côi http://lukepalmer.wordpress.com/2009/01/11/a-world-without-orphans/ Những điều này sẽ cho phép chúng tôi khắc phục thứ bậc số duyên dáng!
Đi sau các khả năng giống như TH cho các mặc định của phương thức đã dẫn đến http://www.haskell.org/haskellwiki/GHC.Generics . Mặc dù đây là công cụ tuyệt vời, nhưng trải nghiệm mã gỡ lỗi duy nhất của tôi khi sử dụng các tổng quát này là không thể, do kích thước của loại gây ra và ADT phức tạp như AST. https://github.com/mgsloan/th-extra/commit/d7784d95d394eb3abdb409a24360beb03731c88c
Nói cách khác, điều này đi sau các tính năng do TH cung cấp, nhưng nó phải nâng toàn bộ một miền của ngôn ngữ, ngôn ngữ xây dựng, thành một đại diện hệ thống loại. Mặc dù tôi có thể thấy nó hoạt động tốt cho vấn đề chung của bạn, nhưng đối với những vấn đề phức tạp, có vẻ như nó mang lại một đống biểu tượng đáng sợ hơn nhiều so với TH hack.
TH cung cấp cho bạn tính toán thời gian biên dịch mức giá trị của mã đầu ra, trong khi đó tổng quát buộc bạn phải nâng phần khớp mẫu / đệ quy của mã vào hệ thống loại. Mặc dù điều này hạn chế người dùng theo một số cách khá hữu ích, tôi không nghĩ rằng sự phức tạp là đáng giá.
Tôi nghĩ rằng việc từ chối TH và lập trình siêu dữ liệu giống như lisp đã dẫn đến sự ưu tiên đối với những thứ như mặc định phương thức thay vì mở rộng macro, linh hoạt hơn như khai báo các thể hiện. Kỷ luật tránh những thứ có thể dẫn đến kết quả không được cải thiện là khôn ngoan, tuy nhiên, chúng ta không nên bỏ qua rằng hệ thống loại có khả năng của Haskell cho phép lập trình siêu dữ liệu đáng tin cậy hơn trong nhiều môi trường khác (bằng cách kiểm tra mã được tạo).