haskell-opengl / OpenGL

Haskell bindings to OpenGL
http://www.haskell.org/haskellwiki/OpenGL
BSD 3-Clause "New" or "Revised" License
147 stars 26 forks source link

Use the `StateVar` package? #61

Closed ekmett closed 9 years ago

ekmett commented 9 years ago

We're currently actively working on writing better SDL 2 bindings in sdl2.

For various reasons, it can't depend on the OpenGL package directly, but it needs a state variable construction.

Sadly, not every platform with SDL 2 has OpenGL -- thanks Microsoft.

We'd like to depend on the StateVar package, but then we'll get two versions of the notion of a StateVar that conflict.

By far the cleanest option for us moving forward would be if OpenGL switched to using the external StateVar package that you also maintain, then we could incur a dependency on that.

I realize that when this was last proposed there was some pushback from the Haskell Platform, but otherwise what we're going to start seeing is a profusion of almost-compatible APIs, which is the very thing that the Haskell Platform is meant to prevent.

ocharles commented 9 years ago

I hope that the presentation of another client in a very close area (lets be honest, most people will be using SDL2 with OpenGL) is ample motivation this time around. This is a very real world scenario :)

svenpanne commented 9 years ago

Can we bring this up again on the libraries mailing list again? The last time (https://mail.haskell.org/pipermail/libraries/2014-October/023976.html) there was no real outcome, and I have another request for this, see haskell-openal/ALUT#1. Note that we need to split off 3 packages: StateVar/ObjectName/Tensor. Although I'm not sure if this would be a good idea (the 3 packages have already been downloaded a few thousand times), I would be open to some bikeshedding discussions regarding the package names if this makes other people more happy.

Splitting this off again is really the right thing, and given the current relatively dormant state of the Haskell Platform, I'm not sure if previous arguments against that really still hold... :unamused:

ekmett commented 9 years ago

Honestly the thing that would keep me from using the StateVar machinery out of the box is the lack of lifting of the operations into MonadIO.

This makes everything incredibly verbose and noisy looking whenever you have local state:

lift $ foo $= bar

vs

foo $= bar

A large part of my mission is to sell people on the fact that Haskell offers them better tools to do their jobs, and frankly, I find the existing API very hard to sell.

ocharles commented 9 years ago

@ekmett did you find anything compelling in quine?

ekmett commented 9 years ago

https://github.com/ekmett/quine/blob/cdaa8216e80e7c501f91fd67b33331c27e97ddac/src/Quine/StateVar.hs is the version I'm using now.

Notably, I make pretty good use out of the Contravariant nature of SettableStateVar's, and found it necessary to separate out updates from mere getting/setting, because otherwise you can't deal properly with atomic updates or transactional variables.

However, i switched to an MPTC based approach, because it let me work better with things like profunctors, IORef's, Ptr's, etc.

For the most part this lets everything "just work".

svenpanne commented 9 years ago

Well, my StateVar module is much more inflexible than the one from quine, caused by my initial design constraint to work within H98, but I don't think that is very important nowadays. So what about making quine's module a separate package, perhaps even under the already existing StateVar package name? It looks like a more powerful drop-in replacement, so people shouldn't have too much trouble with that. (I haven't looked too closely, though.) This way, OpenGL, GLUT, ALUT, quine, etc. can use a common package, which would be great.

A few more points:

ekmett commented 9 years ago

Is your foreign-var package a precursor of quine's StateVar module?

So what about making quine's module a separate package,

foreign-var is actually a successor to quine's StateVar module. I converted quine to it last night. The #haskell-game folks asked me if I'd split it out.

We bikeshedded the names a bit to avoid conflict with StateVar, and to avoid the drama the name caused last time it was proposed. I'm not terribly attached to the shorter names, however, and we could move it back to the StateVar module and just have it take over as a new version of StateVar.

svenpanne commented 9 years ago

OK, making foreign-var the new StateVar sounds like a good plan. I think I'll have some free cycles tomorrow to see how this works out with my packages/examples. If everything looks OK, I think this will be the right way. Regarding the actual name: Whatever one chooses, there will be some bikeshedding, so creating a "fait accompli" will be the only way to proceed IMHO. The package has been downloaded a few thousand times already, and creating yet another name for it, even if it might be slightly better, will only create confusion and make things worse.

Would this be OK for you?

ekmett commented 9 years ago

I'm okay with that. I'll rename the package and module on my local github account, and we can see if we like the result.

ekmett commented 9 years ago

There may be some upgrade weirdness because I inverted the contravariant dependency, making contravariant depend on foreign-var rather than vice versa, so I'll need to work out a plan that doesn't violate the PVP and my own versioning policy.

ekmett commented 9 years ago

I've created a foreign-var branch on the StateVar repository which provides a StateVar 1.1 with the API improvements of foreign-var fully integrated, but with the names switched back to StateVar names.

Once (and assuming) you are ready to bless that, I'll ship an updated contravariant package (major version bump) with direct StateVar 1.1 support.

svenpanne commented 9 years ago

I've tested things locally by making OpenGL's internal StateVar module just re-export Data.StateVar, and everything went smoothly. I made a few tiny fixes to your foreign-var branch and merged it to master, but now Travis CI is a bit unhappy for GHC 7.0.4 and 7.2.2:

https://travis-ci.org/haskell-opengl/StateVar/jobs/53631173 https://travis-ci.org/haskell-opengl/StateVar/jobs/53631174

Can we fix that via some CPP magic? Not being able to support those GHC versions would be a pity, and I'm not even sure if Hackage would be happy with the package (at least there's a warning at the end of the logs about that).

svenpanne commented 9 years ago

OK, I've added a quick fix for older GHCs, see haskell-opengl/StateVar@51e67f5e4ba3e65081b0d5b4f6b98cca7363253f and haskell-opengl/StateVar@ece2ad6f50b86f6678c09ac9736bd155fe535470. If you give me the green light, I can release StateVar 1.1...

ekmett commented 9 years ago

Sure. We can CPP around the DefaultSignatures and then just not use them internally in the library.

I'll do that when I get a chance today.

-Edward

On Mon, Mar 9, 2015 at 6:58 AM, Sven Panne notifications@github.com wrote:

I've tested things locally by making OpenGL's internal StateVar module just re-export Data.StateVar, and everything went smoothly. I made a few tiny fixes to your foreign-var branch and merged it to master, but now Travis CI is a bit unhappy for GHC 7.0.4 and 7.2.2:

https://travis-ci.org/haskell-opengl/StateVar/jobs/53631173 https://travis-ci.org/haskell-opengl/StateVar/jobs/53631174

Can we fix that via some CPP magic? Not being able to support those GHC versions would be a pity, and I'm not even sure if Hackage would be happy with the package (at least there's a warning at the end of the logs about that).

— Reply to this email directly or view it on GitHub https://github.com/haskell-opengl/OpenGL/issues/61#issuecomment-77833756 .

ekmett commented 9 years ago

Oh you already did it.

On Mon, Mar 9, 2015 at 9:48 AM, Edward Kmett ekmett@gmail.com wrote:

Sure. We can CPP around the DefaultSignatures and then just not use them internally in the library.

I'll do that when I get a chance today.

-Edward

On Mon, Mar 9, 2015 at 6:58 AM, Sven Panne notifications@github.com wrote:

I've tested things locally by making OpenGL's internal StateVar module just re-export Data.StateVar, and everything went smoothly. I made a few tiny fixes to your foreign-var branch and merged it to master, but now Travis CI is a bit unhappy for GHC 7.0.4 and 7.2.2:

https://travis-ci.org/haskell-opengl/StateVar/jobs/53631173 https://travis-ci.org/haskell-opengl/StateVar/jobs/53631174

Can we fix that via some CPP magic? Not being able to support those GHC versions would be a pity, and I'm not even sure if Hackage would be happy with the package (at least there's a warning at the end of the logs about that).

— Reply to this email directly or view it on GitHub https://github.com/haskell-opengl/OpenGL/issues/61#issuecomment-77833756 .

ekmett commented 9 years ago

Looks good to me. Pull the trigger whenever you are ready and I'll make a new release of contravariant.

On Mon, Mar 9, 2015 at 9:49 AM, Edward Kmett ekmett@gmail.com wrote:

Oh you already did it.

On Mon, Mar 9, 2015 at 9:48 AM, Edward Kmett ekmett@gmail.com wrote:

Sure. We can CPP around the DefaultSignatures and then just not use them internally in the library.

I'll do that when I get a chance today.

-Edward

On Mon, Mar 9, 2015 at 6:58 AM, Sven Panne notifications@github.com wrote:

I've tested things locally by making OpenGL's internal StateVar module just re-export Data.StateVar, and everything went smoothly. I made a few tiny fixes to your foreign-var branch and merged it to master, but now Travis CI is a bit unhappy for GHC 7.0.4 and 7.2.2:

https://travis-ci.org/haskell-opengl/StateVar/jobs/53631173 https://travis-ci.org/haskell-opengl/StateVar/jobs/53631174

Can we fix that via some CPP magic? Not being able to support those GHC versions would be a pity, and I'm not even sure if Hackage would be happy with the package (at least there's a warning at the end of the logs about that).

— Reply to this email directly or view it on GitHub https://github.com/haskell-opengl/OpenGL/issues/61#issuecomment-77833756 .

svenpanne commented 9 years ago

Done: http://hackage.haskell.org/package/StateVar

ekmett commented 9 years ago

contravariant is now shipped as well.

Any eta for a new OpenGL?

svenpanne commented 9 years ago

Soon. :wink: Either in a few hours or tomorrow. I'll splitt off ObjectName, too, and I have to update the other packages, too. I'll have to think about using the vector package, too, but that would be for another release.

Another point: Would it make sense to use MondaIO instead of plain IO in all my packages? AFAICT, this should be basically a drop-in replacement, too. For OpenGLRaw it's easy, I just have to change the generator, for the rest it's just a bit of manual work.

ekmett commented 9 years ago

I would be a huge :+1: on moving everything into MonadIO.

On Mon, Mar 9, 2015 at 2:19 PM, Sven Panne notifications@github.com wrote:

Soon. [image: :wink:] Either in a few hours or tomorrow. I'll splitt off ObjectName, too, and I have to update the other packages, too. I'll have to think about using the vector package, too, but that would be for another release.

Another point: Would it make sense to use MondaIO instead of plain IO in all my packages? AFAICT, this should be basically a drop-in replacement, too. For OpenGLRaw it's easy, I just have to change the generator, for the rest it's just a bit of manual work.

— Reply to this email directly or view it on GitHub https://github.com/haskell-opengl/OpenGL/issues/61#issuecomment-77911655 .

svenpanne commented 9 years ago

OK, I'll do the move, but that will delay the release a bit of course. Let's see how many free hacking cycles I have...

bananu7 commented 9 years ago

So many liftIO to remove. :+1:

svenpanne commented 9 years ago

[ Short version: I need some help/comments regarding bracket and callbacks. ]

FYI: I'm using the separate StateVar and ObjectName packages in all my OpenGL/OpenAL packages now, this was easy. Furthermore, I've switched almost everything to MonadIO instead of plain IO, but I have a conceptual problem now: What shall I do with functions which internally use bracket and friends? (e.g. withName) Here one can't simply use MonadIO because of bracket's type. The haskeline package has a class MonadException to handle stuff like this, but its instances are incomplete compared to those of MonadIO. (And why on earth is this buried in haskeline? IMHO this should live directly beneath MonadIO itself.) I'm not sure if this is the right approach, so any hints/comments would be highly appreciated.

Another thing to consider is callbacks, e.g. in GLUT or in OpenGL's debug API: AFAICT, these have to use plain IO, because I can't see a way to convert them back to MonadIO. Is this correct? It's not really nice and a bit inconsistent, but probably OK.

ekmett commented 9 years ago

Take a look at the 'exceptions' package. I created it with Mark Lentczner to provide a fairly standard place for such a class. It has wide adoption and is in stackage.

http://packdeps.haskellers.com/reverse/exceptions

Given that it is Mark's baby, getting it into the platform may be a bit easier than any of the alternatives. ;)

-Edward

On Sun, Mar 22, 2015 at 3:00 PM, Sven Panne notifications@github.com wrote:

[ Short version: I need some help/comments regarding bracket and callbacks. }

FYI: I'm using the separate StateVar and ObjectName packages in all my OpenGL/OpenAL packages now, this was easy. Furthermore, I've switched almost everything to MonadIO instead of plain IO, but I have a conceptual problem now: What shall I do with functions which internally use bracket and friends? (e.g. withName https://github.com/haskell-opengl/OpenGL/blob/master/src/Graphics/Rendering/OpenGL/GL/Selection.hs#L79) Here one can't simply use MonadIO because of bracket's type. The haskeline http://hackage.haskell.org/package/haskeline-0.7.2.1/docs/System-Console-Haskeline-MonadException.html package has a class MonadException to handle stuff like this, but its instances are incomplete compared to those of MonadIO. (And why on earth is this buried in haskeline? IMHO this should live directly beneath MonadIO itself.) I'm not sure if this is the right approach, so any hints/comments would be highly appreciated.

Another thing to consider is callbacks, e.g. in GLUT or in OpenGL's debug API: AFAICT, these have to use plain IO, because I can't see a way to convert them back to MonadIO. Is this correct? It's not really nice and a bit inconsistent, but probably OK.

— Reply to this email directly or view it on GitHub https://github.com/haskell-opengl/OpenGL/issues/61#issuecomment-84676215 .

svenpanne commented 9 years ago

Ah, nice, thank you! That seems to be exactly what I was looking for, and I was quite sure that there is already something for such an obvious gap in the APIs shipped with GHC, I was just unable to find it. IMHO this package deserves a more prominent place, perhaps it should even be folded into base/transformers.

Perhaps I can polish my OpenGL packages into something really nice before Khronos finishes Vulkan... :grin:

ekmett commented 9 years ago

transformers doesn't implement the classes themselves. As it stands now you can get and use these classes even on ancient platforms that ship with transformers 0.2.

Folding it into mtl would actually reduce support in many ways. It also uses extensions that the mtl does not currently use. (Rank-N types for mask/uninterruptibleMask).

base isn't really in the business of making classes like this. It just doesn't have any today.

Consequently, where it lives now is probably the best compromise we're going to get.

svenpanne commented 9 years ago

OK, I'm making some progress regarding the generalization of IO to MonadIO/MonadCatch, but I'm hitting a problem now: What can I do with IO actions nested in e.g. allocaArray (see e.g. getHitRecords)? The only way to generalize the IO action is giving up allocaArray and friends and do this via mallocArray/free/bracket by hand. :disappointed: Or did I miss something? @ekmett to the rescue! :wink:

ekmett commented 9 years ago

In general that combinator isn't safe under a monad transformer.

Consider a version of ListT IO a it has MonadIO, but it'll execute the future multiple times. If you switched to a manual 'free' your continuation could free the result several times.

You can limit the 'input' monadic action to IO:

getHitRecords :: MonadIO m => GLsizei -> IO a -> m (a, Maybe [HitRecord]) Trying to allow more is unfortunately lying. You don't have a well-defined scope.

My usual approach would be to refactor until I wasn't trying to use this abstraction, but the above is the best legal fix for it.

-Edward

On Tue, Mar 24, 2015 at 6:15 AM, Sven Panne notifications@github.com wrote:

OK, I'm making some progress regarding the generalization of IO to MonadIO /MonadCatch, but I'm hitting a problem now: What can I do with IO actions nested in e.g. allocaArray (see e.g. getHitRecords https://github.com/haskell-opengl/OpenGL/blob/master/src/Graphics/Rendering/OpenGL/GL/Selection.hs#L37)? The only way to generalize the IO action is giving up allocaArray and friends and do this via mallocArray/free/bracket by hand. [image: :disappointed:] Or did I miss something? @ekmett https://github.com/ekmett to the rescue! [image: :wink:]

— Reply to this email directly or view it on GitHub https://github.com/haskell-opengl/OpenGL/issues/61#issuecomment-85435514 .

ekmett commented 9 years ago

If we want to get this into the platform, we're going to have to move fast. Otherwise, we'll probably have another year of dealing with the status quo.

ekmett commented 9 years ago

This has been all done since May 3rd. Closing the issue.