В общем случае разные порядки преобразователей монад приведут к разному поведению, но, как было указано в комментариях, для двух порядков «состояние» и «читатель» мы имеем следующие изоморфизмы вплоть до новых типов:
StateT MyState (Reader Env) a ~ MyState -> Env -> (a, MyState)
ReaderT Env (State MyState) a ~ Env -> MyState -> (a, MyState)
таким образом, единственная разница заключается в порядке аргументов, и в остальном эти две монады семантически эквивалентны.
Что касается производительности, трудно сказать наверняка, не сравнив фактический код. Однако в качестве одной точки данных, если вы рассматриваете следующее монадическое действие:
foo :: StateT Double (Reader Int) Int
foo = do
n <- ask
modify (* fromIntegral n)
gets floor
затем при компиляции с GHC 8.6.4 с использованием -O2
новые типы, очевидно, оптимизируются, и это генерирует точно то же ядро, если вы измените подпись на:
foo :: ReaderT Int (State Double) Int
за исключением того, что два аргумента foo
меняются местами. Таким образом, нет никакой разницы в производительности, по крайней мере, в этом простом примере.
Стилистически вы можете столкнуться с ситуациями, когда один порядок приводит к более красивому коду, чем другой, но обычно между ними не будет особого выбора. В частности, основные монадические действия, подобные приведенному выше, будут выглядеть одинаково при любом порядке.
Без веской причины я предпочитаю № 2, в основном потому, что Env -> MyState -> (a, MyState)
выглядит для меня более естественным.
person
K. A. Buhr
schedule
03.06.2019
MyMonad1 a
совпадает сMyState -> Env -> (a, MyState)
, аMyMonad2 a
— сEnv -> MyState -> (a, MyState)
, поэтому единственная разница — это порядок аргументов. - person Robin Zigmond   schedule 02.06.2019ReaderT
иStateT
или вообще о том, как выбрать порядок трансформаторов в стеке? - person Daniel Wagner   schedule 02.06.2019