问题
Consider the State
type - or at least a simplified version:
newtype State s a = State { runState :: s -> (a, s) }
Now, let's say we want to derive the StateT
monad transformer. transformers
defines it as follows:
newtype StateT s m a = StateT { runStateT :: s -> m (a, s) }
Here, the m
has been placed on the right of the function arrow, but outside the tuple. However, if we didn't know the correct answer, we might instead put m
somewhere else:
newtype StateT s m a = StateT { runStateT :: m (s -> ( a, s)) }
newtype StateT s m a = StateT { runStateT :: s -> (m a, s) }
Obviously the version in transformers
is correct, but why? More generally, how does one know where to put the type variable for the 'inner' monad when defining a monad transformer? Generalising even more, is there a similar rule for comonad transformers?
回答1:
I think the difference can be easily understood when m ~ IO
:
s -> IO (a, s)
is the type of an action which can read the current state s
, perform IO depending on that (e.g. printing the current state, reading a line from the user), and then produce both the new state s
, and a return value a
.
Instead:
IO (s -> (a, s))
is the type of an action which immediately performs IO, without knowing the current state. After all the IO is over, it returns a pure function mapping the old state into a new state and a return value.
This is similar to the previous type, since the new state and return value can depend both on the previous state and the IO. However, the IO can not depend on the current state: e.g., printing the current state is disallowed.
Instead,
s -> (IO a, s)
is the type of an action which reads the current state s
, and then performs IO depending on that (e.g. printing the current state, reading a line from the user), and then produce a return value a
. Depdnding on the current state, bot not on the IO, a new state is produced. This type is effectively isomorphic to a pair of functions (s -> IO a, s -> s)
.
Here, the IO can read a line from the user, and produce a return value a
depending on that, but the new state can not depend on that line.
Since the first variant is more general, we want that as our state transformer.
I don't think there's a "general rule" for deciding where to put m
: it depends on what we want to achieve.
来源:https://stackoverflow.com/questions/49587122/type-variable-location-in-transformers