haskell - связывает элементы с ассоциативной бинарной операцией
Я промежуточный интриган, но только начинающий на хаскеле. Вот моя проблема:
Предположим, у вас есть ассоциативная бинарная операция, говорит (>>=)
, Есть ли поливариадная функция p
такой, что p (>>=) h g f e = h >>= g >>= f >>= e
?
Я задаю этот вопрос, потому что этот вопрос говорит, что это возможно, если бинарная операция принимает входные данные одного типа. Интересно, можно ли это обобщить?
РЕДАКТИРОВАТЬ-1: Я пытаюсь изменить код в http://okmij.org/ftp/Haskell/vararg-fn.lhs (раздел Переменное число аргументов с переменной типизацией) с небольшим прогрессом.
РЕДАКТИРОВАТЬ-2: немного упростить код.
{-# LANGUAGE FunctionalDependencies, FlexibleInstances #-}
module Main where
class Lfold f a b | b -> a where
lfold :: (a -> (f a) -> (f a)) -> (f a) -> a -> b
instance Lfold f a (f a) where
lfold op rid x = op x rid
instance Lfold f a b => Lfold f a (a -> b) where
lfold op rid x y = lfold op (op x rid) y
test :: [String]
test = lfold (:) [] "a" "b" "c"
main :: IO ()
main = putStrLn $ show test
2 ответа
Да, вы можете создать такую функцию. Однако это очень уродливо, и вам нужно будет явно вводить каждый аргумент, который вы собираетесь передать, чтобы компилятор нашел правильный экземпляр.
Начиная с шаблона поливариадной функции, который вы связали, я пришел к
{-# LANGUAGE FlexibleInstances, InstanceSigs, MultiParamTypeClasses #-}
class ImplicitChain m a r where
p :: m a -> r
instance Monad m => ImplicitChain m a (m a) where
p :: m a -> m a
p x = x
instance (Monad m, ImplicitChain m b r) => ImplicitChain m a ((a -> m b) -> r) where
p :: m a -> (a -> m b) -> r
p x f = p (x >>= f)
h :: Int -> [Int]
h = replicate 2
g :: Int -> [Int]
g = (:[])
f :: Int -> [Int]
f = flip enumFromTo 2
test :: [Int]
test = p [1::Int] h g f
Но вы спрашивали, можем ли мы сделать более универсальный вариант, чтобы двоичная операция также являлась аргументом. Да:
{-# LANGUAGE FlexibleInstances, InstanceSigs, MultiParamTypeClasses, UndecidableInstances #-}
class ImplicitVariadic a b r where
p :: (a -> b -> a) -> r
instance ImplicitVariadic a b (a -> a) where
p :: (a -> b -> a) -> a -> a
p _ x = x
instance (ImplicitVariadic a b (a -> r)) => ImplicitVariadic a b (a -> b -> r) where
p :: (a -> b -> a) -> a -> b -> r
p f x y = p f (f x y)
Вы не можете (по крайней мере, не легко), потому что вам нужно знать, сколько аргументов вы опередили. Поскольку все функции в Haskell автоматически каррируются, каждая функция принимает ровно один аргумент и возвращает одно значение. Даже простой бинарный оператор принимает один аргумент (первый операнд) и возвращает функцию, которая принимает один аргумент (второй операнд) и возвращает результат. То есть,
a + b == (+) a b
== ((+) a) b
Там нет пути для вашей воображаемой функции p
узнать из своего первого аргумента, сколько еще аргументов будет дано. То есть какой тип должен p
быть?
p :: (a -> a -> a) -> a -- zero arguments?
p :: (a -> a -> a) -> a -> a -- one argument?
p :: (a -> a -> a) -> a -> a -> a -- two arguments?
p :: (a -> a -> a) -> a -> a -> a -> a -- three arguments?
Вместо этого лучшее, что вы можете сделать, это использовать фолд, который принимает операцию и список операндов.
foldr (+) 0 [h, g, f, e] == h + g + f + e + 0 -- explicit first argument of 0
foldr1 (+) [h, g, f, e] == h + g + f + e -- assumes a list of at least one value
Чтобы увидеть, что я имею в виду под "не легко", посмотрите на реализацию printf
в модуле Text.Printf. Даже это не очень хороший пример, потому что первый аргумент содержит информацию (количество заполнителей в строке формата), которой нет в двоичной операции.