Как показала утилита pointfree
, можно выполнить любое такое преобразование автоматически. Однако результат чаще запутывается, чем улучшается. Если цель состоит в том, чтобы улучшить разборчивость, а не уничтожить ее, то первая цель должна состоять в том, чтобы определить , почему выражение имеет определенную структуру, найти подходящую абстракцию и построить все таким образом.
Самая простая структура - это просто объединение вещей в линейный конвейер, который представляет собой простую композицию функций. Это продвигает нас довольно далеко само по себе, но, как вы заметили, оно не справляется со всем.
Одним из обобщений являются функции с дополнительными аргументами, которые можно наращивать постепенно. Вот один пример: Определите onResult = (. (.))
. Теперь применение onResult
n раз к начальному значению id
дает вам композицию функций с результатом n-арной функции. Таким образом, мы можем определить comp2 = onResult (.)
, а затем написать comp2 not (&&)
, чтобы определить операцию NAND.
Другое обобщение, которое на самом деле охватывает вышеупомянутое, - это определение операторов, которые применяют функцию к компоненту с большим значением. Примером здесь могут быть first
и second
в Control.Arrow
, которые работают с 2-мя кортежами. Конал Эллиотта Семантический редактор комбинаторов основан на этом подходе.
Несколько иной случай, когда у вас есть функция с несколькими аргументами для некоторого типа b
и функция a -> b
, и вам нужно объединить их в функцию с несколькими аргументами, используя a
. Для общего случая 2-арных функций модуль Data.Function
предоставляет комбинатор on
, который можно использовать для написания выражений типа compare `on` fst
для сравнения 2-х кортежей по их первым элементам.
Это более сложная проблема, когда один аргумент используется более одного раза, но здесь есть значимые повторяющиеся шаблоны, которые также можно извлечь. Распространенным случаем здесь является применение нескольких функций к одному аргументу, а затем сбор результатов с помощью другой функции. Это соответствует экземпляру Applicative
для функций, который позволяет нам писать выражения типа (&&) <$> (> 3) <*> (< 9)
, чтобы проверить, попадает ли число в заданный диапазон.
Важная вещь, если вы хотите использовать все это в реальном коде, это подумать о том, что выражение означает и как это отражено в структуре. Если вы сделаете это, а затем преобразуете его в стиль pointfree, используя значимые комбинаторы, вы будете часто делать смысл кода более ясным , чем это было бы в противном случае, в отличие от типичного вывода pointfree
.