Yesterday I had the idea of lazy sequences that can go in both directions. I dropped into #clojure on irc.freenode.net to ask someone about it.
Given the fact that Clojure-in-Clojure is possible, it is thus possible to define lazy seqs. Would it also be possible to define a lazy seq where the 0th element is actually in the middle of an infinite lazy seq extending in both ways? so like (iterate inc) with negative number included :)
I found Chris Houser(Author of Joy of Clojure) interested, and willing to help me along.
so... how shall we proceed? You want to see my code or should I try to drop hints or ask leading questions?
I went for the later one, and had a great learning experience. Near then end Paul deGrandis chimed in to recommend Joy of Clojure. I'm convinced!
Joy of Clojure has an awesome section on proxy, reify, defprotocol, and defrecord. I reference it often to judge if I'm abusing something
At the point you're at in Clojure (based on what I follow in here), you'd get a lot of mileage out of it.
(defprotocolSpinnable(spin[this]"Return a seq walking the opposite direction as this"))(defniter-bi[xfb](reifySpinnable(spin[_](iter-bixbf))clojure.lang.ISeq(first[_]x)(more[_](iter-bi(fx)fb))(next[this](restthis)); same as rest since all iter-bi's are infinite seqs(seq[this]this)(equiv[__]false))); cheater!(extend-typeclojure.lang.LazySeqSpinnable(spin[this](spin(seqthis))))(->>(iter-bi0incdec)(drop4)spin(take10));=> (5 4 3 2 1 0 -1 -2 -3 -4)
fliebel: Given the fact that Clojure-in-Clojure is possible, it is thus possible to define lazy seqs. Would it also be possible to define a lazy seq where the 0th element is actually in the middle of an infinite lazy seq extending in both ways? so like (iterate inc) with negative number included :)
chouser: ISeq only provides an API for walking forwards. You'd need a different interface for walking the other direction.
fliebel: Hrm :( that means I'll be unable to use with with existing functions, right?
chouser: well, what existing function do you intend to call to get item before 'first'?
fliebel: Dunno‚ I mean, if I'm not using ISeq, I can’t even use first, right?
chouser: well, your theoretical bidirectional lazy seq could implement ISeq for going forward
fliebel: Oh wait, Java, so I can implement ISeq and add my own interface and fns for going the other way.
fliebel: How about this‚ you define a lazy seq like iterate but with 2 fns, for going in either direction. then you call spin on it to change which direction you're seqing over :)
chouser: hm, interesting.
fliebel: So, what Clojure bits do I need to implement that?
chouser: a deftype and ... a function?
fliebel: *looks up deftype*
chouser: actually, just a deftype would do, though it's not quite as pretty.
chouser: I think that's the same as what I have. but you said "nearly"?
fliebel: The problem I had was that first was defined as the first of this, but I couldn't do (first this) of course.
chouser: if you want to "check" your answer:
(defprotocolSpinnable(spin[this]"Return a seq walking the opposite direction as this"))(defniter-bi[xfb](reifySpinnable(spin[_](iter-bixbf))clojure.lang.ISeq(first[_]x)(more[_](iter-bi(fx)fb))(next[this](restthis)); same as rest since all iter-bi's are infinite seqs(seq[this]this)(equiv[__]false))); cheater!(extend-typeclojure.lang.LazySeqSpinnable(spin[this](spin(seqthis))))(->>(iter-bi0incdec)(drop4)spin(take10));=> (5 4 3 2 1 0 -1 -2 -3 -4)
fliebel: this is your version?
chouser: yep
fliebel: very close :) Only you have equiv defined and next uses more. Does it matter if you use (.more method call) or rest?
chouser: not really. yours is probably faster
but duplicates half a line of code. :-)
fliebel: yea... :(
chouser: so take your pick. what you've got is good.
fliebel: One problem I have is that drop 5 returns a lazy seq which I can't reverse.
chouser: yeah, you see how I work around that in my example?
fliebel: rest, I used seq to do the job
chouser: oh! smart!
chouser: interesting -- I hadn't considered that, and wouldn't have been sure it would work. nicely done.
fliebel: I wasn’t sure it would work, but seq is magic you know...
fliebel: One thing I haven't figured out yet is how to make a seq that goes 1 2 3 4 5 4 3 2 1, so changes direction in the middle.
chouser: BTW, I wanted to call attention to my comment on rseq. I think we're abusing it here and probably ought to define a new protocol instead. (defprotocol Spinnable (spin [_])) or something.
fliebel: Okay. that simple?
chouser: yep
fliebel: So now in reify I replace reversible with spinnable?
chouser: there, now you don't have to call seq manually on lazy-seqs:
(defprotocolSpinnable(spin[this]"Return a seq walking the opposite direction as this"))(defniter-bi[xfb](reifySpinnable(spin[_](iter-bixbf))clojure.lang.ISeq(first[_]x)(more[_](iter-bi(fx)fb))(next[this](restthis)); same as rest since all iter-bi's are infinite seqs(seq[this]this)(equiv[__]false))); cheater!(extend-typeclojure.lang.LazySeqSpinnable(spin[this](spin(seqthis))))(->>(iter-bi0incdec)(drop4)spin(take10));=> (5 4 3 2 1 0 -1 -2 -3 -4)
fliebel: What? You can just add behavior to existing types? Am I in #ruby?
chouser: nope. in clojure we do it without monkey patching or adapter classes. :-)
chouser: I mean "nope" you're not in #ruby. Of course you can write new functions that take existing classes as args that then do their own thing with them.
fliebel: But you just added extra behavior to LazySeq, right?
dnolen: it's a properly namespaced extension tho. It's not visible to other namespaces.
chouser: it certainly looks like I did. In this case we could have written spin like: (defn spin [x] (if (instance? LazySeq x) ...))) right?
fliebel: I think so...
chouser: it's our function, we just defined it with defprotocol which allows us to add new cases on the fly
fliebel: yay
chouser: essentially. Except then rhickey sprinkled it with magic dust to make it very fast.
fliebel: :)
chouser: it's good to know not everyone knows about this yet -- it's my topic at Strange Loop
fliebel: oh, I feel special now :)
chouser: good I'm glad! But... why?
fliebel: Because I know something not everyone knows ;)
chouser: ah, good!
fliebel: Except that I'm not o sure yet what exactly you just said. Only that it's magic and fast. Two things I like especially.
Today I was browsing HackerNews and reading up on the latest cool projects including at least 3 buzzword technologies, when I suddenly realized how strange it was to go "ooh" and "aah" for weekend projects written in fancy high-level languages while we are ignorant to the people who get their hand dirty to make it possible in the first place.
That is when I decided to find the authors of my sound card driver and thank them for their amazing work. I run Ubuntu now and then on my iMac, but the sound never worked for me, until recent versions of Ubuntu shipped the correct ALSA driver.
I encourage you to do the same.
Go to a technology you use to write awesome applications or the solution to a problem you had.
Look for the dependencies, and continue to drill down until you find a project that makes you blink your eyes ands say "wow, I didn't know this was involved."
Thank the author. Really, I'm serious!
I had to find out which sound card I have, what the driver for that was, and finally who the authors are. It turns out It's the Intel HDA driver in the alsa-driver package. The source file contained their email addresses.
Did you know Node.js uses V8, which uses Scons, which uses Python, which uses readline, which uses ncurses, which are all compiled using GCC or LLVM?
Did you know you can use your package manager to see the dependencies of a project?
Did you realize your package manager installs hundreds of dependencies and dependencies of dependencies to run something like Python?
Consider who is more awesome, the guy who writes a Twitter app in Node, the guy who made Node, or the guy who made Node possible.
After reading thesetwo articles about how to compromise Twitter tokens from a client and about how Twitter is abusing OAuth secrets and screwing OS clients by blocking their tokens, I came up with the following plan.
I really love Twitter and the Twitter API, but it is obvious by now that Twitter does not care about small OSS projects.
I propose small Open Source Twitter clients to use tokens from the big fish as a fallback for their own tokens. This will allow small clients to continue to work after their tokens are blocked(albeit under the big fish name), unless Twitter blocks their own clients as well or faces the fact that their approach doesn't work.
A more politically correct option would be to write an OAuth Echo service that stores your tokens in a secure place, and accepts request without a secret. Take your pick.