Раздел оператора для аппликативного с <$> и <*>
Рассмотрим функции типа a -> b -> c
и аппликативные значения a1, a2 :: (Applicative f) => f a
,
Я хочу построить функцию, которая может быть применена к функциям типа a -> b -> c
получить значения типа Applicative f :: f c
, Я могу сделать это следующим образом:
g :: (Applicative f) => (a -> b -> c) -> f c
g = \f -> f <$> a1 <*> a2
(Явная лямбда преднамеренная, так как я рассматриваю конструкцию этой функции на любом уровне, а не только на верхнем уровне).
Если я попытаюсь написать g
в бессмысленном стиле:
g = (<$> a1 <*> a2)
Я получаю следующую ошибку компиляции:
The operator `<$>' [infixl 4] of a section
must have lower precedence than that of the operand,
namely `<*>' [infixl 4]
in the section: `<$> gen1 <*> gen2'
Я мог бы написать эту бессмысленную реализацию:
g = flip (flip liftA2 a1) a2
но я чувствую, что это менее читабельно, и проще реорганизовать реализацию, основанную на функциях инфикса, например, для добавления другого аргумента, чем изменять приведенное выше для использования liftA3
,
Можно написать цепочку композиций:
g = (<*> a2) . (<$> a1)
Это позволяет получить стиль без точек и просто добавить аргументы - но они добавляются слева, а не справа, поэтому вы теряете соответствие с типом функции. (a -> b -> c)
, Кроме того, с большим количеством аргументов вы получите гораздо более длинное выражение, чем просто использование лямбда-выражения, как в первой реализации.
Итак, есть ли хороший, краткий способ написать раздел, который я хочу, или я застрял с лямбда?
2 ответа
Я не совсем уверен, что pointfree можно сделать лучше, чем использовать здесь явный аргумент, но есть еще пара идей:
Ты можешь использовать
flip
инфикс:g = liftA2 `flip` a1 `flip` a2
Ты можешь использовать
>>>
отControl.Category
, который.
переворачивается:g = (<$> a1) >>> (<*> a2)