9 ms·
The Building Blocks of Ruby
- subwindow 17y agoBlocks are also an extremely powerful tool when it comes to building DSLs, and if done correctly are a great alternative to complicated options files/hashes/etc. Rails' routes and config/intializer come into mind.
- chromatic 17y ago> Blocks are also an extremely powerful tool when it comes to building DSLs... I can't read this as anything more insightful than functions are also an extremely powerful tool when it comes to building APIs. I first used Ruby in 2000. What am I missing?
- jamesbritt 17y ago"What am I missing?" Nothing. The emphasis on DSLs is distracting. Worse, it may lead people to think that certain coding techniques are only used when creating a "DSL".
- Vitaly 17y agoruby block syntax is clean, elegant and nest-able which allows creating good looking DSLs. foo do bar do .. end baz 123 end now try to do the same with some other language which supports something-kind-of-like-ruby-blocks but with a different syntax. it will not look nearly as good, so in those languages instead of creating DSL people usually implement some kind of config file format instead. or just use XML :) if you'd have to use 'lambda' to define a block for example, it would make a much worse DSL with lots of extra syntax noise.
- chromatic 17y agoThe lack of punctuation characters makes this a DSL and not bog-standard Ruby code? > ... instead of creating DSL people usually implement some kind of config file format instead. or just use XML. Writing a grammar or a parser means you haven't created a DSL? Unless by "DSL" you mean "Ruby syntax and Ruby semantics with symbol names chosen by the programmer", I have no idea what you mean by "DSL".
- jimbokun 17y agoHow about this: Ruby's syntactic flexibility and semantic model mean that many cases handled by a change to the language spec or delegating to another language (XML config files, for example) can be cleanly handled in Ruby itself. The end result is often something that looks like a little language for a specific task, such as Rake, Rails, etc. (I'm not a Ruby programmer, so I hope those are good examples.) So with Ruby, there are very few cases where writing a custom grammar or parser is necessary. Ruby's flexibility gives you the ability to do things that might require a custom grammar or parser in other languages.
- draegtun 17y agoHowever even Jim Weirich doesn't like Rake being called a "DSL". I think Piers Cawley was dead on by calling things like this a Pidgin refs: * http://www.infoq.com/interviews/jim-weirich-discusses-rake http://www.infoq.com/interviews/jim-weirich-discusses-rake * http://www.bofh.org.uk/2007/08/08/domain-specific-pidgin http://www.bofh.org.uk/2007/08/08/domain-specific-pidgin
- flogic 17y agoCommon usage of DSL seems to mean an API whose usage looks more like a secondary language rather than the host language. At least that's my interpretation. A case example would be jQuery. Which while it is JavaScript, idiomatic code using jQuery isn't written in the same style as traditional JavaScript.
- tptacek 17y agopedantic: Digest::MD5.digest(x), not Digest::MD5.hexdigest(x). If humans aren't reading it, don't convert it to hex.
- kscaldef 17y agoI don't feel like any of these are particularly compelling examples, particularly if you are familiar with more languages beyond Java and Python which are used for comparison in the article. In both the file handling and mutex example, blocks seem to be serving as a substitute for proper lexically scoped variables. In Perl, for example, we would use a lexically-scoped file handle to ensure the file is closed when the variable goes out of scope. The same technique is the standard way of implementing mutexes in C++. As for respond_to, I've never understood how it wasn't just a baroque rewriting of a case-statement, but perhaps someone can enlighten me as to why blocks are superior in this situation.
- wycats 17y agoThe problem using a case statement is: what are you casing on? In order to properly do content negotiation, you first need to know all of the possible provided formats, so you can negotiate against the "Accept"ed ones. In order to do it with a case statement, you'd have to repeat the list of provided Mimes: once so the content_type method knew how to negotiate, and then once for each switch in the case statement. Using a block here allows us to make a native'ish case statement that eliminates the duplication.
- kscaldef 17y agoThanks you, that does make sense. Can I suggest adding that explanation to your article so it's clear that the example actually does do more than simply switching on a singular requested format? (I'm actually curious in practice how often the "Accept" header is really used to specify the format, and how often the format is simply coming from the extension on the requested path. I've only ever encountered the latter case, which I why I was thinking of the format as a single-valued variable you can switch on.)
- dmnd 17y agoIt's true that lexically scoped variables can replace closures when all you're using closures for is to wrap a section of code in starting and ending sections. But blocks are more general purpose than this. Have a look at the clean API that blocks have enabled in Ruby's Enumerable module, for example. Once you're using blocks/closures, why not use them to acquire/release resources as well?
- adelevie 17y agoYehuda Katz is always able to remind me that there is so much Ruby I don't know.
- j_baker 17y agoI've always felt that blocks were Ruby's gimmick. They're neat, and I'd like to have them in Python. But I get the feeling that this is just a bikeshed issue. People show them off because they're easy to understand. There's absolutely nothing wrong with that. However, I get the feeling that they're not Ruby's strongest feature. Python's coolest features (metaclasses, descriptors, and other things) wouldn't make sense if you'd just read a blog post on them. I suspect Ruby is the same way.
- jamesbritt 17y ago"However, I get the feeling that they're not Ruby's strongest feature." Perhaps the strongest feature is the underlying architecture that allows for blocks, the general idea that behavior is a data type you can pass around. And since you can alter the properties of objects at different levels (instances, classes, meta-classes), you can alter not just the more conventional values (int, strings) but an object's (and system of objects) very nature.
- Scriptor 17y agoVery true, I think Ruby's metaprogramming is an example of the kind of strong feature you were talking about. I don't use Ruby, but I remember reading about it in the Poignant Guide and being really impressed at how it can make code look very different, basically one way to make a DSL.
- bad_user 17y agoIt's not a bike-shed issue. Surely the other features are great, and Python has lots of powerful abstractions. But once you have closures with a light-weight syntax, as Ruby has, the APIs start to look a lot more different. For example, take this example ... v = [ x for x in collection if x % 2 == 0 ] for item in v: print item In Ruby the equivalent would be ... collection.find_all{|x| x % 2 == 0}.each do |item| puts item end Yes, the Python example is elegant, but Ruby doesn't need extra baked-in features like list-comprehensions. It doesn't need a bunch of other features as well, like generators, or generator expressions, or with statements. There are a lot of PEPs in Python that cover use-cases for Ruby's blocks, trouble is there are still use-cases that aren't covered. Guido is partially right though ... adding blocks in Python wouldn't be pythonic because blocks wouldn't be orthogonal with lots of other Python features. They should've been added from the start, and now it's kind of late.