Трансформаторы в подписи типа или нет?

#api #haskell #monad-transformers

#API #haskell #монады-трансформеры

Вопрос:

Просто думаю о дизайне API. Что является «общим» в Haskell? Трансформаторы в подписи типа или, скорее, «скрытые»?

 findById :: ID -> IO (Maybe User)
findById x = runMaybeT $ do
  ...
  return User
  

или

 findById :: ID -> MaybeT IO User
findById x = do
  ...
  return User
  

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

1. Третья альтернатива для рассмотрения: одна из (MonadIO m, Alternative m) => ID -> m User , если действительно нет интересной информации об ошибках, которую вы можете сообщить, или (MonadIO m, MonadError MyFancyError m) => ID -> m User если есть.

Ответ №1:

Если это для чего-то простого, и это всего лишь несколько функций, которые делают это, возможно, в IO, я бы просто создал тип IO (Maybe User) .

Если это шаблон, который распространяется по всей вашей библиотеке, я бы дал полуабстрактное имя монаде tfm-stack:

 type Request = MaybeT IO

findById :: ID -> Request User
  

… или даже

 {-# LANGUAGE GeneralizedNewtypeDeriving #-}
newtype Request a = Request (runRequest :: MaybeT IO a)
   deriving (Functor, Applicative, Monad)
  

Создание подписи ID -> MaybeT IO User не очень хорошо: трансформатор помогает только в том случае, если вы выполняете целую кучу действий в этой монаде, но в этом случае всегда запись MaybeT IO нарушает принцип DRY.

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

1. ради интереса, findById будет ли тогда выглядеть findById :: ID -> Request User n findById 1 = return User n findById _ = Request . MaybeT $ return Nothing правильно?

2. Да. Если вы также производите Alternative , вы можете упростить случай сбоя до findById _ = empty .