Việc đặt một ràng buộc trong kiểu đối số thay vì kiểu hàm làm gì?


8

Tôi đặt một ràng buộc trong kiểu đối số của hàm thay vì đặt kiểu của hàm.
Tôi nghĩ rằng điều này sẽ cung cấp một lỗi cú pháp hoặc thêm thông tin vào loại hàm.
Nhưng có vẻ như các ràng buộc hoàn toàn bị bỏ qua.

{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE RankNTypes #-}

test :: a -> String
test (n :: (Num a, Ord a) => a) =
    if n > 10 then "Hello"
    else "World"

main = print "Hello World"

Điều này đưa ra lỗi loại sau:

Test3.hs:6:8: error:
     No instance for (Num a) arising from a use of n
      Possible fix:
        add (Num a) to the context of
          the type signature for:
            test :: forall a. a -> String
     In the first argument of ‘(>)’, namely n
      In the expression: n > 10
      In the expression: if n > 10 then "Hello" else "World"
  |
6 |     if n > 10 then "Hello"
  |        ^

Test3.hs:6:8: error:
     No instance for (Ord a) arising from a use of ‘>’
      Possible fix:
        add (Ord a) to the context of
          the type signature for:
            test :: forall a. a -> String
     In the expression: n > 10
      In the expression: if n > 10 then "Hello" else "World"
      In an equation for test’:
          test (n :: (Num a, Ord a) => a)
            = if n > 10 then "Hello" else "World"
  |
6 |     if n > 10 then "Hello"
  |  

Điều gì đặt một ràng buộc trong kiểu của đối số thực sự làm gì?

BIÊN TẬP:

Tại sao điều này cần RankNTypesmở rộng?
Nó không cần thiết nếu tôi loại bỏ (Num a, Ord a) =>ràng buộc.


Bình luận không dành cho thảo luận mở rộng; cuộc trò chuyện này đã được chuyển sang trò chuyện .
Samuel Liew

Câu trả lời:


7

Đây là một ví dụ khá kỳ lạ về sự sụt giảm đa hình, như được mô tả ở đây , tương tác với sự sụt giảm ràng buộc.

Nếu một loại asubsumes b, sau đó exp :: ahàm ý exp :: btrong ngôn ngữ bề mặt. Một ví dụ cụ thể về sự sụt giảm là f :: forall a. a -> angụ ý f :: Int -> Int. Ngoài ra, chúng tôi có n :: Intngụ ý n :: c => Intcho bất kỳ cràng buộc.

Tuy nhiên, trong ngôn ngữ cốt lõi không có sự sụt giảm nào cả. Mỗi trường hợp sử dụng ngôn ngữ bề mặt phải được dịch sang lambdas và ứng dụng rõ ràng. Ngoài ra, c => ađơn giản trở thành c -> avà việc sử dụng các hàm bị ràng buộc được dịch sang ứng dụng hàm đơn giản f :: c => acho một số inst :: c. Do đó, f :: forall a. a -> atrở thành f @Int :: Int -> Int, và n :: Inttrở thành \_ -> n :: c -> Int.

Một trường hợp hiếm khi được sử dụng là quy tắc phụ trợ chống chỉ định cho các chức năng. Sau đây là mã hợp lệ:

f :: (Int -> Int) -> Bool
f _ = True

g :: (forall a. a -> a) -> Bool
g = f

Điều này được dịch sang

f :: (Int -> Int) -> Bool
f = \_ -> True

g :: (forall a. a -> a) -> Bool
g = \x -> f (x @Int)

Nó hoạt động tương tự với hạn chế ràng buộc:

f :: forall a. (Eq a => a) -> Bool
f _ = True

g :: forall a . a -> Bool
g = f

Được dịch sang

f :: forall a. (Eq a -> a) -> Bool
f = \_ -> True

g :: forall a . a -> Bool
g = \x -> f (\_ -> x)

Đến gần hơn với câu hỏi ban đầu, nếu chúng ta có

f (x :: Eq a => a) = True

như một định nghĩa hàng đầu, loại suy ra của nó là forall a. (Eq a => a) -> Bool. Tuy nhiên, chúng ta có thể có bất kỳ chú thích loại fnào được loại bởi loại suy ra! Vì vậy, chúng tôi có thể có:

f :: forall a. a -> Bool
f (x :: Eq a => a) = True

Và GHC vẫn hạnh phúc. Mã ban đầu

test :: a -> String
test (n :: (Num a, Ord a) => a) =
    if n > 10 then "Hello"
    else "World"

tương đương với phiên bản sau rõ ràng hơn một chút:

test :: forall a. a -> String
test (n :: (Num a, Ord a) => a) =
    if n > 10 then "Hello"
    else "World"

Lỗi loại mà bạn nhận được chỉ đơn giản là vì nthực sự là một hàm có hai đối số, một đối số có loại Num avà loại kia Ord avà cả hai đối số này đều là bản ghi chứa NumOrdphương thức. Tuy nhiên, vì không có trường hợp nào như vậy trong phạm vi trong định nghĩa, bạn không thể sử dụng nlàm số. Bản dịch sẽ chuyển đổi n > 10sang (>) inst (n inst) (10 inst), ở đâu inst :: Num a, nhưng không có như vậy inst, vì vậy chúng tôi không thể dịch.

Do đó, trong phần thân của testmã vẫn được kiểm tra n :: (Num a, Ord a) => a). Tuy nhiên, nếu chúng ta chỉ trả về "Xin chào" mà không sử dụng n, thì tương tự như ftrường hợp trước , chúng ta sẽ có một loại được suy ra bao gồm forall a. a -> Stringloại chú thích. Sau đó subsumption được thực hiện trong đầu ra dịch bằng cách thay thế mỗi lần xuất hiện của ntrong cơ thể của testvới \_ -> n. Nhưng vì nkhông xảy ra trong cơ thể, bản dịch không làm gì ở đây.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.