2016-07-27 14 views
18

Can I, và nếu như vậy, làm thế nào, hãy viết loại chữ ký cho một chức năng:Loại chữ ký cho chức năng với khả năng lập luận đa hình

g f x y = (f x, f y) 

như vậy mà đưa ra:

f1 :: a -> [a] 
f1 x = [x] 

x1 :: Int 
x1 = 42 

c1 :: Char 
c1 = 'c' 

f2 :: Int -> Int 
f2 x = 3 * x 

x2 :: Int 
x2 = 5 

như vậy mà :

g f1 x1 c1 == ([42], ['c']) :: ([Int], [Char]) 
g f2 x1 x2 == (126, 15) :: (Int, Int) 
+1

như @ErikR cho thấy bạn trong câu trả lời (bị xóa) phía bên tay trái của bạn có thể thực hiện được nhưng loại trên rhs phụ thuộc vào loại 'f' vì vậy tôi đoán bạn sẽ cần sử dụng kiểu-gia đình - nếu điều này phù hợp với bạn hơn là có thể - tất nhiên đây chỉ là phỏng đoán - cho tất cả tôi biết có cách để diễn đạt điều này;) – Carsten

+0

Vui lòng sử dụng tiện ích mở rộng GHC – Clinton

+0

@ user3237465: trong nháy mắt (ít nhất là phiên bản đơn giản của bạn) Tôi nghĩ rằng bạn sẽ gặp rắc rối với 'f2' vì đây không phải là * forall a * nhưng chỉ cho' Int' còn – Carsten

Trả lời

15

Không, bạn không thể. Vấn đề cơ bản là cú pháp của Haskell đã lừa bạn nghĩ rằng các kiểu của f1f2 tương tự hơn so với thực tế. Khi dịch sang GHC Core, họ trông khá khác nhau nhiều:

f1 :: forall a . a -> [a] 
f2 :: Int -> Int 

Không chỉ này, nhưng tương ứng về cái nhìn khá khác nhau:

f1 = Λa -> λ(x :: a) -> [x] 
f2 = λ(x :: Int) -> 3 * x 

Như bạn thấy, f1f2 thực sự có số đối số khác nhau, trong đó f1 mất một loại và một giá trị f2 chỉ mất một giá trị.

Trong nhiều hoàn cảnh bình thường, khi bạn tiếng tom f2 vào một bối cảnh chờ đợi một chức năng của loại, chẳng hạn, Int -> [Int], GHC sẽ áp dụng f2 với loại cần thiết cho bạn (ví dụ, nhanh chóng f2 đến một loại hình cụ thể), và tất cả ý chí mạnh giỏi nhé. Ví dụ, nếu bạn có

g :: (Int -> [Int]) -> Bool 

và bạn áp dụng g-f, GHC sẽ thực sự biên dịch đó để

g (f @Int) 

Nhưng ở đây bạn muốn đa hình về việc liệu instantiation điều đó xảy ra hay không, mà GHC doesn' t hỗ trợ (Tôi nghĩ rằng nó sẽ là một thay đổi khá triệt để và phá hoại đối với ngôn ngữ cốt lõi).

Vì trường hợp lớp, nhập mẫu gia đình và nhập kết quả gia đình không thể được định lượng, tôi khá tự tin không có cách nào để có được thứ bạn muốn.

7

Điều này thực sự có thể xảy ra nếu bạn không ngại thêm đối số Proxy, sử dụng biến thể trên my answer to a similar question here.

Hầu hết các giải thích của tôi từ câu trả lời mà giữ ở đây, nhưng chúng ta cần phải mở rộng trên đó một chút bằng cách thêm một lớp vài chi tiết helper loại (những gì tôi đang kêu gọi ListBoth đây):

{-# LANGUAGE RankNTypes   #-} 
{-# LANGUAGE MultiParamTypeClasses #-} 
{-# LANGUAGE TypeFamilies   #-} 
{-# LANGUAGE FlexibleInstances  #-} 
{-# LANGUAGE ConstraintKinds  #-} 

import   Data.Proxy 

f1 :: a -> [a] 
f1 x = [x] 

x1 :: Int 
x1 = 42 

c1 :: Char 
c1 = 'c' 

f2 :: Int -> Int 
f2 x = 3 * x 

x2 :: Int 
x2 = 5 

class b ~ [a] => List a b 
instance List a [a] 

class (a ~ b, b ~ c) => Both a b c 
instance Both a a a 

g :: (c a r1, c b r2) => 
     Proxy c -> (forall x r. c x r => x -> r) -> a -> b -> (r1, r2) 
g _ f x y = (f x, f y) 

này cho phép chúng ta làm

ghci> g (Proxy :: Proxy List) f1 x1 c1 
([42],"c") 
ghci> g (Proxy :: Proxy (Both Int)) f2 x1 x2 
(126,15) 

ListBoth không phải là những cái tên tốt nhất (đặc biệt là List), vì vậy bạn có thể muốn đưa ra với những người thân tốt hơn nếu bạn d o sử dụng điều này (mặc dù tôi không chắc chắn tôi sẽ khuyên bạn nên thực hiện loại thủ đoạn kiểu này trong mã sản xuất, trừ khi bạn có thực sự là lý do chính đáng)).

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