Lưu ý: Câu trả lời này dựa trên những quan sát gần đây về các cuộc thảo luận về Levity. Mọi thứ liên quan đến tính đa hình Levity hiện chỉ được triển khai trong các ứng cử viên phát hành GHC 8.0 và có thể thay đổi ( ví dụ: xem # 11471 ).
TL; DR : Đó là một cách để tạo ra các hàm đa hình trên các loại nâng và không có hàm, điều này không thể thực hiện được với các hàm thông thường. Ví dụ: mã sau đây không kiểm tra kiểu với các đa hình thông thường, vì Int#
có loại #
, nhưng các biến loại trong id
có loại *
:
{-# LANGUAGE MagicHash #-}
import GHC.Prim
example :: Int# -> Int#
example = id
Couldn't match kind ‘*’ with ‘#’
When matching types
a0 :: *
Int# :: #
Expected type: Int# -> Int#
Actual type: a0 -> a0
In the expression: id
Lưu ý rằng (->)
vẫn sử dụng một số phép thuật.
Trước khi tôi bắt đầu trả lời câu hỏi này, chúng ta hãy lùi lại một bước và chuyển đến một trong những hàm thường được sử dụng nhất ($)
,.
Là gì ($)
's loại? Vâng, theo Hackage và báo cáo, nó
($) :: (a -> b) -> a -> b
Tuy nhiên, điều đó không hoàn toàn 100%. Đó là một lời nói dối nhỏ tiện lợi. Vấn đề là loại đa hình (giống a
và b
) có loại *
. Tuy nhiên, các nhà phát triển (thư viện) ($)
không chỉ muốn sử dụng cho các loại với loại *
, mà còn cho loại loại #
, ví dụ:
unwrapInt :: Int -> Int#
Trong khi Int
có tử tế *
(nó có thể là đáy), Int#
có tử tế #
(và không thể ở dưới cùng). Tuy nhiên, các kiểu gõ mã sau:
unwrapInt $ 42
Điều đó sẽ không hoạt động. Hãy nhớ loại trả lại của ($)
? Đó là loại đa hình, và các loại đa hình có loại *
, không #
! Vậy tại sao nó hoạt động? Đầu tiên, đó là một lỗi , sau đó là một vụ hack (trích đoạn thư của Ryan Scott trong danh sách gửi thư ghc-dev):
Vậy tại sao điều này xảy ra?
Câu trả lời dài là trước GHC 8.0, trong chữ ký kiểu ($) :: (a -> b) -> a -> b
, b
thực sự không phải bằng hiện vật *
, mà là OpenKind
.
OpenKind
là một cuộc tấn công khủng khiếp cho phép cả loại nâng cao (tử tế *
) và không có kỹ năng (loại #
) sống trong nó, đó là lý do tại sao đánh máy (unwrapInt $ 42)
.
Vậy ($)
loại mới trong GHC 8.0 là gì? nó là
($) :: forall (w :: Levity) a (b :: TYPE w). (a -> b) -> a -> b
Để hiểu nó, chúng ta phải xem Levity
:
data Levity = Lifted | Unlifted
Bây giờ, chúng ta có thể ($)
coi là có một trong hai loại sau, vì chỉ có hai lựa chọn w
:
($) :: forall a (b :: TYPE Lifted). (a -> b) -> a -> b
($) :: forall a (b :: TYPE Unlifted). (a -> b) -> a -> b
TYPE
là một hằng số ma thuật và nó xác định lại các loại *
và #
như
type * = TYPE Lifted
type # = TYPE Unlifted
Việc định lượng các loại cũng khá mới và là một phần của việc tích hợp các loại phụ thuộc trong Haskell .
Tên đa hình Levity xuất phát từ thực tế là giờ đây bạn có thể viết các hàm đa hình trên cả kiểu nâng và không ghép, một điều không được phép / có thể thực hiện được với các hạn chế về đa hình trước đây. Nó cũng đồng thời loại bỏ OpenKind
hack. Nó thực sự "chỉ" về điều này, xử lý cả hai loại.
Nhân tiện, bạn không đơn độc với câu hỏi của mình. Ngay cả Simon Peyton Jones cũng nói rằng cần phải có một trang wiki Levity , và Richard E. (người thực hiện điều này hiện tại) đã nói rằng trang wiki cần cập nhật quy trình hiện tại.
Người giới thiệu