Friday, December 22, 2006

Ruby: Constant values

I generally use constants in Ruby for the following two situations: Markers or Constant values.

Markers are used as a standard for comparison.
module CreditCardTypes
Visa = 0
Mastercard = 1
end
Markers are initialized to a value; however, that value is unimportant as long as it is unique. Markers are generally used in an application within conditional statements.
case card.type
when CreditCardTypes::Visa then VisaLuhnValidator
when CreditCardTypes::Mastercard then MastercardLuhnValidator
end
Note: you can also use symbols as markers, but I prefer constants. This preference is based on the fact that if I mistype CreditCardTypes::Vissa it will fail fast; however, if I mistype :credit_card_type_vissa, I will get a possibly hard to find bug.

Constant values are global values that should never change during the life of your application.
module MathValues
PI = 3.14
end
Constant values can be used throughout applications to ensure that the same value is consistently used.
circumference = circle.diameter * MathValues::PI
Based on these usages, I'm a bit concerned about some behavior I recently found.
irb(main):019:0> module MathVariables
irb(main):020:1> PI = 3.14
irb(main):021:1> end
=> 3.14
irb(main):022:0> module MathVariables
irb(main):023:1> PI = 3.14159265
irb(main):024:1> end
(irb):23: warning: already initialized constant PI
=> 3.14159265
Warning? This means anyone can redefine my constants at any time? Did I do something wrong? Does anyone else think this is dangerous?

17 comments:

  1. Anonymous4:14 PM

    Dangerous? Yes. It's a very, very sharp knife. That's why there's a warning when it happens.

    There's nothing out and out wrong with it though. What is a constant except a variable that we agree not to touch?

    ReplyDelete
  2. I'm not sure I agree. I expect my constants to be, well, constant.

    ReplyDelete
  3. Anonymous5:41 PM

    You'll love this one :

    CONST_VAR = [1,2,3]
    CONST_VAR.push 4

    Not even a warning :-p

    ReplyDelete
  4. Anonymous6:20 PM

    CONST_VAR.freeze should help for that.

    ReplyDelete
  5. Remember that Ruby is designed for giving maximum power to professional programmers. It is not designed to give armies of morons an opportunity to create some kind of reliable code (what Java is for). So, well, if sometimes you really need to do something dangerous (kinds of goto, one line commands, redefine methods/constants, rough fixes and workarounds) you have a total ability to do this. There are no stupid restrictions just because of their potentional danger. This is by design, so this is a feature, not a bug :)

    ReplyDelete
  6. Actually, the constant part is the reference to the object, it should not be changed, but the interpreter will just warn you.

    It is ok, to change the object that is referenced by the constant.

    After struggling with "final" stuff in Java, I really prefer having control and not letting the designers of the language impose useless restrictions.

    ReplyDelete
  7. This irked me somewhat when I first walked up to Ruby. It was a remnant of my static type brainwashing. Three things mitigate against this being a real problem. 1. The warning. You could argue it should be an exception, but in reality, how would that be any different in the case where some other programmer silently caught the exception? Which brings me to number 2) Don't work with morons. Only morons silently catch exceptions and continue and only morons change constant values used in that way. Which brings me to 3) Neither of us is a moron, so you'll be happy to know that anecdotally this has never happened to me. It's really because constants stand out in Ruby anyway, what with their upcase, and how often are you likely to have a constant as an L-value in your code? (See #2)

    Many of the potential oddities we're taught to fear in the statically typed world are exposed as FUD in dynamic languages. Don't sweat the small stuff. :)

    ReplyDelete
  8. I'm cool with everything said, and I too prefer enough rope to hang myself. I'll concede that conceptually it is okay, but I think I would prefer a different name since the value isn't constant.

    In the end, this surely isn't reason enough for me to give up on ruby, but I think if nothing else the name of this concept has room to improve.

    Thanks for the feedback.
    Jay

    ReplyDelete
  9. Anonymous12:08 PM

    Maybe, uh... sticky variables?

    I seem to remember from my reading of the Pickaxe that the CONSTANT_NAMING causes the interpreter to look up the variable somewhat differently. And... it doesn't look like I brought my Pickaxe home so I can't look it up.

    ReplyDelete
  10. Changing names is not a very good thing. Everyone knows that Ruby is totally dynamic and there could not be real constants at all, anywhere. Getting bold about naming could lead to weird disclaimers, like MutableClass, RedefinableMethod, or even putSomethingRespondingTo_to_s() instead of simply puts(). Let this nonsense stuff to Javaists and do things that matter instead. Thank you and happy new year.

    ReplyDelete
  11. It was a bit strange to me at first, also, but it makes sense when you consider that just about everything in Ruby can be redefined in some way. I don't even think it would be possible for constants to truly be "constant" and still allow classes to be redefined or even removed at runtime.

    Ruby: You'll shoot your eye out.

    ReplyDelete
  12. The only true constant is change. Most of our accepted 'constants' are actually only conventions. If you 'expect' your HOURS_PER_DAY == 24, then your code will be lost when a meteor changes the speed of the Earth's rotation. :-P

    ReplyDelete
  13. Here's a weird one we discovered today:

    MYCONST="bob"
    myvar=MYCONST
    myvar << " and fred"

    MYCONST and myvar are now == "bob and fred" without a warning. Clearly this must be a bug? (btw- this is with ruby 1.8.6 (2007-03-13 patchlevel 0) [i686-linux])

    ReplyDelete
  14. Fill, that's because Strings in ruby are mutable. The << modifies the string and thus technically the constant's object hasn't actually changed.
    An anon noted this earlier with arrays and another noted you can use MYCONST.freeze to stop that.

    ReplyDelete
  15. Anonymous10:42 AM

    What is a constant except a variable that we agree not to touch?

    Oh, no sir it is NOT. In reality that might be the case in Ruby but in many other languages in wide spread use it is not a variable. Any smart compiled language actually swaps the code reference for the real value.

    ReplyDelete
  16. Seems to me that in Ruby constant is a mere convention (based on upcase) and the interpreter tries to provide some help sometimes.

    This seems ok to me as long as folks agree on the concept.

    ReplyDelete
  17. So... It seems to me that constants are also different from variables in the way you reference them and that you can not use no_method_error on it.

    Also, I am curious about any thoughts about using constants as hash keys. A co-worker likes to do this throughout his code, and ends up with many constants defined at the beginning of every class and it just seems to me like a big nightmare.

    ReplyDelete

Note: Only a member of this blog may post a comment.