Что означает подчеркивание в конце функции в Haskell?

#haskell

#haskell

Вопрос:

Я заметил, что некоторые имена функций заканчиваются на _ in Haskell (например mapM_ , traverse_ ).

Что это значит?

Комментарии:

1. Вы имеете в виду, например traverse_ , mapM_ , и т.д.?

2. Это кратко упоминается в разделе «соглашения об именах» здесь: hackage.haskell.org/package/base-4.12.0.0/docs/… По сути, это используется для обозначения того, что «результат» монадического значения / действия игнорируется, а действие используется исключительно из-за его «побочных эффектов». (Кавычки, потому что это строго верно только для ввода-вывода, где, я думаю, различие легче понять — для других, «чистых» монад / аппликативов, это скорее аналогия.)

3. @RobinZigmond Я бы назвал это не столько аналогией, сколько обобщением. Это означает «за его прикладные эффекты», где для ИО этот эффект часто называют побочным эффектом.

Ответ №1:

Это просто соглашение об именовании в Прелюдии. Как правило, функции без подчеркивания (например traverse , sequence , mapM ) возвращают некоторое значимое «совокупное» значение, тогда как аналоги с подчеркиванием (например traverse_ , sequence_ , mapM_ ) возвращают () . Обычно вы используете первый набор функций, но если вас не волнует возвращаемое значение, то вы бы использовали второй набор функций. Причина этого в том, что эти функции обычно являются монадическими; если вы используете функцию, возвращаемую () в монадическом контексте, но игнорируете возвращаемое значение, GHC не выдает предупреждения (поскольку оно всегда будет возвращаться () ), но GHC предупреждает вас, если функция выдает значимое возвращаемое значение, которое вы игнорируете. Так что, например do { sequence [print 1,print 2,print 3]; putStrLn "ignoring prev value" } , выдаст предупреждение, но do { sequence_ [print 1,print 2,print 3]; putStrLn "ignoring prev value" } не выдаст.

Комментарии:

1. Возврат () также иногда «проще». traverse_ , например, требуется только Foldable коллекция, потому что коллекцию не нужно перестраивать в новом контексте, даже если traverse требуется Traversable .

2. Это также может оказать существенное влияние на производительность. traverse должен удерживать фрагменты, необходимые для построения результата, в то время traverse_ как может просто выбросить их по ходу дела.