После прочтения this article on writing polyvariadic functions in Haskell, я попытался написать свои собственные.Поливарианские функции в Haskell
Сначала мне показалось, что я попытаюсь обобщить его - так что я мог бы иметь функцию, которая возвращала вариационные функции, сворачивая аргументы как заданные.
{-# 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')
Однако компилятор не нравится:
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
Если бы я вернулся и добавил тип обертки для конечного результата, однако, он работал:
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 (+)
Как только я сделал это изменение, он скомпилировался отлично, и я мог использовать функцию collapse
в ghci
.
ghci> let C s = Collapse.sum 1 2 3 in s
6
Я не уверен, почему для окончательного результата требуется тип обертки. Если бы кто-нибудь мог это объяснить, я был бы очень признателен. Я вижу, что компилятор говорит мне, что это проблема с функциональными зависимостями, но я пока не понимаю, как правильно использовать накопители.
Позже я попытался использовать другой подход и попытался определить генератор вариационной функции для функций, которые взяли список и вернули значение. Я должен был сделать тот же контейнерный трюк, а также разрешить 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)
Не позволяя UndecidableInstances
компилятор жаловался, что мои объявления экземпляров были незаконными:
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)'
Однако после того, как он составлен, я мог бы с успехом использовать его в 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
Я думаю, , что я ищу, объясняет, почему необходим тип контейнера для конечного значения, а также почему все различные функции Нулевые зависимости необходимы.
Кроме того, это казалось странным:
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
Я предполагаю, что это радиоактивные осадки от разрешения UndecidableInstances
, но я не знаю, и я хотел бы, чтобы лучше понять, что происходит.
Это очень классный код, с которым вы экспериментируете! И эта статья Олега Киселева великолепна, она полностью сдула меня в первый раз, когда я прочитал ее и все еще имеет этот эффект. :-) Я сделал попытку ответить на вопрос о необходимости типа обертки ... Надеюсь, это поможет. –