Почему эта функция?Если поведение этой функции так четко разделяется на две части, тогда должно быть двумя функциями.То есть вы написали одну монолитную функцию и пытаетесь определить более простую функцию как утилиту, используя ее.Вместо этого напишите простую функцию и напишите монолитную функцию как ее композицию с другой.Тип в значительной степени просит его: Either a b -> c
изоморфен (a -> c, b -> c)
.
-- you may need to factor out some common utility stuff, too
logError :: (MonadIO m) :: MyError -> m ()
logResponse :: (MonadIO m, ToJSON a) => Response a -> m ()
logResult :: (MonadIO m, ToJSON a) => Either MyError (Response a) -> m ()
logResult = either logError logResponse
logResult
все еще имеет свое применение;если вы получите Either MyError (Response a)
из какой-то библиотеки, то logResult
сможет справиться с этим без особых хлопот.Но, в противном случае, вы не должны писать logResult (Left _)
или logResult (Right _)
очень часто;в сущности, logResult . Left
и logResult . Right
рассматриваются как их собственные функции, что возвращает вас к фактическому написанию их как отдельных функций.
Но в logError
я явно передаю Left MyError
.Разве это не должно быть неоднозначным?
Нет, не должно.Конец и начало проблемы в том, что logResult
выглядит следующим образом:
logResult :: (MonadIO m, ToJSON a) => Either MyError (Response a) -> m ()
Когда вы вызываете его, реализация не имеет значения, один щелчок.Тип говорит, что вам нужно ToJSON a
- вам нужно предоставить ToJSON a
.Вот и все.Если вы знаете, что вам не нужны значения ToJSON a
для значений Left
, то вы обладаете полезной информацией, которая не отражена в типе.Вы должны добавить эту информацию к типу, что в данном случае означает разделение ее на две части.Было бы (IMO) на самом деле плохой языковой дизайн, чтобы позволить то, о чем вы думали, потому что проблема остановки должна сделать невозможным правильное выполнение.