Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Split off PrimMonadInternal haskell/vector#65 #19

Merged
merged 4 commits into from
Feb 11, 2015

Conversation

snoyberg
Copy link
Contributor

@snoyberg snoyberg commented Jan 1, 2015

@cartazio
Copy link
Contributor

cartazio commented Jan 1, 2015

looks like the .travis formula needs to be updated to the modern @hvr style https://github.com/hvr/multi-ghc-travis/blob/master/.travis.yml

could you swap it in?

@snoyberg
Copy link
Contributor Author

snoyberg commented Jan 2, 2015

Swapped in new .travis.yml file, and rebased against newest commits as well.

@snoyberg snoyberg force-pushed the monad-st branch 3 times, most recently from c92cb6d to b69456a Compare January 2, 2015 07:18
@snoyberg
Copy link
Contributor Author

snoyberg commented Jan 2, 2015

OK, after some tweaking, I applied a much smaller patch and got the build to pass on Travis. I was a bit surprised to notice that the test suites are not being run by Travis, is this intentional?

@cartazio
Copy link
Contributor

cartazio commented Jan 2, 2015

no its not, add em! test all the things! :)

@snoyberg
Copy link
Contributor Author

snoyberg commented Jan 2, 2015

Ahh, I see why the tests aren't included: I'm the only person who's written any tests, and I neglected to add testing to Travis. OK, let's see if this latest changes works...

@snoyberg
Copy link
Contributor Author

snoyberg commented Jan 2, 2015

OK, tests are running and passing. Now that all of that is out of the way, I think we can discuss the actual change itself here. I'm up for any bikeshedding on the name of the new typeclass, but other than that I think our hands are tied on what can be changed, since we should leave all method names and signatures the same to avoid unnecessary breakage.

@cartazio
Copy link
Contributor

cartazio commented Jan 2, 2015

hrm, how about we add something like liftST :: PrimMonad m => ST (PrimState m) a -> m a to Control.Monad.Primitive?

I dont have a strong objection to the *Internal name, though it is kinda ugly for an exported name.

I guess I'm mildly bothered by the fact that its still called PrimMonad, even though now its just for historical/compatibility reasons, but we've already established that this style design is needed if we want to minimize breakage.

As written, i guess it looks fine, though it'd be good to have some feedback from a few more folks:
paging @hvr @ekmett @dolio and @erikd et al

@cartazio
Copy link
Contributor

cartazio commented Jan 2, 2015

oh, i'm noticing that this doesnt have the full multi-ghc travis formula btw,

edit: oh did you push that already?

@snoyberg
Copy link
Contributor Author

snoyberg commented Jan 3, 2015

I think liftST is just primToPrim, though I have no problem exposing liftST as another synonym. I agree that MonadST would be a better name, but am strongly opposed to changing the name at this point. I suppose one thing we could do is hide the entire implementation of PrimMonad, and export a ConstraintKind, e.g.:

type MonadST m = PrimMonad m

which may achieve much the same goal. (FWIW, I'm not a fan of the World associated type name; I prefer PrimState.)

@cartazio
Copy link
Contributor

cartazio commented Jan 3, 2015

i dont think we need to do

type MonadST m = PrimMonad m

we would be better served (at least while pre 7.10 support matters, because the above requires contraint kinds in the client module in pre 7.10 right?)
by

class PrimMonad m => MonadST m where
  type World m ...
instance PrimMonad m => MonadST m where
 type World m = PrimState m

or something like that, I think?
and thus MonadST, or whatever we call it, just becomes part of Primitive.

I personally favor World or the like over PrimState, because it is a world token right?

@snoyberg
Copy link
Contributor Author

snoyberg commented Jan 3, 2015

Yeah, that seems fine to me.

@cartazio
Copy link
Contributor

cartazio commented Jan 3, 2015

(though i do agree the UX of the type synonym approach IS better, so maybe that piece should be a bit more forward looking anyways?)

@dolio
Copy link
Contributor

dolio commented Jan 3, 2015

I don't think MonadST is a better name than PrimMonad, since things built on IO are included as well. That's technically true of the existing MonadST, but I actually think that makes MonadST a worse name, strictly speaking.

I'm (edit) not very opposed to including it for (fairly) seamless replacement of monad-st if Ed thinks that's sensible, though.

@cartazio
Copy link
Contributor

cartazio commented Jan 4, 2015

hrmm, you may have a good point though.

@cartazio
Copy link
Contributor

cartazio commented Jan 4, 2015

I guess i'm just thinking PrimMonad is no longer a good/accurate name and I'm fishing for better ideas :)

@ekmett
Copy link
Member

ekmett commented Jan 4, 2015

I'd rather not just start randomly recoloring the bikeshed and breaking every user for no real non-cosmetic reason.

@cartazio
Copy link
Contributor

cartazio commented Jan 4, 2015

fair enough, i was just trying to think outloud. paint coloring ideas withdrawn.

@snoyberg
Copy link
Contributor Author

snoyberg commented Jan 5, 2015

I think the only thing really open to bikeshedding then is the name of the new typeclass. Are there any objections to the current name (PrimMonadInternal) or suggestions for better ones? I don't feel strongly about the name at all, and am happy to have it changed.

Otherwise, is there anything blocking this getting merged and released?

@ekmett
Copy link
Member

ekmett commented Jan 5, 2015

I have some concerns about the interaction between nondeterminism and modules like http://hackage.haskell.org/package/vector-0.10.12.2/docs/Data-Vector-Generic-Mutable.html that I'd very much like to work through first.

@ekmett
Copy link
Member

ekmett commented Jan 5, 2015

For example a MStream m a could never violate the linearity of the result vector before, but with this change it can, so far more of the API may wind up behind PrimMonadInternal than you might think.

@ekmett
Copy link
Member

ekmett commented Jan 5, 2015

Re constraint kinded aliases, they currently require users to turn on extensions, which makes them less useful than the old school UndecidableInstance class + instance pair, but I don't think we'd even want to do that.

@ekmett
Copy link
Member

ekmett commented Jan 5, 2015

We also need to add instances for the various types in transformers. Due to transformers having a hard Haskell 98 requirement they simply can't flow the other way.

@ekmett
Copy link
Member

ekmett commented Jan 5, 2015

Also, even PriimMonadInternal is a bit stronger than most ops require, most things only need linear or affine computation, not isomorphism to ST s.

@snoyberg
Copy link
Contributor Author

snoyberg commented Jan 5, 2015

For example a MStream m a could never violate the linearity of the result vector before, but with this change it can, so far more of the API may wind up behind PrimMonadInternal than you might think.

Can you give an example of a problematic case? I think that might help solidify the discussion.

We also need to add instances for the various types in transformers.

Can you clarify what you mean by this? Which instances need to be added?

@ekmett
Copy link
Member

ekmett commented Jan 5, 2015

Can you give an example of a problematic case?

That was what I wanted to take the time to either reassure myself didn't exist, or find. ;)

That said, let me try to construct one off the cuff:

Consider a world where we have

instance PrimMonad m => PrimMonad (ListT m)

Now, lets look at

transform :: (PrimMonad m, MVector v a) => (MStream m a -> MStream m a) -> v (PrimState m) a -> m (v (PrimState m) a)
transform f v = fill v (f (mstream v))

This will refill the vector after modifying it with the MStream calculation.

Previously, MStream was effectively working in a world where 'm' must be isomorphic to ST s for some s by dint of the PrimMonad constraint, but now a larger class of effects is possible here!

Before, the fact that the 'future' of the calculation was linear, we could only ever 'fill' once, and each step we took filling it in was only going to be visited (at most) once, we could throw an exception in the modified stream, but that is it.

But now in particular with ListT IO effects we could choose to, say, give back multiple answers per entry in the vector, but we only have the one mutable vector to write all these changes into.

Things like that are I'm not in a hurry to rush in and say "oh this more generalized form of PrimMonad is definitely okay" because the entire API of vector was predicated on the more restricted notion of PrimMonad, and we're changing that underlying assumption, so I'm expecting there to be big gaping huge problems until I've had a chance to reassure myself to the contrary.

It seems obvious to me that monads that only use the future of the calculation in a linear (or affine) fashion are okay. It is the ones where we introduce a notion of continuation or non-determinism where it seems to me that we'll run into problems if we aren't careful.

We also need to add instances for the various types in transformers.

Can you clarify what you mean by this? Which instances need to be added?

It strikes me that the very point of allowing a more general type of PrimMonad would be to encompass a larger suite of effects, e.g. state, environments, writer logs, etc. So once you open up the world of PrimMonad beyond IO and ST s, someone needs to supply the instances for the most common source of such extensions. Either primitive has to depend on transformers or transformers has to depend on primitive, but transformers lives under a doctrine that keeps its code Haskell 98 / 2010, so the dependency between them can't viably be flipped the other way.

Say what you will about primitive, but Haskell 98 / 2010 it ain't.

@snoyberg
Copy link
Contributor Author

snoyberg commented Jan 7, 2015

So along these lines: @ekmett how do you feel about adding the PrimMonad instance for ExceptT in transformers-compat, instead of adding a dependency from primitive on transformers-compat?

@snoyberg
Copy link
Contributor Author

snoyberg commented Jan 7, 2015

OK, it's pushed and Travis is passing. (I rebased slightly vs the previous set of commits to put the change under discussion as the last commit, and the other housekeeping stuff around Travis lower in the stack.) I think Edward still wanted to think about linearity a bit, but so far I'm not hearing anyone complain about the name PrimMonadInternal.

@ekmett
Copy link
Member

ekmett commented Jan 7, 2015

I'm okay with tweaking transformers-compat to do the right dance to
minimize the dependencies from primitive.

On Wed, Jan 7, 2015 at 1:56 PM, Michael Snoyman notifications@github.com
wrote:

So along these lines: @ekmett https://github.com/ekmett how do you feel
about adding the PrimMonad instance for ExceptT in transformers-compat,
instead of adding a dependency from primitive on transformers-compat?


Reply to this email directly or view it on GitHub
#19 (comment).

{-# INLINE primitive #-}
instance PrimMonadInternal IO where
internal (IO p) = p
Copy link
Contributor

Choose a reason for hiding this comment

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

should this maybe be written as
internal = \(IO p)-> p
so that it has the same saturation/inlining behavior as other instances?

Copy link
Contributor

Choose a reason for hiding this comment

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

woops, nevermind, the ST instance is in the same style

@Shimuuar
Copy link

I want to bikeshed a little. PrimMonadInternal is not very good name. This type class means that m is isomorphic to ST for some s. Maybe something like StLikeMonad would be better?

Another issue I want to bring up there's no way to constrain monad at the base of stack. Most importantly to specialize it to IO. It becomes problem if most of the library could use any PrimMonad but some need to do IO. Concrete example is mwc-random. PRNG initialization requires IO and rest of library doesn't. Only solution I can think of is to add (MonadIO m, MonadPrim m) constaraint but it feels somewhat redundant.

@dolio
Copy link
Contributor

dolio commented Jan 20, 2015

For the latter, you can use PrimState m ~ RealWorld. But that's longer than MonadIO.

I suppose we could include an alias for (PrimMonad m, PrimState m ~ RealWorld) if that is actually expected to be widely useful. I'm not sure it will be, though.

@cartazio
Copy link
Contributor

i'm inclined to agree with @dolio , (that those are easy to define in userland)

@ekmett
Copy link
Member

ekmett commented Jan 21, 2015

It also has the disadvantage of not being able to be partially applied,
unless we do the usual undecidable/flexible class/instance pair.

On Tue, Jan 20, 2015 at 5:38 PM, dolio notifications@github.com wrote:

For the latter, you can use PrimState m ~ RealWorld. But that's longer
than MonadIO.

I suppose we could include an alias for (PrimMonad m, PrimState m ~
RealWorld) if that is actually expected to be widely useful. I'm not sure
it will be, though.


Reply to this email directly or view it on GitHub
#19 (comment).

@ekmett
Copy link
Member

ekmett commented Jan 21, 2015

Er.. by partially applied I mean, 'left unapplied' as a type of kind (* ->
*) -> Constraint.

On Tue, Jan 20, 2015 at 11:06 PM, Edward Kmett ekmett@gmail.com wrote:

It also has the disadvantage of not being able to be partially applied,
unless we do the usual undecidable/flexible class/instance pair.

On Tue, Jan 20, 2015 at 5:38 PM, dolio notifications@github.com wrote:

For the latter, you can use PrimState m ~ RealWorld. But that's longer
than MonadIO.

I suppose we could include an alias for (PrimMonad m, PrimState m ~
RealWorld) if that is actually expected to be widely useful. I'm not
sure it will be, though.


Reply to this email directly or view it on GitHub
#19 (comment).

@cartazio
Copy link
Contributor

@ekmett clarified what he meant on irc

[15/01/20-23:56:10]  <carter>   edwardk: i cant quite parse your gh remark on the vector thread
[15/01/20-23:56:31]  <edwardk>  don't remember my remark
[15/01/20-23:56:42]  <edwardk>  oh
[15/01/20-23:57:00]  <edwardk>  re: type MonadIO m = (PrimMonad m, PrimState m ~ RealWorld)
[15/01/20-23:57:01]  <edwardk>  ?
[15/01/20-23:57:29]  <edwardk>  class (PrimMonad m, PrimState m ~ RealWorld) => MonadIO m; instance (PrimMonad m, PrimState m ~ RealWorld) => MonadIO m
[15/01/20-23:57:38]  <edwardk>  the latter is a more robust way of saying the former
[15/01/20-23:57:50]  <edwardk>  the former requires an extension on the behalf of the user before 7.10
[15/01/20-23:58:08]  <edwardk>  but it _also_ has the problem that you can't talk about 'MonadIO :: (* -> *) -> Constraint' as a type in its own right
[15/01/20-23:58:22]  <edwardk>  its only a 'thing' when its applied to something MonadIO IO :: Constraint
[15/01/20-23:59:06]  <carter>   ohh
[15/01/20-23:59:12]  <edwardk>  in the same sense as type Flip f a b = f b a   -- is only a 'thing' when you have given it all 3 arguments. you can't write  instance Monad (Flip Either x)

which i agree with

@Shimuuar
Copy link

Yes. I agree that PrimState m ~ RealWorld is not best idea. It's also quite
cryptic. So probably (MonadPrim m, MonadIO m) is best solution. MonadIO
type synonym will also clash with MonadIO from trasformers

Any opinion on naming of PrimMonadInternal?

@cartazio
Copy link
Contributor

i dont really care what we name it, but PrimMonadBase or PrimMonadSimple are probably viable too right?

@Shimuuar
Copy link

I don't particulary care either. I just think that PrimMonadInternal is
actively misleading. Your variants are fine too. Even better probably since
they mention PrimMonad in their name

@cartazio
Copy link
Contributor

cartazio commented Feb 1, 2015

we should probably also expose a pretty version of primitive that is prettier as a derived combinator.

I think @dolio will do that a few other bits of cleanup and ship this soon (we talked through this and a few related action items at compose conf)

@snoyberg
Copy link
Contributor Author

snoyberg commented Feb 9, 2015

It looks to me like this PR has officially stalled. Anything we should do to un-stall it?

@dolio
Copy link
Contributor

dolio commented Feb 9, 2015

I'll talk to Ed tomorrow about any remaining objection he has.

Other than that, there seems to be some dislike of the name. But I don't have a better one, and neither does anyone else, I think. Does anyone like the other two names Carter suggested better than the current one? Or have something better? Otherwise I'll probably just merge it.

@Shimuuar
Copy link

Shimuuar commented Feb 9, 2015

I like Carter's MonadPrimBase much better than MonadPrimInternal. Internal usually used to describe private parts of API but this is public API. So I think PrimMonadInternal is misleading name

@dolio
Copy link
Contributor

dolio commented Feb 11, 2015

How does PrimBase strike everyone? I think it's nicely short, and it isn't a travesty that it doesn't have the word "monad" in it.

Don't bother revising the pull request. I'll just merge it and then we can rename on top of it.

@Shimuuar
Copy link

Shimuuar commented Feb 11, 2015 via email

@cartazio
Copy link
Contributor

that works for me.

@snoyberg
Copy link
Contributor Author

No objection from me.

On Wed, Feb 11, 2015, 7:50 PM Carter Tazio Schonwald <
notifications@github.com> wrote:

that works for me.


Reply to this email directly or view it on GitHub
#19 (comment).

dolio added a commit that referenced this pull request Feb 11, 2015
@dolio dolio merged commit 7617954 into haskell:master Feb 11, 2015
@dolio
Copy link
Contributor

dolio commented Feb 12, 2015

@cartazio By the way, I just realized that primToPrim is the nicer form of primitive that you wanted. It can lift any PrimBase into a stack based on that base.

It might be difficult to notice that. Perhaps we should mention that in the comment/documentation?

@cartazio
Copy link
Contributor

@dolio, YES, good point, we should probably document it! Would it be worth having some sort of liftPrim alias?

@cartazio
Copy link
Contributor

but yeah, one way or another, we should probably make primToPrim a bit more prominent in the docs

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.

None yet

6 participants