Does it really? Maybe this is another one of those famous misconceptions!
https://hackage.haskell.org/package/tardis-0.4.1.0/docs/Cont...
Does it really? Maybe this is another one of those famous misconceptions!
https://hackage.haskell.org/package/tardis-0.4.1.0/docs/Cont...
What you may be misunderstanding is that "sequencing expressions" does not mean "execute one expression after the other". It means, take a sequence of expressions and give it some meaning. Yes, that meaning can be "execute one after the other", or "run everything at the same time", or any other ordering. The meaning can ignore any ordering and only care about some context, or apply some order over the result in an ordered structure, or almost anything else.
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"
Have you used TardisT? In order to use the backwards traveling state, you need to bind the state syntactically before you use it, just as you have to do with the forwards traveling state.
For example, a typical setup for TardisT is fixing up jump offsets for an assembler. To write this, you do something like
assembler (Label l) = do
fixup <- getFuture
(curOps, offset) <- getPast
modifyBackwards (M.insert l offset)
let ops = curOps ++ [ Jump (M.lookup l fixup) ]
sendFuture (ops, offset + 1)
assembler ... = ...
Note that both the forwards and backwards traveling state needed to be bound before they were used. There is an inherent 'forwards-traveling' sequencing due to the monadic structure.If tardist were not monadically sequenced, you ought to be able to do
assembler (Label l) = do
(curOps, offset) <- getPast
let ops = curOps ++ [ Jump (M.lookup l fixup) ]
sendFuture (ops, offset + 1)
fixup <- getFuture
modifyBackwards (M.insert l offset)
In this sense, the forwards traveling state is privileged (and indeed if you write anything sufficiently complicated, you'll note that you'll need to add a lot of lazy pattern matches on the backwards traveling state, but not the forwards traveling one, again indicating a notion of sequencing, despite what it may seem).