Правильный рефакторинг кода на Haskell, когда немонадное значение становится монадическим значением - PullRequest
0 голосов
/ 22 декабря 2019

Допустим, у меня есть этот пример кода:

h = 2
add = \x y -> (x + y)
addH = add h

main = return (fmap addH [1,2])

Запуск его оценивается как [3,4]


Теперь, скажем, h не установлен в "2", но на «Просто 2».

Вопрос, часть 1:

Что же тогда является правильным рефакторингом, чтобы все же вернуть [3,4] в конце?

Вопрос, часть 2:

Предпочитает ли опытный разработчик на Haskell изменить возвращаемое значение на [Just 3, Just 4]

Например, с таким рефактором:

h = Just 2
add = \x y -> (x + y)
addH = liftM2(add) h . pure         --isn't there a better syntax for that?

main = return (fmap addH [1,2])

В общем, в существующей кодовой базе, как минимизировать влияние рефактора, когда функция, которая раньше возвращала 'Num t => [t]'теперь должен вернуть 'Num t => [Может быть, t]'?

Ответы [ 2 ]

1 голос
/ 27 декабря 2019

Я хотел бы отметить, что в вашем исходном коде [] и Maybe являются избыточными, и что более тщательное изучение значения этих двух функторов приводит к интересному обсуждению. [] (список) представляет успех или неудачу в том же смысле, что и Maybe, главное отличие в том, что [] может представлять 0 или более значений, тогда как Maybe ограничено значением 0 или 1.

Это приводит к другому рассуждению о значении Just в Just 2. Что означает, что 2 будет заключено в Just, и что будет означать Nothing? Чтобы продвинуться на один шаг вперед, вот пять различных результатов, которые вы можете выбрать, каждый из которых имеет различное значение:

  1. Just [3, 4]: было одно монолитное вычисление, которое работало с несколькими значениями, и оноудалось.
  2. Nothing: могло быть монолитное значение, но его нет.
  3. [3, 4]: был пакет вычислений, и это значения, которые были получены.
  4. []: могло быть несколько значений, но их нет.
  5. [Just 3, Just 4]: был пакет вычислений, где каждое вычисление могло завершиться неудачей, и всеони успешно выполнены.

Случай 1 : если h представляет монолитное значение и является уникальной точкой отказа в этой цепочке вычислений, то варианты 1 и 2 выглядят какразумный выбор. Следующий код выполняет это:

import Control.Applicative (LiftA2)
h = Just 2
add = (+)
addH = LiftA2 add h . pure
-- Returns Just [3,4] or Nothing
mainFunc = traverse addH [1,2]

Случай 2 : если add представляет функцию, которая может завершиться ошибкой в ​​зависимости от ее аргумента (например, представьте функцию hDivBy,что не сработает при каждом 0), тогда варианты 3 и 4 кажутся хорошим выбором.

import Data.Maybe (mapMaybe)
hDivBy = \x -> (`div` x) <$> h
--Returns [1]
mainFunc = mapMaybe hDivBy [0, 2]

Случай 3 : Если вы хотите отслеживать индексацию элементов, то вариант 5 даст вам следующее:

--Returns [Nothing, Just 2]
mainFunc = fmap hDivBy [0,2]

Обратите внимание, что этот кодсохраняет все в Applicative царстве, делая его более общим, среди других преимуществ .

0 голосов
/ 22 декабря 2019

Чтобы получить Федор Сойкин результат, вы можете попробовать следующий.

h = Just 2
add = \x y -> x + y
addH arr = pure fmap <*> (add <$> h) <*> pure arr

main = return $ addH [1, 2]
...