Charging Objects in Ruby
timeless.judofyr.net
timeless.judofyr.net
This is a sort of DSL, but it doesn't really give us any more clarity than traditional allow/disallow arrays, does it?
It's this:
+ url("your-url-here")
versus this: allowed << url("your-url-here")
Why is the former more attractive?As a configuration language, it might be.
Building up an object system like this for simple configuration seems excessive to me.
I would do something like this:
c = YAML.load_file 'config.yml'
allowed, disallowed = c['allowed'], c['disallowed']Data is code is data, avoids having to write a parser (or even reuse an existing one), avoids having to resolve the impedance mismatch between the programming language and the configuration language, scales up infinitely in case the user needs more power (which is not that rare, especially for a rules system which is the use case here)
> I would do something like this:
> c = YAML.load_file 'config.yml' > allowed, disallowed = c['allowed'], c['disallowed']
Now you have two superfluous lines in your code and you have not gained anything of value.
> Data is code is data.
This is hardly a case of homoiconicity, is it?
You need that code to load/create rules in any case, but for you it's done from a config file which the user writes. For TFAA, the user loads his data directly into the running system. Therefore the layer you have to setup between the config file and the actual running code (parsing the config file then interpreting its values to translate them into a running ruleset) is superfluous: you can do without it.
Because you know, your "example" YAML parsing is useless in and of itself, it doesn't result in a working ruleset, you skipped the whole part where you feed that data to your code and setup the stuff that actually does work.
TFAA removes the parsing and interpretation from the chain and instead lets the user feed data directly to the running code without layers inbetween.
Because you know, your "example" YAML parsing is
useless in and of itself, it doesn't result in a
working ruleset, you skipped the whole part where you
feed that data to your code and setup the stuff
that actually does work.
Sorry, but that's not actually the case, is it? There is no "business logic" in his example. To actually use the config system he's set up, you need to evaluate Rule#charge for every rule you want to evaluate. In my example, all you could run allowed.include? or simply iterate over the allowed array.____
Church: you can represent data using functions/function application.
Historic example: Church encoding of the natural numbers.
Day to day example: The hash table and the array are in fact pure functions.
____Turing: the input of a machine (data) can be used to carry computations.
Historic example: the Turing machine (duh :-)
Day to day example: from machine language to Coffeescript.Why? And why would you draw that distinction for unary operators but not binary ones? Could it be just out of habit, because you've seen binary operators overloaded to mutate operands before but have never seen that with unary ones?
If the language implements unary operators as regular methods/messages (rather than special forms) why couldn't they be overloaded in the same way binary operators are, as long as it makes sense and reads correctly? (and in this case it certainly does)
I wouldn't -- I think it's wrong for binary operators too.
allow url("your-url-here")
(with reject or something similar for the opposite).You could even drop the url method and simply write
allow "your-url-here"
if you only need to allow/disallow based on URLs.Magic operators are one of Perl's worst features & now we can make them even worse in Ruby by creating our own set of unmaintainable overloaded magic operators. Everyone can create their own private hell to invoke on every other Ruby coder!
Put down teh overloaded magic operators and step away from the keyboard.
:)
Anyway, thanks for the post & for your blog in general; lots of helpful, well written articles.
They are here-and-now solutions that ultimately cost more time and effort in the long run.
Because some Perl coder wanted to type
$>
instead of spending a fraction of a second more and type
$EUID
anyone attempting to use that code forever after needs to address the micro meanings of the syntax itself. There's no expressiveness in the syntax, no way to understand the complex meaning of the operator without a reference.
These operators are dramatically different than basic operators. They require much more context and are much less intuitive.
I know the other side of this argument is that it's akin to learning any other syntax of a language. I can understand the argument and I don't disagree. But personally, I find the maintainability of any code drops off a steep cliff once it starts using magic operators.
Personally, I want to deal with finding solutions to problems, not deal with the esoteric symbols that have been arbitrarily associated with some abstract and complex concept.
I know immediately when I look at Ruby's
Process.euid
what we're dealing with.
Not so much when I see
$<
or is it
$>
hmm... let me go look that up...
Personally, I would have gone with allow/deny instead of +/- but I'm glad the choice is available. It certainly pays off for numeric types or DSLs that involve complex nested expressions e.g. parsing grammars.
My magic vars/operators mix & match was inaccurate, but still appropriate; the point still remains, overloading operators seems risky for long term maintenance if it's not used cautiously.
It's clever & no doubt has its usefulness, but as the author of the article implies, it can be (ab)used. And I guess I hopped off the train right there.
I think I'll take a look at this again tomorrow with fresh eyes & see what I'm missing.
Thanks again for the correction.
The "DSL" part looks like this:
Rules do (
+ url("http://timeless.judofyr.net/*")
- url("http://timeless.judofyr.net/")
- url("http://timeless.judofyr.net/changelog/*")
)