Почему преобразователь является первым аргументом "запускаемых" функций?

Я имею в виду, почему это не приходит последним?

Из-за этого соглашения для оценки стека трансформаторов нужно написать такую ​​неловкую вещь:

runStateT (runReaderT (runRWST stack r s) r') s'

вместо:

runStateT s' $ runReaderT r' $ runRWST r s $ stack

И сочетая это с немедленным do становится все более неловко

let 
  action = do
    liftIO $ putStrLn "Do"
    liftIO $ putStrLn "something"
  in runStateT (runReaderT (runRWST action r s) r') s'

вместо:

runStateT s' $ runReaderT r' $ runRWST r s $ do
  liftIO $ putStrLn "Do"
  liftIO $ putStrLn "something"

Есть ли у этого мотивация или это просто неудачное соглашение?

Кстати, я понимаю, что текущее соглашение облегчает реализацию функции "run" с использованием синтаксиса записи, но это не может быть аргументом, поскольку библиотеки должны предпочесть удобство использования легкости реализации.

3 ответа

В записи HaskellWiki для порядка параметров и в этом вопросе о переполнении стека есть две рекомендации для порядка параметров:

По моему опыту с монадой Reader/State, одни и те же вычисления выполняются в разных средах / состояниях чаще, чем разные монадические вычисления в одной среде / состоянии. Текущий порядок параметров удобен для этого.

Другая причина: монадические значения Reader/State ведут себя очень похоже на функции, которые принимают параметры среды / начального состояния в качестве параметров. А в Haskell функции идут перед параметрами.

Чтобы избежать необходимости вложенных ReaderT/StateT/RWST трансформаторы, вы можете работать с одним RWST трансформатор, несущий глобальное состояние / окружающую среду, и использование zoom а также magnify из библиотеки линз для адаптации вычислений, которые работают в более ограниченных средах.

Я уверен, что есть элемент целесообразности. Это очень легко определить

newtype StateT = StateT { runStateT :: s -> m (a, s) }

и покончим с этим.

Я хотел бы добавить последовательность как ответ на соединение. Это все домыслы, так что возьмите это с крошкой соли:

Первоначальные реализации использовали синтаксис записи нового типа для записи этих определений данных, поэтому данные запускаются первыми при их запуске, не обращая внимания на то, является ли это предпочтительным по сравнению с данными, поступающими в качестве последнего аргумента. Более новые типы данных просто следуют этому соглашению.

Другие вопросы по тегам