2011-12-11 11 views
5

Giả sử tôi có một số chức năng chungCung cấp một cơ quan chức năng khác nhau cho một hàm tổng quát dựa trên loại

genericFunc :: a -> b 
genericFunc x = doSomeHardWork 

Nhưng đối với một loại đặc biệt, có một cách hiệu quả hơn mà genericFunc thể được thực hiện.

genericFunc :: ParticularType -> b 
genericFunc x = doSomeEasyWork 

cách tốt nhất để kết hợp hai cơ quan chức năng vào cùng genericFunc là gì, như vậy mà khi sử dụng trên ParticularType, nó sẽ doSomeEasyWork, nhưng khi được sử dụng trên các loại khác, nó sẽ doSomeHardWork? Tôi đặc biệt loại trừ tùy chọn sử dụng tên khác hoặc các mô-đun khác.

Tôi tin rằng điều này có thể được thực hiện với một typeclass, nhưng tôi quan tâm nhiều hơn đến các giải pháp sử dụng pragmas ngôn ngữ. Tôi có một ý tưởng mơ hồ rằng điều này có thể được thực hiện với pragmas ngôn ngữ nhưng tôi không có ý tưởng làm thế nào. Điểm thưởng nếu bạn so sánh và đối chiếu các cách tiếp cận này và/hoặc bất kỳ phương pháp tiếp cận nào có thể khác.

+0

Lấy cảm hứng mơ hồ khi nghĩ về câu hỏi này: [Nếu có gì đó không phải là danh sách] (http://stackoverflow.com/questions/ 8463777/if-something-is-a-list-in-haskell) –

Trả lời

8

Điều này có thể được thực hiện với các lớp loại bằng cách xác định phương pháp đa năng trong định nghĩa lớp và ghi đè nó trong một cá thể. Chức năng ghi đè sẽ luôn được sử dụng.

class ContainsInt c where 
    toList :: c -> [Int] 

    -- generic function 
    elem :: Int -> c -> Bool 
    elem n x = Prelude.elem n (toList x) 

instance ContainsInt() where 
    toList _ = [] 

    -- Override the generic function for type() 
    elem _ _ = False 

Một thay thế được hỗ trợ bởi GHC là sử dụng quy tắc ghi lại. Quy tắc viết lại yêu cầu GHC thay thế một biểu thức này bằng một biểu thức khác bất cứ khi nào có thể. Nếu sự thay thế không được đánh máy, nó sẽ không được thực hiện, vì vậy bạn có thể sử dụng nó để thay thế một chức năng bằng một phiên bản chuyên dụng. Quy tắc viết lại được đưa ra bởi một pragma {-# RULES #-}.

class ContainsInt c where 
    toList :: c -> [Int] 

elem :: ContainsInt c => Int -> c -> Bool 
elem n x = Prelude.elem n (toList x) 

-- Replace 'elem' by 'elemUnit' if it has the same type 
{-# RULES "elem()" forall. elem = elemUnit #-} 

elemUnit :: Int ->() -> Bool 
elemUnit _ _ = False 

Quy tắc ghi lại được thực hiện theo quyết định của trình biên dịch, vì vậy chức năng chuyên biệt có thể hoặc không được gọi trong bất kỳ tình huống cụ thể nào. Ví dụ, viết lại có thể phụ thuộc vào việc trình biên dịch quyết định nội tuyến một hàm:

foo :: ContainsInt c -> Int -> [c] -> [Bool] 
-- Must use the generic function 
foo n cs = map (elem n) cs 

useFoo :: Int -> [()] -> [Bool] 
-- If 'foo' is inlined and 'elem' is not inlined, then this function will contain a rewritable call to 'elem'. 
-- Otherwise rewriting cannot happen. 
useFoo n cs = foo n cs 
2

Với GHC, bạn có thể sử dụng RULES pragma.

{-# RULES "genericFunc/easy" genericFunc = doSomeEasyWork #-} 

Điều này sẽ áp dụng quy tắc viết lại bất cứ khi nào các loại khớp và sử dụng cách thực hiện chung khác.

+0

Bất cứ khi nào _GHC có thể chứng minh rằng các loại khớp với nhau. –

+1

Có một số ví dụ hay về điều này "trong mã thực" mà tôi có thể xem? –

+1

Không sử dụng quy tắc, sử dụng kiểu chữ. Đây là những gì typeclasses cho. – augustss