Monday, April 10. 2006
The beauty of Rasmus's Unframework, and the ugliness of advocates
Railing on Rails
Probably the most annoying, asinine, and missing-the-point-entirely, is an entire blog post that claims to compare the no framework PHP version with Rails. Look, I am not about to rag on Rails. It is obvious that it does something right and good for a lot of people; in fact, installing and playing with rails is very high up on my todo list.One of the things that bugs me about this comparison it is at the end of each subsection is a running tally of lines of code written, as if that is the be-all and end-all of programming. If it really had to do with how many lines of code we wrote, we'd all agree that a language like scheme is best, and we'd all program in one language.
But really, the biggest thing is this, I'll just quote the relevant authors, and let them speak for themselves:
I don't have much of a problem with MVC itself. It's the framework baggage that usually comes along with it that I avoid. Parts of frameworks can be useful as long as you can separate the parts out that you need. As for MVC, if you use it carefully, it can be useful in a web application. Just make sure you avoid the temptation of creating a single monolithic controller. A web application by its very nature is a series of small discrete requests. If you send all of your requests through a single controller on a single machine you have just defeated this very important architecture. Discreteness gives you scalability and modularity. You can break large problems up into a series of very small and modular solutions and you can deploy these across as many servers as you like. You need to tie them together to some extent most likely through some backend datastore, but keep them as separate as possible. This means you want your views and controllers very close to each other and you want to keep your controllers as small as possible.--Rasmus
The controller handles the requests of the user. Every request goes through a controller first. The controller uses the model and view to create a response. In this application, one of the tasks of the controller is to load a list of products, using the model. The controller gives this list to the view, which turns it into HTML.--Jules
The difference is obvious. Rasmus is talking about why using a monolithic controller might not be a good thing, and shows us how one can get round that using his language of choice. Jules is showing us that his language and framework of choice is so much better then ours because his framework writes all his code for him. Glossing over the fact that Rails itself has, just as a guess, several kilo-lines of code. So I guess that means that PHP is better, because it only took PHP a few hundred lines for what took rails thousands.
Now as I said, I don't have it in for Rails. I don't actually believe that a framework is better then a language for writing web applications. I think both have their place.
Object Fascism
The other funny/annoying part of the comments to Rasmus's blog is the OO-Zealots, another great quote:I think you are doing a lot of damage with this posting. Do you realize what's going to happen if ONE of the 30 programmers in the building I work in find's your post? For years I have struggled convincing fellow programmer's that PHP is NOT a simple scripting language for the gamers and what not, that it actually CAN compete with J2EE and ASP.NET and C# if used where appropiate. To read this from the creator of the language....I'm still shocked--Jurgan
J2EE Tag Soup
One of the arguments against PHP is the "Islands of code" argument. The theory is that islands of code inside of your HTML files is bad, hard to maintain, and basically evil.The problem is however, that the J2EE camp seem to be trying to sell us on using islands of XML instead. So there is a 3rd markup language that the web developers and the app developers have to learn. There is yet more layers of indirection. Indirection when used appropriately is important. Some indirection however is like too much makeup on a cheap whore.
Personally, I rather prefer the <?PHP ... ?> style (assuming that you are separating your display logic from your business logic). I mean, we are using the XML processing instruction for... are you ready for this? Document processing.
Anyway, the central point here is that you can talk till you are blue in the face about how PHP is a perfectly valid language, about how it can be used appropriately, until you are blue in the face. 90% of them will not listen. They don't care. The C# guys spent a fuck-tonne of money becoming certified by Microsoft. They are firmly, and proudly locked in. If they have to admit to the possibility that there is another perfectly valid way of doing things, their house of cards comes crashing to the ground. The same can be said with the Java guys. They love their Bondage and Discipline language, and wear their love proudly. It is so much easier to sneer at a language that challenges what we think of as "good" then it is to actually learn from and learn about that language.
To those guys, I say "So what". Let them spend countless dollars on training and education on broken languages. Go learn Scheme, Haskell or something. Go see what the fuss is about continuations and web programming, so that the next time they start to sneer in your direction, you can retort with something like "Oh, I'm sure glad your language of choice solves every problem. Why aren't we all using Language X for everything? Quick tell the world!". Language advocates are annoying and dumb, and should be treated as such. Don't treat them as people to convince, but people to scorn.
Okay, sorry, this got a little ranty. But language advocates piss me off. There is a big difference between loving a language, and loving a language to the exclusion of all others. I'll finish off by pointing you to this great entry about Java by Steve, titled: Execution in the kingdom of nouns.
Tuesday, February 28. 2006
Introducing BunnyRegex: easy regular expressions, and mini-languages inside of PHP.
Regular expressions are hard. They are hard to create, and even harder to read after the fact. Regular expressions, while quite powerful, are a blight upon readable code. There is no easy way to know that '/^\d{4}\/\d{2}\/\d{2}$/' is a search for a string in the form of 'xxxx/xx/xx'. You have to know that ^ is the start of a line, \d is a digit, \/ is / escaped, etc.
Granted, once you know regular expressions, that information is portable across which ever language you use, be it PHP, Perl, Javascript, whatever. But getting to that point is not easy, and even after you are there, the fact remains:Regular expressions are not human parseable. This is where BunnyRegex comes in.
BunnyRegex is a BSD Licensed class wrapper around the preg_* functions of PHP. The whole point of its existence is to make working with regular expressions less like the memorization and usage of ancient Norse runes (Elder Futhark) and more like, you know, actual programming. BunnyRegex is also a great example of Fluent Programming and the implementation of mini-languages (more on this in a new entry). BunnyRegex is available on my personal wiki.
So, here is some code illustrating how BunnyRegex works. We are going to do the same regular expression for the string 'xxxx/xx/xx'.
include("BunnyRegex.php");
$pattern = new BunnyRegex();
$pattern->bol() // ^
->digit() // \d
->exactly(4) // {4}
->string('/') // /
->digit() // \d
->exactly(2) // {2}
->string('/') // /
->digit() // \d
->exactly(2) // {2}
->eol(); // $
$pattern->match('2006/02/28'); // returns true
$pattern->numberOfMatches('2006/02/28'); // returns 1
There are a few things to notice here. First off, you will notice that the way this code is laid out is almost completely backwards from the way PCRE is normally written inside of PHP; usually the readable part of the code is the comments, and the unreadable part is the code itself. Second, you will notice that there is a chain of method calls, starting from the bol() (i.e. beginning of line) method and finishing off at the eol() method. And finally, you will notice that the regular expression can be re-used, as shown by the match() and numberOfMatches() method calls. So what is going on here?
Fluent Regular Expressions
Every method of BunnyRegex that helps build your regular expression returns an object reference to the current object. This means that you can chain together methods like This:
startCapture()->startGroup()->upper()->lower()->oneOrMore()->endGroup()->moreThan(1)->endCapture()
which is equivalent to this:
$pattern->startCapture(); $pattern->startGroup(); $pattern->upper(); $pattern->lower(); $pattern->oneOrMore(); $pattern->endGroup(); $pattern->moreThan(1); $pattern->endCapture();
This method of programming is called "Fluent Programming" and depending on the situation, can make for much more readable code. When fluent programming is applied to BunnyRegex, it allows you to format your regular expression code in the same way that you format the rest of your code, with proper indentation of blocks. For example:
$pattern
->startCapture()
->startGroup()
->upper()
->lower()->oneOrMore()
->endGroup()
->moreThan(1)
->endCapture();
You can pretty well see what is going on here just by looking, as opposed to the string '((?[A-Z][a-z]+){2,})'. What is lost of course is brevity; those of you who enjoy being able to pound out something as powerful as "return every CamelCase word to me as an array" in just one line are not going to be impressed. Neither are those of you who feel somehow special by knowing and understanding cryptic regular expressions. However, those of you who work with regular expressions seldom enough not to remember every minute character/meta-character combination, but frequently enough so that it is a pain in the ass, then BunnyRegex is for you. The method names may even be slightly less memorable then the usual regex sigils, but they are about 100 times more readable.
I have found so far that the best way to format the regular expression is to give each component its own line, with exception to the repetition operators (noneOrOne(), noneOrMore(), oneOrMore(), moreThan(), between(), etc.) which should be grouped with the entity they are repeating. The nonGreedy operator should follow the repetition operator.
Update:
I hit post just a little too soon on this one. Oops. Here is the restAs you can see, this is a lot like a mini-language that is implemented right inside of php, rather then just a simple class. This is where fluent programming really shines because it allows you to do two things: 1) think about your problem in terms of the implementation of a mini-language and 2) wrap the solution of your problem in a reuseable, exportable software component. This idea of implementing a language to solve your problem is not a new one, the Scheme guys have been doing it for years, but it is really quite an effective way to think about and solve your problem. If the language you implement is sufficiently general, you can turn around and use it to solve other similar problems.
Matching, Grepping, Replacing and Splitting with BunnyRegex
Once you have built your regular expression, you probably want to do something with it. About the easiest thing that you can do is to simply get the raw regular expression with get(). You can then use the normal preg_* functions on that string. However, BunnyRegex also gives you access to the same functions from within (with slightly different names and semantics). Use whichever method you feel is comfortable. I am not going to document these, because their behavior so closely mirrors the preg_* functions. Check the PHP Manual for more info.
- match($subject) is similar to preg_match, except it will return a boolean as to whether or not the match failed or succeeded
- numberOfMatches($subject) is exactly the same as preg_match_all.
- captures($subject) will return the array of captures that preg_match would return in its 3rd argument.
- allCaptures($subject) is similar to the previous method, except it uses preg_match_all.
- grep($subjectArray) is preg_grep. It takes an array, and then returns an array of elements that match.
All of these methods take a (string) subject to match against as an argument, with exception to grep.
- replace($replacement, $subject, [$limit]) is preg_replace. No frills, no lace.
- replaceWithCallback($callback, $subject, [$limit]) is preg_replace_callback. No frills, no lace.
The final method is split($subject, [$allowempty], [$limit]) which, like most of the other methods, is a simple wrapper around the similarly named preg_split function.
As I mentioned earlier, you can run as many of the match methods on your regular expression as you like. You can even build a regular expression, match it, and then continue to build it and matching it:
$r = new BunnyRegex()
$r->bol()
->whitespace()->noneOrMore()
->string('*@');
if ($r->match($myString))
{
$r->string('param');
if ($r->match($myString))
{
....
}
}
I am not sure just how useful this is to people, but it shows just how flexable BunnyRegex is.
At any rate, I hope that this class will be useful to the PHP community, especially those who are tired of programming in Elder Futhark
Tuesday, January 24. 2006
Making Fluent Interfaces Readable in PHP
There is a lot of talk about fluent interfaces out there in the PHP blogsphere. I like the idea of fluent interfaces, they're great mojo, but like most mojo, it takes some care.
What are Fluent Interfaces anyway?
Fluent Interfaces are a method of programming where setters, and sometimes behavioural methods that normally return void/null actually return an object instance, usually (but not always) the object being operated on. This is so that you can get around the effect where you make a temporary variable to alter an object, but don't end up using it again.
Fluent Interfaces were first developed discovered by Eric Evans, and described by Martin Fowler in his bliki. Mike Naberezny Describes fluents quite succinctly, especially if you are a PHP guy.
However, there seem to be some complaints about the readability and usability of Fluent interfaces, (especially if you check Jason E. Sweat's blog). One relevant criticism (brought up by Jason in his comment section) is that it violates the Principle of Least Surprise.
Making Fluent Interfaces... Fluent
The key to making fluent interfaces readable, workable and—dare I say—fluent, is to sprinkle in some magic. If you define an empty interface called "Fluent", and any time you build a class that is to be fluent, then you are in effect signalling to other users of the class that you are providing a fluent interface. Easy hey?
interface Fluent
{
}
But check this out, the real magic is here. Using PHP's magic __call() method, you can build a wrapper class to make a non-fluent class in to a fluent class quite easily.
class Fluenter
{
private $obj;
function __construct($obj)
{
$this->obj = $obj;
}
static function MakeFluent($obj)
{
if ($obj instanceof fluent)
return $obj;
else
return new fluenter($obj);
}
function __call($method, $args)
{
$result = call_user_func_array(array($this->obj, $method), $args);
if (is_null($result))
return $this;
else if (is_object($result) and ($result instanceof $fluent))
return $result;
else
throw new RuntimeException(
"Fluent::__call called method $method ".
"and expected a null return or a non-fluent object, ".
"got (".gettype($return).") $return instead.");
}
}
Using the Fluenter
class NonFluentClass
{
private $a, $b, $c, $d = array();
function __construct($a, $b)
{
$this->a = $a;
$this->b = $b;
}
function setC($c)
{
$this->c = $c;
}
function addD($d)
{
$this->d[] = $d;
}
}
class FluentClass implements Fluent
{
function __construct($a, $b)
{
$this->a = $a;
$this->b = $b;
}
function setC($c)
{
$this->c = $c;
return $this;
}
function addD($d)
{
$this->d[] = $d;
return $this;
}
}
$foo = new nonFluentClass(2,3);
Fluenter::MakeFluent($foo) ->setC(5)
->addD(6)
->addD(23);
echo "NonFluentClass turned into a Fluent with Fluenter::MakeFluent:\n";
var_dump($foo);
echo "\n\n";
echo "FluentClass used with Fluenter::MakeFluent:\n";
$bar = new FluentClass(2,3);
Fluenter::MakeFluent($bar) ->setC(5)
->addD(6)
->addD(23);
var_dump($bar);
Output
NonFluentClass turned into a Fluent with Fluenter::MakeFluent:
object(NonFluentClass)#1 (4) {
["a:private"]=>
int(2)
["b:private"]=>
int(3)
["c:private"]=>
int(5)
["d:private"]=>
array(2) {
[0]=>
int(6)
[1]=>
int(23)
}
}
FluentClass used with Fluenter::MakeFluent:
object(FluentClass)#2 (4) {
["a"]=>
int(2)
["b"]=>
int(3)
["c"]=>
int(5)
["d"]=>
array(2) {
[0]=>
int(6)
[1]=>
int(23)
}
}
Notice how the Fluenter class handles fluent and non-fluent objects relatively gracefully. Also by using the syntax Fluenter::MakeFluent($obj) we are explicitly telling the programmer that the next chunk of code is being programmed in a fluent style. With some proper class documentation, programmers unaware of the fluent style could easily become educated.
Monday, December 6. 2004
One macro makes you small... Scheme and the BunnyObject System
One of the things that scheme doesn't have natively (at least, not defined by R5RS) is the notion of classes and objects. It does have closures, and practically anything you can do with objects, you can do with closures (including building a class system, but I am getting ahead of myself). In fact, Scheme was built with the intention to play with an Actors Model style of OOP, and the original designers found that the special language constructs they built to create actors, and handle messages, can be adequately expressed with a lambda and case statement. You can see an example of this here: simple-object at the CSW.
So this time around (If you haven't guessed yet) I am going to show you the basics of building an object system within scheme. There are plenty of them out there and each one with their own properties. The one I am going to present to you is based around a simple closure-based object system, with a Meta-Object-Protocol. Because my Scheme-of-choice is LispMe, it makes use of "def-macro" style macros. If your scheme doesn't have this, you're going to have to convert it to your implementations method of macros (which is quite likely a hygienic macro. Go you.)
Design Decisions
The basics of the class construction are very similar to the simple object above. That is, we are going to use closures and message passing. except we are going to sugar out a lot of the extra syntax. The reason I chose this method is that I want each of the objects methods to have access to its fields in its environment. We're also going to keep a list of fields (and field values) available to the object so that we can provide a meta-object protocol.A little Digression on LispMe Macros
Given that the first element in the list that is the arguments to a LispMe macro is the name of the macro, it is not entirely inconceivable to build some kind of macro-dispatch system. You could do some kind of Aspect-Oriented-Meta-Programming with this. You could even build some kind of twisted class system out of this.LispMe Macros
LispMe macros are a bit weird. Sometimes I have found that if you use a macro on the same memo that you define it, LispMe will get all mention-not-use on you, and complain that [macro] is not a function. I don't know why that is, and I haven't really attempted to suss it out (or post a bug report, bad Jonnay!) but there it is. It also explains the list bound to default-slots, instead of a metaclass. Again, I get ahead of myself.A Macro in LispMe takes one argument, a list of all the arguments passed to that macro with the car of that list being the name of the macro. This allows us to handle syntax inside of our macro. Very primitive mind you! This is the "Ugh Fire!", bone tools, and cave paintings of defining syntax.
Now, when I am defining my macro, I define a very simple stub macro, that calls a function, with the rest of the arguments passed to the macro, ignoring the first argument (that being the name of the macro). The primary reason I do this is that LispMe doesn't have any kind of native Macro-Expansion facilities, so this fudges a first-level expansion. This is by far the easiest way to debug a macro as well. Now, our macro will need to spit out a s-expression that will become our valid Scheme code. So we need to think about what we what to input (a list of member/method names) and what we want to output (a lambda-in-a-lambda closure, with a message handler of sorts).
Really, the most important thing is how we actually intend to use this class system. So, lets start by defining a class with our fictitious class macro:
(define point (class
(slots (
(x 0)
(y 0)
(%construct (lambda (cx cy)
(set! x cx)(set! y cy)))
(get (lambda () (cons x y)))
(set-x (lambda (v) (set! x v)))
(set-y (lambda (v) (set! y v)))))))You can already see a few features of this class system by this very contrived example, First of all, class members (such as x and y) are visible to other members from within the class. This removes the need for an object to constantly call itself to get its own values. No more this.foo or $this->foo! Horray! (Though, as we shall see later, as part of the MOP, we are going to provide this kind of functionality.) Second of all, class methods are just members that happen to be lambda expressions, if you are a scheme programmer, this shouldn't surprise you. You'll notice in this example I called the constructor %construct. This is similar to the PHP/Python method of naming important (or magic) members with a typographic convention (__, double underscore in PHP/python). I chose the scheme sigil %, which means "lower-level".
Another thing to note is that not only are these classes data structures first-class (that is, you can pass a class as an argument to a function or special form) but they are anonymous, just like lambdas. For instance, we could easily do something like this:
((class (slots ( (foo 'baz) (%construct (lambda (f) (set-foo f))) (set-foo (lambda (f) (set! foo f))) (get (lambda () (list 'foo 'is foo)))))) 'bar)Which, constructs an object (with the argument 'bar) out of an anonymous class with the members foo, set-foo and get.
Liber MMM - Magickal Mystery Methods
%construct is one of them, but what kind, and how many other of these mystery methods should we have? The answer is actually quite simple. We're going to need one to handle the message, which we will call %act (after the Actors Model). We're also going to need a list of slots (or members) for this object, which we will call %slots, and a method to return that list, %get-slots.Of course, once we have this basis, we will find that it would be nice to have a way to tell if a slot was bound or not, (for our %act method), so we have '%in-slots?'. Because we are going to provide an intercessory MOP, we need a way to directly access and change slots, which we will call %get and '%set!' respectively. This will allow our class user to change the way a class works by using '%set!'. For instance, a very simple method of superclassing can be added by an object user by modifying %act to pass messages on to a parent class (or classes if you like). You could also institute private/public access control, aspect weaving, or practically anything else. The only thing that you cannot do is dynamically add members. This actually fits in (in my opinion) with how scheme itself works in regards to lexical scope.
And now... The Code
Now that we have our features all worked out, I am going to show you the code. It shouldn't be too hard to figure out.; Base.BunnyObjects
;; The actual macro. Note that it just calls %class
(macro (class a)
(apply %class (cdr a)))
;; This performs the work of writing out the class definition.
;; note that you can call it directly, and it will return the
;; sexpression, instead of constructing the class.
(define (%class . a)
; This is where we handle our syntax. 'a' is a list of
; our arguments, which start out being (slots (...))
; very similar to a let expression. Later I will add
; a (static (...)) syntax, and maybe a (parent (...))
; syntax.
;
; a-val is defined later. It simply applies a function
; to the value of an assoc.
(let ((slots (a-val cadr 'slots a)))
; Take the list of defined slots, and join it together
; with a list of the default slots. (see below)
;
; we do a set! here, because I am going
; to modify this soon to allow for inheritance
; alist-merge is defined later. It merges two
; associative lists, with the first taking
; precedence over the second.
(set! slots
(alist-merge slots
default-slots))
; Now we actually write out our first lambda, which will
; construct our closure-object
`(lambda c-args
; Write out our slot key-vals into a letrec.
(letrec ,slots
; Now we set our magickal member %slots to that
; key-val list for our MOP. We include both keys
; and values, so that we can add implementation
; inheritance later.
(set! %slots ',slots)
; Now we actually execute the classes constructor
(apply %construct c-args)
; And finally, return a procedure which will handle
; the message. Note that LispMe doesn't like
; dotted lists in a quasi-quoted expression , so I
; had to Unquote and requote instead.
,'(lambda (msg . margs)
(%act msg margs))))))
;; This is our list of default slots. The adventuresome user
;; could redefine this, and completely change the behavior
;; of all future objects at the time of redefinition.
(define default-slots
; Default constructor is empty.
'((%construct (lambda () ()))
; This handles messages.
; A message is generally (but not always) the quoted
; name of a procedure, and any arguments passed to it.
; every slot that is a procedure is a message. Note
; that we have to use EVAL to change the symbol (mention)
; to our identifier (use).
(%act (lambda (m a)
(if (and (%in-slots? m)
(procedure? (eval m)))
(apply (eval m) a)
(%no-msg m a))))
(%slots '())
(%get-slots (lambda () %slots))
(%in-slots? (lambda (slot)
(assoc slot %slots)))
; Again, we use eval
(%get (lambda (s)
(if (%in-slots? s)
(eval (list s))
(%no-slot s))))
; Again, we use eval.
; Note that this has a bug. It should also update the
; %slots. This is relatively minor at the moment, because
; the values of %slots isn't being used for anything... yet.
(%set! (lambda (s v)
(if (%in-slots? s)
(eval `(set! ,s v))
(%no-slot s))))
(%no-msg (lambda (m a)
(error (string-append
"no message ["
(object->string m)
"] with args: "
(object->string a)
" slots avail:"
(object->string %slots)))))
(%no-slot (lambda (s)
(error (string-append
"no slot: "
(object->string s)))))))
;; A simple, but useful procedure to mangle the result
;; of an assoc. for instance:
;; (a-val cadr 'foo '((foo bar)(baz blarg))) => bar
;; (a-val cdr 'baz '((foo bar)(baz blarg))) => (blarg)
(define (a-val p k l )
(if (assoc k l) (p (assoc k l)) #f))
;; Merges a primary alist list with a secondary list. As you might expect
;; If a member is in both lists, the primary member will take precedence.
;; The values of each alist are ignored.
;; It does no consistency checking. Bad Jonnay!
(define (alist-merge p s)
(cond ((or (null? s) (not s)) p)
((or (null? p) (not p)) s)
((assoc (caar s) p)
(alist-merge p (cdr s)))
(else
(alist-merge
(append p (list (car s)))
(cdr s)))))Now that we have Mentioned the BunnyObject System...
Lets actually use the thing. You define a class like this:(define point (class
(slots (
(x 0)
(y 0)
(%construct (lambda (cx cy)
(set! x cx)(set! y cy)))
(get (lambda () (cons x y)))
(set-x (lambda (v) (set! x v)))
(set-y (lambda (v) (set! y v)))))))The class macro returns a procedure that constructs objects specified by the arguments that you passed to class. The standard way to do this is to bind that procedure to a name. Like in the point class above.All of the magick % slots can be over-ridden as well. For the most part, users of this class system will be most concerned with overriding %construct. %construct must be a procedure.
One you have a class, you can construct objects like this (using the point example):
(define p1 (point 5 3)). Easy, no? Using the objects is a bit of a stretch for some OO guys, because we are not calling methods but passing messages. Remember that (with an un-modified object) every procedure is considered a message. So to use the methods in our point object :
(p1 'get) => (5 . 3) (p1 'set-x 2) => #unspecified (p1 'get) => (2 . 3) (p1 '%get-slots) => ((x 0) (y 0) (%construct ...) ... ) (p1 '%construct 3 2) => #unspecified (p1 'get) => (3 . 2)Note the passing of the %get-slots message. this shows the basics of the MOP. Also note that we can pass a '%construct message, which would re-initialize the object, in so far as it was initialized by it's constructor.
Conclusion?
As we saw in my other blog post, it is not difficult to build type systems in scheme, and in this one, it is not difficult to build object systems. The melding of the two (type and object systems) seems very natural to some, and is left as an exercise to the reader.
The possibilities of this object system however are pretty wide open. I have a working theory that an open-ended scheme-like object system with first class entities and a MOP would make expressing patterns (as in Gang of Four Design Patterns) extremely easily, clearly, and with brevity.
Listening to:
Rakimou - Plaid (6:01)
Wednesday, August 25. 2004
TinyCLOS
Only works on MIT Scheme (apparently). But looks damn cool.
I want to build my own OO system with a metabject protocol.
Listening to:
Sister Curare (BeatBlender 128k: A late night blend of deep-house & downtempo chill [SomaFM]) - Kid Loco
Sunday, August 8. 2004
MMm... Scheme Objecty Goodness.
Listening to:
Moon Space - Intersperse (6:34)
textures




