3 ms·
In some sense yes. If I see that a function has type `Teletype ()`, I can be certain that it is composed of solely teletype actions. Compare to `IO ()` where an
by bweitzman 11y ago
In some sense yes. If I see that a function has type `Teletype ()`, I can be certain that it is composed of solely teletype actions. Compare to `IO ()` where anything can happen. Of course you still have that issue when implementing `run :: Teletype a -> IO a`, but at least it's localized to one spot.
I think there is sort of a hole in the logic about testing though, since the article talks about testing `runPure` not `run` which is almost useless unless `run` is implemented in terms of `runPure` which introduces its own can of worms because you have to consider how things like laziness, streaming, etc. come into play in the pure version, which is a step in the wrong direction IMO.
What the article says about reasoning is important, but I feel like it's misleading to say that it comes from the using the free monad. It's really about the Functor instance. The fact that `ExitSuccess >> m = ExitSuccess` is a result of the Functor instance for TeletypeF not using (and not being able to use) the `f` parameter in the `fmap f ExitSuccess` definition.
If you don't want to use a free monad, you could also use a type class / monad transformer like `MonadTeletype` which defines the actions of the teletype can perform. Then you write all your teletype functions with a signature like `MonadTeletype m => .... -> m a`. You still get the guarantee that no arbitrary actions are there but you don't have the complexity of the free monads which can be a little daunting for newer Haskellers.