Skip to content

Conversation

@Ericson2314
Copy link
Contributor

Does the good half of #63.

I went ahead to give that idea a spin, and found a few interesting things in the process. Will comment in line notes.

deriving (Functor, Applicative, Monad, MonadIO, PrimMonad)
instance HasWriter tag (DList a) m => HasStream tag a (StreamDList m) where
yield_ _ = coerce @(a -> m ()) $ tell @tag . DList.singleton
instance HasStream tag (DList a) m => HasStream tag a (StreamDList m) where
Copy link
Contributor Author

@Ericson2314 Ericson2314 Dec 3, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's no reason to pull in all of HasWriter to discharge this.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The haddocks do not really represent what is happening here anymore, do they? In the case of HasWriter tag (DList a) it was clear that the dlists would be combined using their monoid instance, but with HasStream tag (DList a) this is no longer necessarily the case. It could be that we're using S.Stream (Of (DList a)) underneath. In that case we could end up with, say, a list of singleton dlists if we choose to run the stream that way.


-- | Accumulate streamed values with their own monoid.
newtype StreamLog m (a :: *) = StreamLog (m a)
deriving (Functor, Applicative, Monad, MonadIO, PrimMonad)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With this change, WriterLog needs an instance for HasStream, so the strategy becomes StreamLog and goes here. There is a WriterLog alias for back compat.

-- over
--
-- > deriving (HasStream tag w) via
-- > Lift (SomeTrans (StreamLog (MonadState SomeStateMonad)))
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This issue already existed: Lift (StreamDList (WriterLog (... SomeStateMonad))) was already possible, whereas StreamDList (WriterLog (... (Lift SomeStateMonad))) would be the preferred alternative. That said, with new StreamLog, less indirection is needed to create the same scenario. Given that, I decided to copy the warning from Capability.Writer.Discouraged, but not move the instance to a Capability.Stream.Discouraged as there's plenty of non-state-based ways to implement HasStream.

Copy link
Member

@aherrmann aherrmann left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks! It's taking me a while to grok this change. But, I think I'm seeing what you're getting at.

Though, I think this deserves some more documentation. I'd also love an example that makes use of this super class relationship, if you can think of something.

Here's what confused me about this. Other future users might be similarly confused.

My intuition for HasStream tag w m is that it is something that talks about individual values of type w where each yield tosses a new value down the chute, such as Stream (Of w).

On the other hand, my intuition for HasWriter tag w m is that it is something that talks about one value of type w where every tell makes the value grow bigger by its monoid instance.

These intuitions collide in HasStream tag w m => HasWriter tag w m.
Of course, an instance of HasStream tag w m that collapses all w into one is perfectly valid.
It's just not immediately obvious.

deriving (Functor, Applicative, Monad, MonadIO, PrimMonad)
instance HasWriter tag (DList a) m => HasStream tag a (StreamDList m) where
yield_ _ = coerce @(a -> m ()) $ tell @tag . DList.singleton
instance HasStream tag (DList a) m => HasStream tag a (StreamDList m) where
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The haddocks do not really represent what is happening here anymore, do they? In the case of HasWriter tag (DList a) it was clear that the dlists would be combined using their monoid instance, but with HasStream tag (DList a) this is no longer necessarily the case. It could be that we're using S.Stream (Of (DList a)) underneath. In that case we could end up with, say, a list of singleton dlists if we choose to run the stream that way.

-- prop> pass @t (tell @t w >> pure (a, f)) = tell @t (f w) >> pure a
-- prop> writer @t (a, w) = tell @t w >> pure a
class (Monoid w, Monad m)
class (Monoid w, Monad m, HasStream tag w m)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think HasStream tag w m => HasWriter tag w m deserves a good bit of additional documentation. To me this is far from obvious. (See overall review comment).

@Ericson2314
Copy link
Contributor Author

Ericson2314 commented Dec 4, 2018

Thanks @aherrmann. I'm quite willing to add all the docs you or anyone wants for these changes; I definitely agree these changes are quite surprising up front for people used to the MTL/seeing Writer as an atom of capability. You may want to look at #63 for all the discussion that this has generated so far.

Writer is a funny thing in that it almost doesn't need the Monoid constraint at all. pass, for example, could work by mapping the function on each tell argument, rather than after they've been mappended together (the laws here permits either). It's just listen which, in containing a runWriterT or it's moral equivalent), speaks to how the effect is eliminated thus forces the Monoid.

My view is sort of syntactic. yield/tell alone cannot speak to whether the garbage chute ends in a monoid trash heap or conveyor belt for further processing :). If I write code using just that one method/capability, I wish my code to be open to as many possible interpretations as possible.

@aherrmann
Copy link
Member

@Ericson2314

I'm quite willing to add all the docs you or anyone wants for these changes;

Thank you! Yes, I think this is a good addition to capability. I'll be happy to merge it with some more docs.

@Ericson2314
Copy link
Contributor Author

OK added some documentation.

@aherrmann aherrmann merged commit 2ca5d48 into tweag:master Dec 22, 2018
@aherrmann
Copy link
Member

@Ericson2314 Sorry for leaving this hanging for so long. Thanks for the added documentation! Looks good to me.

I noticed that the examples needed fixing. I added the corresponding changes.

@Ericson2314
Copy link
Contributor Author

Oh, right. Thanks for fixing those!

@Ericson2314 Ericson2314 deleted the stream-writer branch December 22, 2018 16:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants