Sau khi đọc this article on writing polyvariadic functions in Haskell, tôi đã cố gắng viết một số của riêng mình.Chức năng đa biến trong Haskell
Lúc đầu, tôi nghĩ tôi sẽ cố gắng khái quát hóa nó - vì vậy tôi có thể có một hàm trả về các hàm variadic bằng cách thu gọn các đối số như đã cho.
{-# OPTIONS -fglasgow-exts #-}
module Collapse where
class Collapse a r | r -> a where
collapse :: (a -> a -> a) -> a -> r
instance Collapse a a where
collapse _ = id
instance (Collapse a r) => Collapse a (a -> r) where
collapse f a a' = collapse f (f a a')
Tuy nhiên, trình biên dịch không thích rằng:
Collapse.hs:5:9:
Functional dependencies conflict between instance declarations:
instance Collapse a a -- Defined at Collapse.hs:5:9-20
instance (Collapse a r) => Collapse a (a -> r)
-- Defined at Collapse.hs:7:9-43
Nếu tôi quay trở lại và thêm một loại wrapper cho kết quả cuối cùng, tuy nhiên, nó làm việc:
module Collapse where
class Collapse a r | r -> a where
collapse :: (a -> a -> a) -> a -> r
data C a = C a
instance Collapse a (C a) where
collapse _ = C . id
instance (Collapse a r) => Collapse a (a -> r) where
collapse f a a' = collapse f (f a a')
sum :: (Num a, Collapse a r) => a -> r
sum = collapse (+)
Một khi tôi đã thực hiện thay đổi này, nó biên soạn tốt, và tôi có thể sử dụng chức năng collapse
trong ghci
.
ghci> let C s = Collapse.sum 1 2 3 in s
6
Tôi không chắc chắn tại sao loại trình bao bọc được yêu cầu cho kết quả cuối cùng. Nếu ai đó có thể giải thích điều đó, tôi sẽ đánh giá cao điều đó. Tôi có thể thấy rằng trình biên dịch nói với tôi rằng đó là một số vấn đề với các phụ thuộc chức năng, nhưng tôi không thực sự grok việc sử dụng thích hợp của fundeps được nêu ra.
Sau đó, tôi đã cố gắng thực hiện một cách khác, và thử xác định bộ tạo hàm variadic cho các hàm lấy danh sách và trả về một giá trị. Tôi đã phải làm cùng một mẹo chứa và cũng cho phép UndecidableInstances
.
{-# OPTIONS -fglasgow-exts #-}
{-# LANGUAGE UndecidableInstances #-}
module Variadic where
class Variadic a b r | r -> a, r -> b where
variadic :: ([a] -> b) -> r
data V a = V a
instance Variadic a b (V b) where
variadic f = V $ f []
instance (Variadic a b r) => Variadic a b (a -> r) where
variadic f a = variadic (f . (a:))
list :: Variadic a [a] r => r
list = variadic . id
foldl :: (Variadic b a r) => (a -> b -> a) -> a -> r
foldl f a = variadic (Prelude.foldl f a)
Không cho phép UndecidableInstances
trình biên dịch phàn nàn rằng tuyên bố dụ tôi là bất hợp pháp:
Variadic.hs:7:0:
Illegal instance declaration for `Variadic a b (V b)'
(the Coverage Condition fails for one of the functional dependencies;
Use -XUndecidableInstances to permit this)
In the instance declaration for `Variadic a b (V b)'
Variadic.hs:9:0:
Illegal instance declaration for `Variadic a b (a -> r)'
(the Coverage Condition fails for one of the functional dependencies;
Use -XUndecidableInstances to permit this)
In the instance declaration for `Variadic a b (a -> r)'
Tuy nhiên, một khi nó được biên dịch, tôi có thể thành công sử dụng nó trong ghci:
ghci> let V l = Variadic.list 1 2 3 in l
[1,2,3]
ghci> let vall p = Variadic.foldl (\b a -> b && (p a)) True
ghci> :t vall
vall :: (Variadic b Bool r) => (b -> Bool) -> r
ghci> let V b = vall (>0) 1 2 3 in b
True
Tôi đoán những gì tôi đang tìm kiếm là giải thích lý do tại sao loại vùng chứa cho giá trị cuối cùng là cần thiết, cũng như tại sao tất cả các dấu câu khác nhau phụ thuộc nal là cần thiết.
Ngoài ra, đây có vẻ kỳ lạ:
ghci> let vsum = Variadic.foldl (+) 0
<interactive>:1:10:
Ambiguous type variables `a', `r' in the constraint:
`Variadic a a r'
arising from a use of `Variadic.foldl' at <interactive>:1:10-29
Probable fix: add a type signature that fixes these type variable(s)
<interactive>:1:10:
Ambiguous type variable `a'in the constraint:
`Num a' arising from the literal `0' at <interactive>:1:29
Probable fix: add a type signature that fixes these type variable(s)
ghci> let vsum' = Variadic.foldl (+)
ghci> :t vsum'
(Num a, Variadic a a r) => t -> a -> r
ghci> :t vsum' 0
(Num a, Variadic a a r) => a -> r
ghci> let V s = vsum' 0 1 2 3 in s
6
Tôi đoán đó là hậu quả của phép UndecidableInstances
, nhưng tôi không biết, và tôi muốn hiểu rõ hơn về những gì đang xảy ra.
Đây là một số mã rất hay mà bạn đang thử nghiệm! Và bài viết đó của Oleg Kiselyov thật tuyệt vời, nó hoàn toàn thổi bay tôi lần đầu tiên tôi đọc nó và vẫn có tác dụng đó ngay bây giờ. :-) Tôi đã chụp một câu trả lời liên quan đến sự cần thiết của các loại wrapper ... Hy vọng nó sẽ giúp. –