Удаление дублирования (с помощью аппликативного ((->) t), возможно?)

Я играл с простой функцией для кого-то другого вопроса о переполнении стека и написал выражение:

f a x ++ f a y

Очевидно, что это лучший способ написать это выражение в реальной жизни, учитывая, что у меня есть все эти переменные в области видимости, но я видел дублирование f a и думал: "Эй, может быть, вы можете удалить это с помощью аппликативного экземпляр для функций". Я закончил с:

liftA2 (++) (flip f x) (flip f y) a

что просто ужасно. Есть ли какой-нибудь лучший способ удалить это дублирование? Очевидно, я мог бы также удалить дублирование, привязав f a к чему-то в предложении where, но это было предназначено как упражнение при использовании встроенных функций.

Ответы

Ответ 1

Вы могли бы сделать

((++) `on` f a) x y

Это не использует Applicative, хотя (извините).

Ответ 2

[...] возможно, вы можете удалить это с помощью аппликативного экземпляра для функций.

Вам нужно использовать экземпляр Applicative ((->) t)? Если вы просто хотите избавиться от дублированного f a, почему бы не использовать монаду списка вместо?

[x, y] >>= f a

или, что эквивалентно,

f a =<< [x, y]

Пример:

λ> let f :: Int -> Int -> [Int]; f a x = [a .. x]

λ> f 1 2 ++ f 1 3
[1,2,1,2,3]

λ> [2, 3] >>= f 1
[1,2,1,2,3]

λ> f 1 =<< [2, 3]
[1,2,1,2,3]

Ответ 3

Bikeshedding - это весело! Другой вариант - использовать экземпляр Monoid для функций:

(($x) <> ($y)) (f a)

Ответ 4

Так как вопрос намекнул на решение с использованием аппликативного (хотя другие ответы более элегантны)...

((++) <$> ($ x) <*> ($ y)) (f a)