Раздел оператора для аппликативного с <$> и <*>

Рассмотрим функции типа 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 ответа

<*> действует на результат <$>, так:

g = (<*> a2) . (<$> a1)

Я не совсем уверен, что pointfree можно сделать лучше, чем использовать здесь явный аргумент, но есть еще пара идей:

  • Ты можешь использовать flip инфикс:

    g = liftA2 `flip` a1 `flip` a2
    
  • Ты можешь использовать >>> от Control.Category, который . переворачивается:

    g = (<$> a1) >>> (<*> a2)
    
Другие вопросы по тегам