Допустим, нам надо произвести некое относительно простую "агрегирующую" операцию над списком. С одной стороны, специально для этого есть функции fold*. С другой стороны — можно просто реализовать рекурсивную функцию. Но как лучше поступить? Есть ли какие-либо подводные камни? Не считается ли моветоном неиспользование fold* там, где его логично использовать?
Чтобы не быть голословным — вот пример. Пусть у нас стоит такая задача (взята из
викикнижки по хаскелю):
Implement a function sequenceIO :: [IO a] -> IO [a]. Given a list of actions, this function runs each of the actions in order and returns all their results as a list.
Авторы предлагают такое решение — без fold
sequenceIO :: [IO a] -> IO [a]
sequenceIO [] = return []
sequenceIO (a:as) = do {v <-a ; vs <- (sequenceIO as) ; return (v : vs)}
То же самое можно сделать, допустим с foldr (т.к., насколько я понимаю, здесь возможны ленивые вычисления, поправьте, если это не так)
sequenceIO :: [IO a] -> IO [a]
sequenceIO = foldr (\io acc -> do {x<-io; xs<-acc; return (x:xs) }) (return [])
Строчек (и букв) меньше (в полтора раза
). И вроде чуть более читабельно.
Вот мои мысли. Есть такой паттерн — "агрегирующая функция" (не знаю, как правильно назвать). И fold* — "хелперы" для этого паттерна. Они, может, не всегда полезны для самого разработчика, но код читать помогают (собственно, это ко многим подобным функциям относится: scan*, map и т.п.).
PS: Или мне просто пора немного отдохнуть?