The crucial detail is that TardisT must wrap a MonadFix, which is used for cyclical data structures. It effectively works not by making time go "backwards", but by making time loop around. It still creates a sequencing relationship.
The crucial detail is that TardisT must wrap a MonadFix, which is used for cyclical data structures. It effectively works not by making time go "backwards", but by making time loop around. It still creates a sequencing relationship.
So? The non-transformer version can still "read data before it's written". It's very hard to see any form of sequencing in that example.
> It still creates a sequencing relationship.
A sequencing relationship between what exactly?
(>>=)
:: Monad m
=> m a -- Left argument
-> (a -> m b) -- Right argument
-> m b
You don’t need to explain an individual monad instance to notice that this signature requires “a” in order to generate “m b”.If you are convinced that the Identity monad does "force sequencing" then you are also convinced that lazy evaluation "forces sequencing" (because the Identity monad is just lazy evaluation). That would be a fairly controversion conviction. If you are not convinced that the Identity monad "forces sequencing" then can you explain in more detail the meaning of your claim "What a Monad does is to force sequencing by creating a data dependency.".
The sequencing is very obvious from the signature of `>>=`. You don't need to know anything about any instances to see that it's there.
You most certainly do not! See https://news.ycombinator.com/item?id=16420742
The operational semantics and details of thunks are not relevant here. Tricks with `undefined` are not relevant here. This is just the meaning of the arrow type.
Although your example is exactly what's happening in the identity monad, because there is no extra stuff.
Let's define an operator `|> = (flip $)`, which has a type signature `a -> (a -> b) -> b`, which, by your reasoning, should force sequencing too. Except it doesn't: Laziness is a bitch to work with, and `a |> f |> g` evaluates to `g(f(a))`, and g is evaluated first, which then evaluates f(a) on demand. If you consider `1 |> (+1) |> const 4`, this will evaluate to 4 without ever performing the addition.
Instead, let's compare this to the monadic equivalent. `(return 1) >>= return . (+ 1) >>= return . (const 4)`. The sequence that's being forced here isn't that (+ 1) is evaluated, it's that the `return` in `return 1` is evaluated (producing a 1) before `return . (+ 1)` is ever touched, and that _that_ return is evaluated (presumably producing an unevaluated thunk for (1 + 1)) before `return . (const 4)` is touched.
In IO world, this corresponds to guaranteeing that the IO operations happen in sequence irrespective of the order in which the pure computations they wrap are evaluated in.
That's not quite right either. The former returns need never be evaluated.
> undefined >>= (return . (const 4)) :: Identity Int
Identity 4Neither does monad force a sequencing relationship nor is it the only thing than can force a sequencing relationship so I can only conclude that "What a Monad does is to force sequencing by creating a data dependency" is indeed one of those misconceptions!
Aha! Now we're on to something.
There's no "time loop" in a cyclical data structure. There's just a data loop. Moving backward through a circular buffer, or doing arithmetic mod N, isn't "time travel"