Hacker Timesnew | past | comments | ask | show | jobs | submitlogin

Well, the alternative could be more like Python.

Python supports both object methods and functions. Ruby only supports object methods. However, Ruby does allow you to define methods outside of an object - these become methods of the Object class. As such, any methods defined like functions become changes to all objects since they inherit from Object.

Python is also very explicit about its namespacing and imports. In Python, you should always be able to know where any name you're using is coming from. In the example from the article, 'to_yaml' is added by requiring rubygems. That isn't so apparent (in the way that 'gem' being added by requiring rubygems would be apparent). Names are reused all the time. Names sometimes aren't related to their package in an apparent way.

With Python, you have three options (one of which Python programmers will flog you for): from package import name, name2 import package from package import * -- should really only be used in an interactive python shell and not in programs

In the first one, you could then call name(something) or name2(something). BUT you can see exactly where those names are coming from -- package. Unlike to_yaml which could come from wherever, it's explicit that it comes from 'package'. In the second one, you can do package.name(something) and the like. Similarly, you can see that it comes from 'package'.

Ruby is really flexible, but I would argue that the author is correct in assessing that the ability to modify all objects implicitly is a bad thing.



Also, information hiding is rumored to be important in real software. Ruby gives you the ability to nominally "hide" data, but it's trivially easy for any later programmer to violate the encapsulation. It's DHINO programming -- Data Hiding In Name Only.

Not only isn't this considered a bad idea in the Ruby world, but it's actually a common technique. Use a third-party library, and you may well be re-writing core parts of the language. Sound good? You can't prevent it.

I like parts of Ruby; it's a fun language. But the author is right -- it's not a language for large projects, or projects with more than a few programmers. My alternative to Ruby would allow the fast-and-loose stuff that makes Ruby fun, but also give you a way to make classes immutable and guarantee data-hiding and encapsulation when you need to do real software engineering.


DHINO is not really a problem. We are missing true private methods and attributes in Perl, but it doesn't stop us from writing large maintainable programs. The rule is that you don't call methods that start with an underscore from outside of the class that defines. Sure, you could, but we don't.

Java's strict hiding has made my life more difficult in a number of situations. Recently, I needed to change the way URLConnection worked. I could have fixed the problem by changing a private attribute, but no no, you can't do that in Java. Instead I had to write 3 additional classes to set everything correctly for me. This is not good software engineering practice, it is a waste of my fucking time. (This was a one-off script, not a large program. Yes, I know, too many hacks and your app falls apart... but with Java you end up with an app that is falling apart AND a shit-ton of useless code to wade through.)

Anyway, if you think Java-style programming is "real programming", that's great. We don't need to work together, so you are free to waste your time however you like.


Out of curiosity, what attribute could you not change in URLConnection?

I don't consider Java-style any more "real" as any other, but if something is private in a class and doesn't provide methods for modification, there is either a reason for it, or it is poorly coded... If you mess with private variables, you could be jacking up the way the class works and not know it. If you instead write 3 additional classes, you're at least ensuring your URLConnection state is consistent.

> Anyway, if you think Java-style programming is "real programming", that's great. We don't need to work together, so you are free to waste your time however you like.

Sounds like you're a lot of fun to work with in any language.


I don't like the "real software engineering is big heavy Java projects" thing (aka "bondage and discipline"). Plenty of "real software engineering" has been done to great effect with dynamic languages.

Here's a random idea: rather than strict locks, perhaps transparency would be a good approach. Load up a third party library, and you get informed that it's fiddling with things, and can then find out what. Maybe it really does need to fiddle with things. Or maybe in a given situation, fiddling with things is the most efficient way of accomplishing something.


"rather than strict locks, perhaps transparency would be a good approach"

Transparency is a great, but you still need rules when you're working with any code that's bigger than what you can hold in your head at any given time. Doubly so when you're working with a team.

Programming in a team is hard in the same way that taking care of a room full of toddlers is hard -- obviously, you want everything out in the open, but you also want to hide the sharp objects, put caps over things that can shock, and otherwise make the big, transparent room as soft and bouncy as possible. You need locks on the cabinets full of poisonous chemicals. Obviously, the locks need to be open-able, but only in special situations, and certainly not by nosy toddlers.

(Before you criticize me for being paternalistic, realize that I include myself in the "toddler" category. I much prefer code that's written in a defensive style, because it's easier for me to maintain later on -- and that's where any programmer spends the majority of his time. I've developed this attitude because I know that I'm stupid, not because I'm arrogant.)


While I agree that writing code defensively is the way to go when a team is involved and/or you plan to have to maintain the code for years, I don't think it's the language's job to take away all the 'sharp objects'.

Programmers should be smart enough to know what is sharp and how to avoid it when needed, and they should collaborate enough that no one can sneak through sloppy code.

I'd rather have the ability to do certain things and rarely use it than not have the ability at all. If other programmers are constantly doing really dumb things, don't use their code in your projects.


Even in strict languages, there's rarely anything that absolutely prevents you from doing what you need to do; they just make it harder to do bad things accidentally. Languages like Ruby actually make it harder to do things correctly, and constantly tempt you with hideous shortcuts.

Again, I view programming teams as groups of nosy toddlers. Thus, any sentence beginning with "programmers should be..." is wrong by definition. Programmers are people, and people make mistakes. People also get lazy, try to take shortcuts, and therefore make even more mistakes. The way that people minimize mistakes is by setting up systems that make it harder to do wrong things. This is a fundamental tenet of engineering.


Yea, it is fairly trivial to enable logging every time a method is overridden or added to any class in ruby. Dynamic code inspection like in JavaScript would be pretty cool, too.


But wait, there's more!

You can achieve immutability in ruby: http://scie.nti.st/2008/9/17/making-methods-immutable-in-rub...

You can achieve data-hiding if you really need to in ruby using the same techniques you use in JavaScript.


Dear god in heaven, that's ugly.

I sincerely hope that people voted that up out of a deep appreciation for dark, ironic humor.


"Can't" is very different from "can, but is very ugly."

Most of the "problems" with Ruby that are often quoted are the result of bad programming and management decisions. Ruby puts a lot of power into the hands of the programmer (not as much as IO [the langauge]) and with great power comes...flexible re-factoring! Thankfully, it should be easy to re-factor the offending code, send a note to the programmer about what you changed and why and chalk it up to a learning experience.

Sufficiently typed languages make re-factoring very easy.

If you are having a hard time debugging your Ruby, you are doing it wrong.

As for ActiveSupport's littering of Object, meh. Rails really changes Ruby. If you stick to the Rails way, then it shouldn't cause a problem in Rails projects, and it isn't a problem outside of rails projects. Don't like it? Well, there are plenty of good Ruby alternatives for the web (Sinatra!!! Also, merrrrrrrrrrrrb,) and you don't have to have everything in one app.


Three things:

1) If ugliness isn't a concern, I can do everything I need to do in C++. Arguments in favor of ugliness don't get a pass just because they happen to support your favored language.

2) What does "sufficiently typed" mean? This sounds suspiciously like a tautology: "re-factoring is easy when you've written code that's easy to re-factor."

3) The "you're doing it wrong" response is zero-content. Prove that we're doing it wrong.


1) I never said it wasn't a concern. In fact, I emphasized the important difference between can't and what translates to "shouldn't" in most circumstances.

2) I apologize if i was vague. I was trying to be concise. The nature of a "Type" is not a fixed thing. Some languages are said to be strongly-typed, some are said to be weakly-typed. Some languages are type-safe, others are not. If we were to simply draw a line and suggest that one end approaches absolute Typeness and the other approaches absolute untypeness, then sufficiently typed were to mean the lowest point along this line that supports the goal. In this instance, we are talking about re-factoring. So, you may say that my argument sounds circular: "refactoring is easy when you use a language in which refactoring is easy." However, the important point that I am making is that ruby's amount and implementation of Type is what facilitates its ease of refactoring. In particular, I have found its features of reflection, its implementation of polymorphism ("duck typing"), and its hooks into the Type system (look at Object, Module, Class and Method for more info) to be particularly helpful. Also, because there is not too much typedness, we don't have to worry about sending data of a new/different type to a function as long as it satisfies the assumptions that the function makes about it (which can be inferred by a number of techniques.) Delegator and Forwardable are also pretty nifty. One of the least appreciated tricks is calling .dup on an instannce of Class (that is, an object that represents a class definition, not an instance of a class,) but thankfully it is rarely necessary. -- You need to use it when you want to a) inherit from Foo and still b) have 'super' refer to the implementation in Foo's parent class.

3) I suppose the above might give some clues into my favorite debugging techniques. If you'd like me to go further in depth, contact me via email, my name @gmail.com I didn't think the details were salient to this discussion.

Edit: c++ makes some of the worst decisions wrt supporting the notion of Type, in syntax and in features. That being said, Bjarn is still one of my favorite authors and learning C++ was a great entre into OO for me.


"Strong" and "Weak" are not useful nomenclature.

What To Know Before Debating Type Systems: http://www.pphsg.org/cdsmith/types.html


That's a great article. I will attempt to use more precise terms in the future.

To clarify my previous post: I intended "strong" typing to mean increasing restriction of what you can do in order to provide more guarantees about runtime behavior, and "weak" typing, the opposite.


So you really meant "static" and "dynamic", referring to type-checkers, which are completely independent of "strength" (which is commonly used to imply soundness).

Ironically, all dynamic type-checkers are more sound (by definition, they check all types at runtime) than most ancient static type-checkers that just do naive structural compile-time checks, allow casts, and mainly exist to specify size.


No, how did you infer that from what I wrote? A dynamic type system, as you pointed out, may be better at enforcing restrictions than a static one. The notion of 'strength' that I was talking about is separate from the enforcement mechanism.

Some questions that pop into my mind when i consider type systems are:

1) What are the criteria that a datum must fulfill in order to be considered of type Foo? 2) How is this to be determined? 3) Why would I ever need to know this?

1 & 3 impact the strong-weak continuum, number 2 does not. The less that type impacts allowed (at run or compile time) operations (#3), the weaker the type system. Note that you often can implement the manipulation of metadata in your language if your language does not already provide these features.

As for #1, the more stringent the criteria, the more 'strong' the given language's type system. A very weakly typed language may have no mechanism of checking for type (in such a language, the type system may only be used internally to the object for things like method resolution.) The criterion for being of a particular type may be 1) the datum supports all operations defined for a particular type 2) the datum supports all operations defined for a particular type that are used in the current execution scope 3) the datam has the name of the type in a list of types that it claims to be 4) the datum is at least as large as the size of the given type .. and many more ..

In the context of re-factoring, the less frequently type is checked and the less rigorous the criteria when it is checked, the more opportunity for type substitution (and thus more flexibility) we have.




Consider applying for YC's Summer 2026 batch! Applications are open till May 4

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: