После чтения этой статьи о записи polyvariadic функционирует в Haskell, я пытался записать некоторым моим собственным.
Сначала я думал, что попытаюсь обобщить его - таким образом, у меня могла быть функция, которая возвратила функции variadic путем сворачивания аргументов, как дали.
{-# 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
Я не уверен, почему тип обертки требуется для конечного результата. Если бы кто-либо мог бы объяснить, что, я высоко ценил бы его. Я вижу, что сообщение компилятора меня, что это - некоторая проблема с функциональными зависимостями, но я еще не делаю действительно grok надлежащего использования fundeps.
Позже, я пытался выбрать другой подход и попытаться определить variadic функциональный преобразователь для функций, которые взяли список и возвратили значение. Я должен был сделать тот же контейнерный прием и также позволить 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
:1:10:
Ambiguous type variables `a', `r' in the constraint:
`Variadic a a r'
arising from a use of `Variadic.foldl' at :1:10-29
Probable fix: add a type signature that fixes these type variable(s)
:1:10:
Ambiguous type variable `a'in the constraint:
`Num a' arising from the literal `0' at :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
, но я не знаю, и я хотел бы лучше понять то, что продолжается.
Идея функциональных зависимостей заключается в том, что в объявлении типа
class Collapse a r | r -> a where
...
бит r -> a
говорит что a
однозначно определяется r
. Итак, у вас не может быть экземпляра Collapse (a -> r) (a -> r)
и экземпляра Collapse a (a -> r)
. Обратите внимание, что экземпляр Collapse (a -> r) (a -> r)
следует из instance Collapse a a
для полного изображения.
Другими словами, ваш код пытается установить экземпляр Collapse t t
(имя переменной типа, конечно, не имеет значения) и экземпляр Collapse a (a -> r)
. Если вы замените (a -> r)
на t
в объявлении первого экземпляра, вы получите instance Collapse (a -> r) (a -> r)
. Теперь это единственный экземпляр Collapse
со вторым параметром типа, равным (a -> r)
, который у вас может быть , потому что в объявлении класса указано, что параметр первого типа должен выводиться из второго. Затем вы попытаетесь установить экземпляр a (a -> r)
, который добавит еще один экземпляр Collapse
со вторым параметром типа (a -> r)
. Таким образом, GHC жалуется.
Если вы все еще экспериментируете с этим, вот пример построения поливариадической функции из функции, принимающего список, не требуя либо типа обертки, ни неразрешенных экземпляров:
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE FunctionalDependencies #-}
class Variadic a b r | r -> a where
variadic :: ([a] -> b) -> r
instance Variadic a b (a -> b) where
variadic f x = f [x]
instance (Variadic a b (a -> r)) => Variadic a b (a -> a -> r) where
variadic f x y = variadic (f . (x:)) y
vList :: (Variadic a [a] r) => r
vList = variadic id
vFoldl :: (Variadic b a r) => (a -> b -> a) -> a -> r
vFoldl f z = variadic (foldl f z)
vConcat :: (Variadic [a] [a] r) => r
vConcat = vFoldl (++) []
main = do
putStrLn $ vConcat "abc" "def" "ghi" "jkl"
putStrLn $ vList 'x' 'y' 'z'
if vFoldl (&&) True True True True then putStrLn "Yes" else putStrLn "No"
if vFoldl (&&) True True False True then putStrLn "Yes" else putStrLn "No"
Данные для этого подхода - это то, что тип проверки типа Должен быть в состоянии сделать вывод типа результата (или вы должны аннотировать его), и что он не сильно не удается на полиморфных числовых ценных константах; Причины обеих проблем обсуждаются в статье, которую вы упомянули. Не знаю, если вы обнаружите, что это полезно, но я развалился с поливариадическими функциями ранее и вспомнил этот вопрос.
Michał Marczyk - это абсолютно правильно относится к вопросу о необходимости сопоставления и принципиальной связи, а тип обертки выглядит как простое исправление. С другой стороны, если вы уже читаете сайт Олега, вы можете предпочесть пойти глубже по глубине кролика и попробуйте написать экземпляр для «любого типа, который не функция ".
Насколько нераздельно проходят неразъемы, условие покрытия описано здесь ; должно быть очевидно, почему ваши случаи не удаются. Обратите внимание, что слово «неразрешено» здесь означает неразрешимый в примерно в том же смысле, что и в «проблемах остановки неразрешен» - то есть вы говорите HHC безрассудно, пытаясь решить код, который может отправить его в бесконечную петлю основываясь только на вашем утверждении, что все в порядке. Это весело для взлома аккуратных идей, но согласие быть человеком первого прохода типа Checker для GHC - это бремя, которое я лично нахожу утомляю.