Применить список булевых функций к двум аргументам в Haskell
У меня есть список функций типа a -> b -> Bool
, и я пытаюсь применить их к двум входам и объединить результаты с All
или же Any
, У меня это работает с функциями одной переменной:
mconcat (map (All . ) [(<7),(>7),(==7)]) $ 6
но я не могу понять, как сделать то же самое с двумя переменными функциями.
Это работает:
mconcat (map (All . ) (map (uncurry) [(<),(>),(==)])) $ (,) 6 7
но это выглядит как уродливый обходной путь.
Есть лучший способ сделать это?
2 ответа
Это тот код, который пишет сам - нужно просто объединить типы. Но есть пара стандартных инструментов (а именно Applicative
а также Traversable
), который вы можете использовать, чтобы сделать ваш код короче.
в Data.Traversable
модуль жизни sequenceA :: (Traversable t, Applicative f) => t (f a) -> f (t a)
, Когда специализируется на []
экземпляр Traversable
и функция аппликативная (->) r
мы получаем:
sequenceA :: [r -> a] -> r -> [a]
Так sequenceA
рывки ->
снаружи []
, применяя каждую функцию в списке к фиксированному аргументу.
sequenceA [(< 7), (> 7), (== 7)] :: (Num n, Ord n) => n -> [Bool]
-- equivalent to:
\n -> map ($ n) [(< 7), (> 7), (== 7)]
Итак, ваша первая функция может быть записана как
f :: (Num n, Ord n) => n -> Bool
f = and . sequenceA [(< 7), (> 7), (== 7)]
я использую and
вместо mconcat . map (All .)
,
Для вашей второй функции, uncurry
это правильный инструмент. Мы должны на карту uncurry
через список бинарных функций, чтобы получить список унарных функций кортежей, чтобы мы могли поднять один аргумент, используя sequenceA
, Так как traverse f = sequenceA . map f
мы можем написать это как:
g :: Ord n => n -> n -> Bool
g = curry $ and . traverse uncurry [(<), (>), (==)]
(NB, для любого правильно реализованного экземпляра Ord
, >
а также <
должен быть взаимоисключающим. Так что обе эти функции всегда будут возвращать False
.)
Близкая альтернатива оригинальному коду:
mconcat (map (\f a b -> All (f a b)) [(<),(<=)]) 3 4
Можно еще переписать \f a b -> All (f a b)
в бессмысленном стиле:
mconcat (map ((.) (All .)) [(<),(<=)]) 3 4