Có một số phương pháp bạn có thể thực hiện; Tôi không nghĩ rằng bạn đã cung cấp đủ ngữ cảnh để xác định ngữ cảnh nào phù hợp nhất. Nếu bạn đang sử dụng GHC-7.4, bạn có thể thử tiện ích mở rộng DefaultSignatures
.
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE DefaultSignatures #-}
-- A generic class with a generic function.
class Foo a where
foo :: a -> a
default foo :: Bar a => a -> a
foo = bar . baz
-- A specific class with specific functions.
class Bar a where
bar :: a -> a
baz :: a -> a
instance Bar String where
bar = id
baz = id
instance Foo String
main :: IO()
main =
putStrLn (foo "bar")
Bạn vẫn cần phải tuyên bố rằng một loại là một thể hiện của Foo
, nhưng bạn không cần phải lặp lại phương pháp kê khai vì việc thực hiện mặc định sẽ được sử dụng.
Một cách tiếp cận khá nhẹ khác là sử dụng loại mới. Nếu bạn có các hàm cần một cá thể Foo
, bạn có thể bao bọc một phiên bản Bar
trong loại mới.
newtype FooBar a = FooBar { unFooBar :: a }
instance Bar a => Foo (FooBar a) where
foo = FooBar . bar . baz . unFooBar
-- imported from a library or something...
needsFoo :: Foo a => a -> b
myFunc = needsFoo (FooBar someBar)
Ngoài ra, bạn có thể nhận được bởi với thay foo
với một chức năng bình thường, hoặc thực hiện một phiên bản đặc biệt cho Bar
trường hợp:
-- if every `Foo` is also a `Bar`, you can just do this. No need for `Foo` at all!
foo :: Bar a => a -> a
foo = bar . baz
-- if most `Foo`s aren't `Bar`s, you may be able to use this function when you have a `Bar`
fooBar :: Bar a => a -> a
foo = bar . baz
Đây là có lẽ là giải pháp tốt nhất nếu họ làm việc cho hoàn cảnh của bạn.
Một tùy chọn khác là khai báo mỗi cá thể Foo
thủ công. Mặc dù có thể có nhiều trường hợp khác nhau, nhưng nó khá phổ biến đối với các codebases chỉ có một số ít các cá thể thực sự được sử dụng. Nếu đó là sự thật ở đây, nó có lẽ ít công việc để chỉ viết ra 3 hoặc 4 trường hợp bạn cần thay vì cố gắng thực hiện một giải pháp tổng quát hơn.
Là phương sách cuối cùng, bạn có thể sử dụng mã như mã ban đầu, nhưng bạn cũng cần OverlappingInstances
để làm cho nó hoạt động (nếu bạn không cần OverlappingInstances
, thì bạn không cần lớp Foo
). Đây là phần mở rộng cho phép GHC chọn "trường hợp cụ thể nhất" khi có nhiều kết quả phù hợp. Điều này sẽ làm việc nhiều hơn hoặc ít hơn, mặc dù bạn có thể không nhận được những gì bạn mong đợi.
class Foo a where
foo :: a -> a
class Bar a where
bar :: a -> a
baz :: a -> a
instance Bar String where
bar = id
baz = id
instance Bar a => Foo a where
foo = bar . baz
instance Foo [a] where
foo _ = []
main :: IO()
main =
print (foo "foo")
Bây giờ main
in một chuỗi rỗng. Có hai trường hợp Foo
, cho a
và [a]
. Sau này là cụ thể hơn, vì vậy nó được chọn cho foo "foo"
vì một chuỗi có loại [Char]
, mặc dù bạn có thể muốn trước đây. Vì vậy, bây giờ bạn cũng cần phải viết
instance Foo String where
foo = bar . baz
tại thời điểm đó bạn cũng có thể bỏ hoàn toàn trường hợp Bar a => Foo a
hoàn toàn.
Bằng cách khai báo 'instance Bar a => Foo a' bạn đã giới thiệu một cá thể cho mỗi' a' - ngữ cảnh 'Bar a' không được sử dụng trong trường hợp chọn - mặc dù nếu bạn có trường hợp chồng chéo GHC sẽ tìm thấy nhiều nhất cụ thể trên cơ sở mỗi mô-đun. –
Đó là ... truy cập trực quan :-) Có cách nào không? Tôi giả định rằng "tìm kiếm một ví dụ cụ thể nhất" là những gì 'UndecidableInstances' sẽ cố gắng làm, nhưng trong mã của tôi nó đã thất bại thảm hại. –
Liên quan: http://stackoverflow.com/questions/3213490 – sdcvvc