Как переписать функцию Haskell из двух аргументов в стиль без точек

У меня есть следующая функция в Haskell

agreeLen :: (Eq a) => [a] -> [a] -> Int
agreeLen x y = length $ takeWhile (\(a,b) -> a == b)  (zip x y)

Я пытаюсь научиться писать "идиоматический" Haskell, который, по-видимому, предпочитает использовать . и $ вместо круглых скобок, а также, если это возможно, предпочитает код без кода. Я просто не могу избавиться от явного упоминания x и y. Есть идеи?

Я думаю, что у меня будет такая же проблема с pointfreeing любой функцией из двух аргументов.

Кстати, это просто стремление писать хороший код; а не "использовать все, что нужно, чтобы сделать это беспроблемным" домашним упражнением.

Спасибо.


(Добавлен комментарий) Спасибо за ответы. Вы убедили меня, что эта функция не приносит пользы от pointfree. И вы также дали мне отличные примеры для практики трансформации выражений. Мне все еще сложно, и они кажутся столь же важными для Haskell, что и указатели на C.

Ответ 1

а также, если это возможно, предпочтительнее использовать код точки.

Не "где возможно", но "где он улучшает читаемость (или имеет другие преимущества)".

Чтобы освободить ваш

agreeLen x y = length $ takeWhile (\(a,b) -> a == b)  (zip x y)

Первым шагом будет переместить ($) вправо и заменить тот, который у вас есть с (.):

agreeLen x y = length . takeWhile (\(a,b) -> a == b) $ zip x y

Теперь вы можете перемещать его еще дальше:

agreeLen x y = length . takeWhile (uncurry (==)) . zip x $ y

и вы можете сразу отбросить один аргумент,

agreeLen x = length . takeWhile (uncurry (==)) . zip x

Затем вы можете переписать это как префиксное приложение оператора композиции,

agreeLen x = (.) (length . takeWhile (uncurry (==))) (zip x)

и вы можете написать

f (g x)

а

f . g $ x

вообще, здесь с

f = (.) (length . takeWhile (uncurry (==)))

и g = zip, давая

agreeLen x = ((.) (length . takeWhile (uncurry (==)))) . zip $ x

из которого легко удаляется аргумент x. Затем вы можете преобразовать префиксное приложение (.) в раздел и получить

agreeLen = ((length . takeWhile (uncurry (==))) .) . zip

Но это менее читаемо, чем оригинал, поэтому я не рекомендую это делать, кроме практики преобразования выражений в стиль без точек.

Ответ 2

Вы также можете использовать:

agreeLen :: (Eq a) => [a] -> [a] -> Int
agreeLen x y = length $ takeWhile id $ zipWith (==) x y

Idiomatic Haskell - это то, что легче читать, а не обязательно то, что больше не имеет смысла.