2015-02-27 17 views
13

Sự khác biệt giữa f1f2 là gì?RankNTypes và PolyKinds

$ ghci -XRankNTypes -XPolyKinds 
Prelude> let f1 = undefined :: (forall a  m. m a -> Int) -> Int 
Prelude> let f2 = undefined :: (forall (a :: k) m. m a -> Int) -> Int 
Prelude> :t f1 
f1 :: (forall   (a :: k) (m :: k -> *). m a -> Int) -> Int 
Prelude> :t f2 
f2 :: (forall (k :: BOX) (a :: k) (m :: k -> *). m a -> Int) -> Int 

Liên quan đến câu hỏi này trên RankNTypes and scope of forall. Ví dụ được lấy từ hướng dẫn người dùng GHC trên kind polymorphism.

Trả lời

11

f2 đòi hỏi đối số của nó là đa hình trong các loại k, trong khi f1 chỉ là đa hình trong loại riêng của mình. Vì vậy, nếu bạn xác định

{-# LANGUAGE RankNTypes, PolyKinds #-} 
f1 = undefined :: (forall a m. m a -> Int) -> Int 
f2 = undefined :: (forall (a :: k) m. m a -> Int) -> Int 
x = undefined :: forall (a :: *) m. m a -> Int 

sau đó :t f1 x loại tốt, trong khi :t f2 x phàn nàn:

*Main> :t f2 x 

<interactive>:1:4: 
    Kind incompatibility when matching types: 
     m0 :: * -> * 
     m :: k -> * 
    Expected type: m a -> Int 
     Actual type: m0 a0 -> Int 
    In the first argument of ‘f2’, namely ‘x’ 
    In the expression: f2 x 
+0

Chính xác thì 'k' ở đó là gì? là 'k' biến bất kỳ điều đặc biệt nào như' * '? – Sibi

+1

@Sibi 'k' là một biến kiểu,' * 'là một trong những giá trị" có thể "của nó. Trong các kiểu Haskell có nhiều kiểu giống như các giá trị có kiểu, mặc dù bạn cần các phần mở rộng để sử dụng nhiều hơn các kiểu cố định dựng sẵn như '*' và '* -> *'. –

11

Hãy là đẫm máu. Chúng ta phải định lượng mọi thứ và cung cấp cho miền định lượng. Giá trị có các loại; loại cấp có các loại; các loại sống trong BOX.

f1 :: forall (k :: BOX). 
     (forall (a :: k) (m :: k -> *). m a -> Int) 
     -> Int 

f2 :: (forall (k :: BOX) (a :: k) (m :: k -> *). m a -> Int) 
     -> Int 

Bây giờ, trong không loại ví dụ là k định lượng rõ ràng, vì vậy GHC là quyết định nơi để đặt rằng forall (k :: BOX), dựa trên việc và nơi k được đề cập. Tôi không hoàn toàn chắc chắn rằng tôi hiểu hoặc sẵn sàng bảo vệ chính sách như đã nêu.

Ørjan đưa ra ví dụ tốt về sự khác biệt trong thực tế. Hãy cũng đẫm máu về điều đó. Tôi sẽ viết /\ (a :: k). t để làm rõ ràng sự trừu tượng tương ứng với forallf @ type cho ứng dụng tương ứng. Trò chơi là chúng ta có thể chọn các đối số @ -ed, nhưng chúng ta phải sẵn sàng để đưa ra bất kỳ đối số /\ nào mà ma quỷ có thể chọn.

Chúng tôi có

x :: forall (a :: *) (m :: * -> *). m a -> Int 

và theo đó có thể phát hiện ra rằng f1 x thực sự là

f1 @ * (/\ (a :: *) (m :: * -> *). x @ a @ m) 

Tuy nhiên, nếu chúng tôi cố gắng cung cấp cho f2 x điều trị tương tự, chúng ta thấy

f2 (/\ (k :: BOX) (a :: k) (m :: k -> *). x @ ?m0 @ ?a0) 
?m0 :: * 
?a0 :: * -> * 
where m a = m0 a0 

Các Hệ thống kiểu Haskell xử lý ứng dụng kiểu như hoàn toàn cú pháp, vì vậy cách duy phương trình có thể được giải quyết là bằng cách xác định các chức năng và xác định các đối số

(?m0 :: * -> *) = (m :: k -> *) 
(?a0 :: *)  = (a :: k) 

nhưng những phương trình không được thậm chí cũng kinded, vì k là không được tự do được chọn: nó là /\ -ed không @ -ed.

Nói chung, để hiểu thấu các loại đa hình này, bạn nên viết ra tất cả các định lượng và sau đó tìm ra cách biến thành trò chơi của bạn chống lại ma quỷ. Ai chọn cái gì, và theo thứ tự nào. Di chuyển forall bên trong một loại đối số thay đổi lựa chọn của nó, và thường có thể tạo sự khác biệt giữa chiến thắng và thất bại.

3

Loại f1 nhiều nơi hơn hạn chế về định nghĩa của nó, trong khi các loại f2 nhiều nơi hơn hạn chế về đối số của nó.

Đó là: loại f1 đòi hỏi định nghĩa của nó là đa hình trong các loại k, trong khi các loại f2 đòi hỏi đối số của nó là đa hình trong các loại k.

f1 :: forall (k::BOX). (forall   (a::k) (m::k->*). m a -> Int) -> Int 
f2 ::     (forall (k::BOX) (a::k) (m::k->*). m a -> Int) -> Int 

-- Show restriction on *definition* 
f1 g = g (Just True) -- NOT OK. f1 must work for all k, but this assumes k is * 
f2 g = g (Just True) -- OK 

-- Show restriction on *argument* (thanks to Ørjan) 
x = undefined :: forall (a::*) (m::*->*). m a -> Int 
f1 x -- OK 
f2 x -- NOT OK. the argument for f2 must work for all k, but x only works for * 
Các vấn đề liên quan