Summary
My previous posting about Ruby generated a lot of noise and very little light -- that is, not much in the way of compelling reasons to learn the language. So I went to a couple of Seattle.rb Ruby users group meetings and spent time with 3 uber-geeks. Now I at least have the beginning of an understanding of what's interesting about this language.
Advertisement
But first, a conference, and what I learned at the PHP meetup.
Conference: Programming the New Web
I've participated in several OpenSpace events and have held a couple of private ones, and they are easily the most interesting, stimulating and educational ways for people to communicate in a conference setting that I've ever seen. If I could figure out a way to do it, I would spend a large portion of my time running OpenSpace events.
I'm going to be in Crested Butte, Colorado until the end of the ski season, and have decided to hold my first public conference during that time. I debated about the whole skiing issue ("what if it looks like a boondoggle to a manager that has to approve it?"), and finally decided that there's no way around it. If people come to the event, they will ski, so we'll open up the best time of the day to let that happen, and organize the conference around it. If you don't want to ski, it's free time.
You can learn more about the conference, register, and buy a T-shirt here.
PHP
The PHP meetup was helpful, but I seem to have generated more questions than answers, mostly because I haven't figured out the best way to use PHP.
I think PHP is what HTML should have been. That is, not just a markup language, but also something to program with on the server side. Things would have been much easier had this been the case from the beginning (of course, had we had a more-standardized and less-heinous language than Javascript on the client side that would have helped everything an awful lot, too).
I really like how straightforward PHP is. You create one file containing both your server-side programming and your HTML markup, drop it into any directory in your URL-space, and it works. At some point there are probably scaling problems that result from mixing logic and presentation, but up to a certain size and complexity it seems easier to manage than the alternatives, precisely because everything is in one place.
I have an account at GoDaddy, which supports PHP, and all I had to do was drop this file anywhere in the directory tree (not just in a cgi-bin or other special directory), and it worked:
Basically, all this does is display the information about the PHP installation by calling phpinfo(), but it gives you the idea of the syntax of PHP, which is familiar if you've used anything like ASP or JSP. You can see the output here.
This brought up my first question. When I signed up for GoDaddy roughly a year ago (basically just to support downloads), I talked to their tech support people who made it sound like they'd be updating to PHP5 any day now. But here it is a year later, and when I sent an email to them I got a reply indicating they had no immediate plans to do so.
This is disappointing because PHP5 is the Cadillac of PHPs, and the one that I think got IBM's endorsement. This version has all the object-oriented stuff in it: classes, interfaces, access control, pretty much everything you've seen in Java with a bit of C++ here and there. Basically, it allows you to build serious stuff without feeling like you've been thrown back into C by using PHP.
But GoDaddy's reluctance to upgrade bothers me. Is this typical; are the major web hosts reluctant to upgrade for some reason?
And does it matter that much, or will I mostly just be bashing data into and out of a database, so perhaps I don't need the fancy features anyway? (Or will I get into it and discover that I could really clean up some of the database-bashing by writing a few classes and saving a lot of work?). I don't know the answer to this; maybe a PHP expert can tell me.
I do know I'd like to do more of it, one way or another, because it's so direct. I've seen that a lot of sites use it and do reasonably complex things, and they seem to keep the complexity under control (ITConversations, for example). And there are tools so that you can easily run PHP locally while you build the site. The best one of these for Windows is apparently XAMPP, which also supports Linux, OSX and Solaris.
One thing I've noticed, though, is that I've gotten very attached to Zope's through-the-web interface. I even looked around to see if there was some way to run PHP code through Plone (the popular CMF built on top of Zope) and it would seem that there is, but I have no idea how well it works.
In the process of looking through the GoDaddy setup screens, I came across something that claimed it supported Python. I tried putting a python script, with the #!/usr/bin on the top, in the cgi-bin directory, set permissions, etc. but it wouldn't run. Has anyone tried this?
I also found something even stranger in the GoDaddy setup screens. There was an option to "enable" Java, which apparently took 24 hours. But no information on what you were actually enabling -- like whether it supported Servlets and JSPs or if you were just getting a standard Java installation (and if the latter how you were expected to use it). If anyone has used this feature, please add a note in the comments; thanks.
Ruby
When I went to the PHP meetup, Ryan Davis happened to overhear and came over to join us. Later we got to talking about Ruby and I voiced my current complaints. He asked me a critical question about what bothered me:
"Is it aesthetics?"
At which point I realized that yes, I had gotten hung up on aesthetics and similar issues. For example, looking at all the "end" statements in Ruby is annoying; I find Python's indentation much more natural. I've noticed that the same people who can't cope with indentation to establish code blocks still indent their code to show code blocks, even if their language uses curly braces or the like. And yes, some of the things in Ruby that look like Perl have made me recoil. But Ryan was right, these were aesthetics, and worth getting over, at least temporarily, in order to see what's really interesting about Ruby.
Ryan described meetings of the Seattle.rb Ruby users group and so I ended up coming to a couple of them and getting some great one-on-one tutorial time with Ryan, Evan Webb and Eric Hodel, all of whom have done some very immersive work with the language. They all live and breathe this stuff, which is exactly what I needed to get some actual insights into Ruby.
My perception is that I've been getting very sketchy information up until now. Bruce Tate's "Beyond Java" book came closest to showing some of the especially interesting things about Ruby (continuations, for example), but most discussions just don't seem to have any of the real meat in them that I found by going to the user group meetings.
The first meeting I went to I focused on some of the bigger, fancier things like continuations and threading. But last night Eric Hodel (who considers himself lucky to have been able to go directly from college into a Ruby-only existence four years ago) went through Ryan's Ruby Quick Reference and basically gave me a walkthrough of the language.
The result: there are definitely some very cool things here. I don't know why Ruby fans don't talk about them, but they're here. Whether or not I end up using Ruby for anything, experimenting with some of these things will definitely change my perspective on OO. In the past, I've found that after learning Java I could go back and write C++ that I hadn't been able to think about before. Learning Python affected both my C++ and Java code. And some of these Ruby concepts will certainly improve what I can do with the other languages.
case
In the category of basic language improvements, consider the case statement. Python doesn't have a switch or equivalent, the argument being that it's effectively a sequence of if-elses. Which is not too bad in Python; the language is such that you don't end up typing much more than you would with a builtin switch. But C, C++, Java, and C# (with the exception of string values) all insist on using integral values in their case statements. The reason has traditionally been "efficiency, and because C did it that way."
The problem with the efficiency argument is that if you want to match anything other than integral values -- and you often do -- then you must write a bunch of other code that doesn't use the switch statement, which will end up taking at least as much CPU time, and probably more, than if you could have any kind of case (yes, C# lets you have string cases and that helps, but it's not a general solution). And of course it ends up taking significantly more programmer time, which is the actual scarce resource, anymore.
In Ruby, the case statement can match with any object. It uses the "===" method in Object to perform the match, which can be overridden for your own classes. Since everything is really an object in Ruby, this includes any type of literal, regular expressions, and object types. Here's an example (comment syntax is like Python's):
# File: Case.rb
def f(x)
case x
when "foo"
puts "#{x.class} foo"
when /\d+.*/
puts "mixed: #{x}"
when String
puts "string #{x}"
when 1000..1500
puts "between 1000 and 1500: #{x}"
when Fixnum
puts "Fixnum #{x}"
when Object
puts "object #{x} #{x.class}"
end
end
f("foo")
f("123abc")
f("bar")
f(1066)
f(1)
f(1.1)
The output for the above program is:
$ Ruby Case.rb
String foo
mixed: 123abc
string bar
between 1000 and 1500: 1066
Fixnum 1
object 1.1 Float
You can see that the syntax is kind of backwards; case is the equivalent of switch in other languages, and the branches are delimited by when.
Regular expressions are similar to Perl's in that they can be delimited by '/' (but you can also create more conventional regular expression objects as you do in Python). One way to insert a variable in a string is to use the '#{}' as you see above.
String, Fixnum and Object all represent class objects. Class types are inherited from Object and thus also have '===' defined, so they can be used in a when comparison.
Implied self
One thing I've never quite bought into in Python is the explicit self. This is supposed to be because "explicit is better than implicit," but if that's true then why is it explicit in the method definition:
class PythonClass:
def method(self):
self.x = 1 # Create a field x if it doesn't exist
but implicit in the method call?:
pc = PythonClass()
pc.method()
Ruby follows C++, Java, C#, and most other languages in making 'self' or 'this' implicit everywhere. It appears only if you need it. Since you always have self in a method, this makes more sense to me (albeit it ranks as a small nit).
Here are some examples of classes in Ruby:
class MyClass
def method1(arg1) # Like C++ & Java, self is implicit
puts arg1
self.f() # ... but still available
end
def f()
puts "in f()"
end
def method2(arg1, arg2)
puts "#{arg1}, #{arg2}"
end
# Constructor:
def initialize(arg)
puts "In constructor with arg #{arg}"
@field = arg
end
end
obj = MyClass.new("yo!")
obj.method1("hi")
obj.method2("hi", "howdy")
p obj.class
# Inheritance:
class MyClass2 < MyClass
end
# Constructor with arguments automatically passed through:
obj = MyClass2.new("yow!")
class MyClass3 < MyClass2
def initialize(arg)
super # Passes argument list through to base constructor
super(arg) # Explicit argument passing
end
end
obj = MyClass3.new("bing!")
p obj.class.ancestors # Simple reflection
Here's the output:
In constructor with arg yo!
hi
in f()
hi, howdy
MyClass
In constructor with arg yow!
In constructor with arg bing!
In constructor with arg bing!
[MyClass3, MyClass2, MyClass, Object, Kernel]
Class names must start with a capital letter.
def starts a block (without you having to say begin). initialize() is how you create a constructor, and super can be used to explicitly call the base-class constructor. Inheritance is indicated with <.
The '@' sign is used in place of Python's self. to indicate a field. Although it does make me think of Perl, the brevity is nice.
At the end, note the use of simple reflection, which just calls intuitively-named methods. The p evaluates and prints the result; it's generally used for debugging.
Reflection
As seen above, this is quite intuitive. For an object, for example, you can say:
p obj.class
p obj.methods
For a class, you can find the class methods by saying:
p MyClass3.methods
There's lots of other stuff like this, which you can discover by poking around.
Open classes
Here's something quite different. You can modify an existing class. Put another way, classes are not "closed," and anyone's class can be extended.
class Array
def dump
for x in self
puts x
end
end
end
a = [1,2,3,4,5]
a.dump
Array is a built-in class for the object created for square-bracketed lists. But in the code above it looks like I'm defining my own class called Array. But I'm able to call dump when I create a built-in Array using square brackets. The output is:
1
2
3
4
5
Thus, you can easily add to any class. This eliminates the need for a significant number of tools created for other OO languages that modify existing classes: multimethods, AOP (to some degree, anyway), and even the visitor pattern.
Class Variables and Class Methods
Class variables in Ruby are not too different from the way they work in Python, but they are special denoted in Ruby with a leading '@@' symbol.
Not a huge issue, but Python went through some gyrations to produce class methods, ending with the @classmethod decorator. In Ruby it doesn't require any particular meta-text; it's consistent with everything else:
class ClassVariablesAndMethods
@@classVariable = "initialized"
def ClassVariablesAndMethods.classMethod()
p @@classVariable
end
end
x = ClassVariablesAndMethods.new
x.class.classMethod()
ClassVariablesAndMethods.classMethod()
Note that you can't call a class method directly through an instance; you must call class first.
Prototyping and singleton classes
You can take a single instance and add methods to it:
class X
def f()
puts "f()!"
end
end
x = X.new
x.f()
def x.g()
puts "g()!"
end
x.g()
y = X.new
y.f()
# y.g() # Undefined method
The method g() is only added to x, which you can see because y, created after g() is defined, doesn't have a g() anyway.
You can do something like prototyping with Python, but I don't think it's as obvious.
A singleton class is a slightly more formal way of accomplishing the above:
class X
def f()
puts "f()!"
end
end
x = X.new
class << x
def g()
puts "g()!"
end
end
x.g()
Modules and mixins
One thing that has been requested, largely by the folks who create Zope, is interfaces in Python. Since Python is a dynamic language and interfaces tend to be more of a compile-time thing, I've almost seen the value sometimes, but it's never pushed me over the edge on the feature.
Modules look like they might be the dynamic language's answer to interfaces. Syntactically, a module is a class with the keyword module instead of the keyword class. So, unlike a Java interface, you can have code in a module (and, unlike Python, there's no pass equivalent in Ruby, so you're basically forced to provide code for all method bodies). Like a Java interface, you can't instantiate a module, you can only use it as a "mixin," to paste in both interface and functionality into another class. So a module distributes a common interface and default behavior across class boundaries. Later, if you change a method definition in that module, it will change in all classes that include that module.
Here's an example:
module CommonStuff
def f()
puts "f()"
end
def g()
puts "g()"
end
end
class Mixed
include CommonStuff
def h()
puts "h()"
end
end
def takesCommon(arg)
if not CommonStuff === arg
raise "You have to pass in a CommonStuff!"
end
arg.f()
arg.g()
end
m = Mixed.new
m.h()
takesCommon(m)
begin
CommonStuff.new
rescue Exception => e
puts e
end
begin
takesCommon([])
rescue Exception => e
puts e
end
As you can see, a module is written exactly the same way as you write a class, but the way it is used is constrained. If you try to create a new object, it tells you that there is no new for CommonStuff. This attempt uses exception handling (the rescue clause) to prevent the program from aborting; you can also see an Array object passed to takesCommon() at the end. The output is:
$ Ruby Modules.rb
h()
f()
g()
undefined method `new' for CommonStuff:Module
You have to pass in a CommonStuff!
Exception handling: retry
As you can see in the example above, basic exception handling is fairly consistent with other languages with the exception of the structure. Any begin-end block can include an exception handler, which uses the keyword rescue instead of Python's except or Java/C++'s catch. If you want to do something with the exception object, you extract it into a local variable using ==>. To throw an exception, you use the raise kewyord just like Python. There's the equivalent of a "finally" mechanism using the ensure keyword.
Unfortunately there doesn't seem to be an assert as there is in Python and Java. Effectively this is syntactic sugar, but I still like it.
Instead, in the syntactic sugar department, there's retry, which appears at first to be an attempt to resurrect the idea of resumptive exception handling. This means that the exception handler gets a chance to catch the exception and then continue at the point of failure. Resumption was tried for systems in the 60's, but failed, most likely because of the resulting high coupling between the various points an exception could be thrown and the points where it was caught.
If you use the retry keyword, on the other hand, the point of execution goes back to the beginning of the block that threw the exception, so it's basically the same as if you set up a while-not-succeeded loop. Here's an example:
count = 0
begin
puts "at beginning"
if count == 0
count = count + 1
raise "First time through"
else
puts "second time through"
end
rescue Exception
puts "In handler"
retry
end
Here's the output:
$ Ruby Retry.rb
at beginning
In handler
at beginning
second time through
You can see that retry sends the point of execution all the way to the beginning of the block.
Comment Documentation
Ruby also has a standardized comment-documentation system and a utility called rdoc to build help from the source code. It seems likely that this was inspired by Java's, as a number of other systems appear to have been. But there are some situations where it's very nice if rules are blessed by the language so that people are less likely to get stuck arguing about them (another reason I like Python's indenting -- all source code looks the same, so I don't have to use brain cycles sorting it out). Although the resulting Java documentation isn't always the best you could hope for, I consider standardized comment-documentation one of the number of contributions that Java has made to programming world.
Conclusion
This is just a few of the features I found interesting. Note that I haven't even touched on blocks (although the uber-geeks gave me plenty of coaching on these, I haven't internalized them yet) or continuations, threads and processes. All these look quite interesting as well, but require more time and space than I have right now.
Am I going to drop Python for Ruby because of the very interesting way that Ruby does things, and because of some of the additional features it has? No -- I have a great deal invested in Python and am very productive in that language. Also, there are places where Ruby still has a kind of early feel to it -- the libraries are not nearly as filled-out as Python's, Ruby can seem slow at times, and the error checking can be primitive (for example, I left off an end statement on a method in the middle of a file, and all it would say is "syntax error" for the last line of a file -- in a larger file that could be a very hard bug to find).
However, for starters, I'll now be looking more closely at Python to see how it handles some of the less obvious issues, especially classes and reflection. I've also got a foothold in Ruby so I will be learning more about it (and going to more user group meetings if I get the chance). And for me, most importantly, I've got some new ways to think about object-oriented problem solving, which I have always found to come in handy.
Good resources:
http://www.rubycentral.com/book The "Pickaxe" book; this is for an earlier version of Ruby and there aren't any diagrams, but it's still reasonably useful.
www.zenspider.com In particular the Ruby Quick Reference; there are missing elements but it's quite helpful.
onestepback.org Contains numerous tutorials and articles to help you learn Ruby.
Some minor. I'm a little confused about your discussion of "prototypes". Io like prototying requires efficiently clonable objects not just adding attributes after object creation. Io clones share attributes with the prototype unless they get modified. So what is your point? Has Ruby any language specific prototype semantics or does it just use a deepcopy mechanism to create new instances from existing objects?
> Has Ruby > any language specific prototype semantics or does it just > use a deepcopy mechanism to create new instances from > existing objects?
I think the term prototyping here is confusing the issue. It's not the prototype pattern in GoF (as I understand it.) It's a completely different animal. Ruby allows you to create a new method and 'add' it to an instance. This method doesn't need to exist in the Object's class.
"puts" is "print" with line feed ("\n"), but Ruby has print too.
It's a little weird compared to the other languages, but it's handy and short. I prefer it to the "Write" and "WriteLine" anyday. By the way, I wish all the programming languages had the convention of starting method names with a lower case letter, and I know in C# they start it with upper case (wish they didn't).
puts is not a statement (unlike in Python, which makes Pythons print somewhat inconsistent) it is a plain Kernel module method. There is also a print method in Ruby which can format its output. Since everything in Ruby is an object, You could also write: 1.display (which takes stdout as a default parameter), very Smalltalkish :D
Not to be a smart-aleck, but you could have learned these things from the pickaxe book. Sounds like you didn't make much effort to study it before criticizing Ruby.
(And no, I'm not a Ruby fanboi. I'm a Lisp fanboi, if anything.)
> ... I don't care for > languages that 'make it easy' to do X. They generally > make it easy to do X in a aubstandard way.
Just because you misused the feature doesn't make it a bad idea or "substandard". This is just another feature you can either exploit or shoot yourself in the foot with.
Err.. Bruce.. None of the features you're describing are really new. You may want to take a look at Smalltalk, which had all this for ages (and more - Google for Traits), has a lot less syntactic stuff getting in the way than either Python or Ruby, typically comes with top-notch IDE's, and it's fast as well. Smalltalk is half of the reasons I never learnt Ruby; the other half matches pretty well with your observations - aesthetical and the fact that Python, which I knew before Ruby, pretty well fills the same niche.
There are various implementations. Squeak is the most "open-sourcy" one, Cincom VisualWorks has a non-commercial version that includes a fairly extensive Smalltalk tutorial.
One warning: you may get hooked. Which means that programming in most other languages becomes boring, frustrating, and worth avoiding in general :-)
If you're interested, ping me (if you don't have my mail, Bill V does).
In retrospect this got longer than you were likely hoping for...
"The PHP meetup was helpful, but I seem to have generated more questions than answers, mostly because I haven't figured out the best way to use PHP."
Don't think anyone really has - or there are many different opinions about the way to go there. It starts with where you stand on Apache's role in PHP - is Apache PHP's front controller or not? Let's not go there...
For something more complex, perhaps the best example of how to do "sane" PHP is MySQL's Eventum bug tracker (http://dev.mysql.com/downloads/other/eventum/) - it's not the most minimalistic code (there are smarter / more efficient ways to do PHP) and it's PHP4 style but it does represent a solid design / structure which should remain more or less consistent as the code base grows.
It's also worth staying away from things carrying the label "framework" in PHP, at least until Zend releases it's framework. That's not to say there aren't some good ones but it's a path to never ending projects. To be blunt, there I can't think of any successful Open Source PHP application that used a third party framework (some have had success through building their own though, such as eZpublish - http://ez.no).
"This brought up my first question. When I signed up for GoDaddy roughly a year ago (basically just to support downloads), I talked to their tech support people who made it sound like they'd be updating to PHP5 any day now. But here it is a year later, and when I sent an email to them I got a reply indicating they had no immediate plans to do so."
One issue there is PHP5 hasn't really become "stable enough" until 5.1.x. OK - in an environment you control (your own server), 5.0.x is probably OK but bigger shared hosts, I imagine, research this in more detail and will probably hold back if there is any indication that there are any major stability / security issues.
If you really want PHP5, it may be better to use a smaller host that specialises in it and with your Python requirements, it may even be better to have something like a "complete server" you control. These days this is less daunting than you might expect - software you need is typically pre-installed and web based tools like Cpanel and plesk simplify sysadmin. There's a maze of people offering this kind of stuff but one opinion I'm personally liable to trust is Jeff Moore's who has to rely on this type of service for his living (http://www.procata.com/hosting.php - http://www.procata.com/ - http://www.procata.com/blog/).
"And does it matter that much, or will I mostly just be bashing data into and out of a database, so perhaps I don't need the fancy features anyway?"
It's worth being aware that PHP4's object model wasn't that bad, the only significant pain (until you get used to it) that objects were passed around, by default, as a copy not a reference.
Object-wise what PHP5 provides is really a stricter object model, closer to Java's but without radically changing the paradigm or giving you much that wasn't already "doable" in PHP4, at least by convention. For the long answer there, my attempt is here: http://www.sitepoint.com/print/coming-soon-webserver-near .
Would say PHP5 is probably more interesting for new functionality / extensions. In particular XML support in PHP4 means pretty much only a SAX parser, if you know you have "XML requirements".
"One thing I've noticed, though, is that I've gotten very attached to Zope's through-the-web interface. I even looked around to see if there was some way to run PHP code through Plone (the popular CMF built on top of Zope) and it would seem that there is, but I have no idea how well it works."
From a quick glance at that, it's basically a wrapper round the PHP binary, effectively executing a PHP script as if it were on the command line via Pythons popen2. I imagine that's likely to be fairly slow but it's probably easy to setup.
Side note;
"You can see the output here."
You may want to be careful about posting the output of the phpinfo() command. Technically its just information and there's the whole security by obscurity thing but it does help potential attackers, by disclosing various useful bits of information about where you are on the server's filesystem, which might have been harder to find otherwise.
As to why most hosts haven't upgraded to PHP5, I suspect it's because fundamental language changes (e.g., pass-by-reference is now the default instead of pass-by-value) break the hell out of a lot of existing code.
Too bad PHP5 is _still_ a nasty and inconsistent mess in most ways.
Flat View: This topic has 43 replies
on 3 pages
[
123
|
»
]