Just learned a bunch of neat stuff this weekend. Had a discussion on darcs-devel with Juliusz over issue171. I had propoposed sort of a naive fix based on looking for ssh paths on the command line arguments, and he suggested instead something that would call the ssh control master when need be, using laziness and unsafePerformIO. My reaction to this was... "uh, sounds nice, but I don't know how?". So after I submit my patch, he shows me what he has in mind, and lightbulb! It makes perfect sense when you look at it, but it so is not something I could have come up with by myself. The pattern goes like this:
type Bar = ...
foo :: Bar
foo = unsafePerformIO fooIO
fooIO :: IO Bar
And the effect is that when you call foo, the embedded IO action gets executed when needed (because of laziness), but only once! Haskell is still a pretty mysterious and magical language to me, but the way I like to think of it goes like this: we're doing functional programming, right, so once we know the value for something, that value is never going change, so there's no point reevaluating it. We just keep it around all pointer-like (hand waves). Kinda scary how long I've been getting by without really understanding how things work underneath.
There's two things I do with laziness at work:
- learn to live with it - i.e. program without actually caring about when things actually happen
- use it to simplify code (since it's lazy and you don't care, you can write some things in a naive-looking way and know that they'll work just fine)
but my Haskell still has not reached the point where you actually reach out and exploit the laziness, really bend it to your will as opposed to just accepting it as a free, if occasionally annoying, gift. I don't mean to be making a really big deal out of this, seems to be something we do a lot in the darcs code (lazy, unsafe, magical, yaaaawn), but from my fresh newbie eyes, little things like that just make you go woah.
atexit
The other wow-neat moment was looking at the AtExit code. Not something I neccesarily understand, although I do recognise bits and pieces of Concurrent Haskell that I gleaned from one the Simons' Asynchronous Exceptions paper (we needed asynchronous exceptions at work), but very neat nonetheles. The net effect is that the unsafe+lazy idiom that Juliusz showed me combined with Tomasz's atexit means that all the SSH stuff is now all nice and encapsulated. No details! The ssh control master stuff just works magically and the person invoking it doesn't have to know a danged thing about it. This black box has been brought to you by lazy evaluation and people with names that end in Z.
patch set
Not only did I not go to work this weekend like I told myself I would be doing, I even procrastinated on my hobby. Basically put off till almost bedtime looking into darcs internals and figuring out what the heck a patch set is so that I can implement the minimal context function. My one insight after unrolling the big ball-o-yarn is that there probably is some relationship between patch sets and inventories; and that the best way to find out what a patch set was would be to look at the code that actually makes them out of thin air. So now the function I'm staring at is
DarcsRepo.read_repo_private
. Slowly getting closer. Guess I'll figure more stuff out next weekend.
Just learned a bunch of neat stuff this weekend. Had a discussion on darcs-devel with Juliusz over issue171. I had propoposed sort of a naive fix based on looking for ssh paths on the command line arguments, and he suggested instead something that would call the ssh control master when need be, using laziness and unsafePerformIO. My reaction to this was... "uh, sounds nice, but I don't know how?". So after I submit my patch, he shows me what he has in mind, and lightbulb! It makes perfect sense when you look at it, but it so is not something I could have come up with by myself. The pattern goes like this:
type Bar = ...
foo :: Bar
foo = unsafePerformIO fooIO
fooIO :: IO Bar
And the effect is that when you call foo, the embedded IO action gets executed when needed (because of laziness), but only once! Haskell is still a pretty mysterious and magical language to me, but the way I like to think of it goes like this: we're doing functional programming, right, so once we know the value for something, that value is never going change, so there's no point reevaluating it. We just keep it around all pointer-like (hand waves). Kinda scary how long I've been getting by without really understanding how things work underneath.
There's two things I do with laziness at work:
- learn to live with it - i.e. program without actually caring about when things actually happen
- use it to simplify code (since it's lazy and you don't care, you can write some things in a naive-looking way and know that they'll work just fine)
but my Haskell still has not reached the point where you actually reach out and exploit the laziness, really bend it to your will as opposed to just accepting it as a free, if occasionally annoying, gift. I don't mean to be making a really big deal out of this, seems to be something we do a lot in the darcs code (lazy, unsafe, magical, yaaaawn), but from my fresh newbie eyes, little things like that just make you go woah.
atexit
The other wow-neat moment was looking at the AtExit code. Not something I neccesarily understand, although I do recognise bits and pieces of Concurrent Haskell that I gleaned from one the Simons' Asynchronous Exceptions paper (we needed asynchronous exceptions at work), but very neat nonetheles. The net effect is that the unsafe+lazy idiom that Juliusz showed me combined with Tomasz's atexit means that all the SSH stuff is now all nice and encapsulated. No details! The ssh control master stuff just works magically and the person invoking it doesn't have to know a danged thing about it. This black box has been brought to you by lazy evaluation and people with names that end in Z.
patch set
Not only did I not go to work this weekend like I told myself I would be doing, I even procrastinated on my hobby. Basically put off till almost bedtime looking into darcs internals and figuring out what the heck a patch set is so that I can implement the minimal context function. My one insight after unrolling the big ball-o-yarn is that there probably is some relationship between patch sets and inventories; and that the best way to find out what a patch set was would be to look at the code that actually makes them out of thin air. So now the function I'm staring at is
DarcsRepo.read_repo_private
. Slowly getting closer. Guess I'll figure more stuff out next weekend.
laziness, unsafePerformIO, atexit
1 comment:
The way I think about unsafePreformIO is that it's actually a big non-haskellian hack, as it essentially breaks the referential tranparancy haskell relies upon. In a pure functional language, there shouldn't be a difference between a function and it's value. The fact that it works to initialise a variable only once is in fact dependant on 'implementation defined behaviour', not on a supported interface.
Unfortunately, sometimes there is just no good way around using unsafe IO, and I think that's an important limitation in the core haskell language.
Post a Comment