>sum' xs = foldl (+) 0 xs
Образец >xs
представлен дважды с правой стороны. Из–за каррирования мы можем пропустить образец >xs
с обеих сторон, так как >foldl (+) 0
создаёт функцию, которая принимает на вход список. Если мы запишем эту функцию как >sum' = foldl (+) 0
, такая запись будет называться бесточечной. А как записать следующее выражение в бесточечном стиле?
>fn x = ceiling (negate (tan (cos (max 50 x))))
Мы не можем просто избавиться от образца >x
с обеих правых сторон выражения. Образец >x
в теле функции заключён в скобки. Выражение >cos (max 50)
не будет иметь никакого смысла. Вы не можете взять косинус от функции! Всё, что мы можем сделать, – это выразить функцию >fn
в виде композиции функций.
>fn = ceiling . negate . tan . cos . max 50
Отлично! Во многих случаях бесточечная запись легче читается и более лаконична; она заставляет думать о функциях, о том, как их соединение порождает результат, а не о данных и способе их передачи. Можно взять простые функции и использовать композицию как «клей» для создания более сложных. Однако во многих случаях написание функций в бесточечном стиле может делать код менее «читабельным», особенно если функция слишком сложна. Вот почему я не рекомендую создавать длинные цепочки функций, хотя меня частенько обвиняли в пристрастии к композиции. Предпочитаемый стиль – использование выражения >let
для присвоения меток промежуточным результатам или разбиение проблемы на подпроблемы и их совмещение таким образом, чтобы функции имели смысл для того, кто будет их читать, а не представляли собой огромную цепочку композиций.
Ранее в этой главе мы решали задачу, в которой требовалось найти сумму всех нечётных квадратов меньших 10 000. Вот как будет выглядеть решение, если мы поместим его в функцию:
>oddSquareSum :: Integer
>oddSquareSum = sum (takeWhile (<10000) (filter odd (map ( 2) [1..])))
Со знанием композиции функций этот код можно переписать так:
>oddSquareSum :: Integer
>oddSquareSum = sum . takeWhile (<10000) . filter odd $ map ( 2) [1..]
Всё это на первый взгляд может показаться странным, но вы быстро привыкнете. В подобных записях меньше визуального «шума», поскольку мы убрали все скобки. При чтении такого кода можно сразу сказать, что >filter odd
применяется к результату >map ( 2) [1..]
, что затем применяется >takeWhile (<10000)
, а функция >sum
суммирует всё, что получилось в результате.