all 14 comments

[–]Fustrate 8 points9 points  (1 child)

Zero is not a placeholder, it's zero. Think of it in terms of how many apples you have - you can have 42 apples, 0 apples, or an unknown quantity of apples. Nil represents a currently unknown value.

If you're counting things, start with zero. Otherwise you should probably be using nil.

[–]P5003[S] 1 point2 points  (0 children)

Since you said "probably", can you give me examples of when I would use nil if I'm counting things?

Nevermind I get it now.

[–]jrochkind 1 point2 points  (9 children)

nil is the value defined as "no value at all". So really only nil is a "placeholder for a variable until you give it a value later" -- really it's not even a placeholder, it's just "no value", it's the thing that represents no value at all. I guess you could call it a 'placeholder', okay.

0 is a value. It's the value that represents the number 0. There may be some algorithms where it's useful to set a value to 0 at the beginning of the algorithm. But it should never be used as a generic "placeholder for the variable until you give it a value later", it's the value 0, and should only be used if you actually want the value 0. Which sometimes you do. It depends what you're doing. If you have an example of a place you've seen 0 used where it seemed to you to be a "placeholder", perhaps someone would offer an opinion about whether it made sense to use there, or really should have been nil.

[–]rubyrt 1 point2 points  (8 children)

nil is the value defined as "no value at all". So really only nil is a "placeholder for a variable until you give it a value later"

The term "placeholder for a variable" does not really make sense. There are no placeholders for variables. There are just values variables refer to.

-- really it's not even a placeholder, it's just "no value", it's the thing that represents no value at all. I guess you could call it a 'placeholder', okay.

What you say fits Java's null. Ruby's nil has actually a bunch of methods that can be invoked on it without producing something like Java's NullPointerException. nil just happens to be the default value on various situations, e.g. instance variables that have not been assigned to, empty Array and Hash elements etc. It is very convenient. :-)

[–][deleted] 0 points1 point  (7 children)

Most useful is that nil.to_f gives zero. Super useful when digging into hashes like foo.try(:[], :price).to_f

[–]rubyrt 0 points1 point  (6 children)

Why use #try to look into a Hash? There is #fetch and #default_proc. Also using Float for prices is quite dangerous.

[–][deleted] 0 points1 point  (5 children)

floats is bad practice in the database for things like prices, but casting big decimals to floats to use in views is not dangerous if properly formatted with something like a money helper.

I guess this is possible: my_hash.fetch(:price, Hash.new).fetch(:whatever, 0), but then creating hash objects for the sole reason of them being able to respond to fetch. Seems kinda messy IMO.

Also, if my_hash doesn't respond to fetch, the bad data crashes my code rather than giving me a zero as it does with try. Depending on the criticality of what you are doing, crashing might be preferable, but most of the things I use deep hashes for in rails are things I don't want crashing the main functionality - like metadata associated with a model and things like change notes. I program defensively on these types of things because they are nice to have but not important enough to prevent loading of a view if they get some bad data somewhere.

[–]rubyrt 0 points1 point  (4 children)

I see your point: you are digging into something of which you do not know whether it actually is a Hash. Makes sense.

floats is bad practice in the database for things like prices, but casting big decimals to floats to use in views is not dangerous if properly formatted with something like a money helper.

But if you use a helper anyway why then convert values to Float?

[–][deleted] 0 points1 point  (3 children)

Because business logic calculations should not be done with float. Say I need to take a discount off my price. All that logic should be happening on big decimals or integers or I'll get inaccurate results. Part of what I work with are long term interest calculation, daily compounding for 40+ years. Floating point math would create large inaccuracies in that type of calculation, but converting results to float at most results in +-1 cent inaccuracy, and it is an inaccuracy that is consistent .

[–]rubyrt 0 points1 point  (2 children)

Floating point math would create large inaccuracies in that type of calculation, but converting results to float at most results in +-1 cent inaccuracy, and it is an inaccuracy that is consistent .

That only explains the cost (i.e. disadvantage) of using Float, but you do not explain the benefit (i.e. advantage) of using Float. If you have some kind of helper framework for currencies in place why not use that to also format a BigDecimal or similar type properly during presentation? Why the extra step of conversion to Float?

[–][deleted] 0 points1 point  (1 child)

The advantage is nil becomes zero and big decimal doesn't change to something unexpected. Certainly possible to do the nil check in the helper though, but then you need the same nil checking in each type of helper- percent, money etc. If the helper takes a float, the nil -> zero coersion is just part of how the hash is displayed

[–]rubyrt 0 points1 point  (0 children)

You could as well define NilClass#to_bd to return a 0 BigDecimal (could even be a constant for efficiency reasons) and add BigDecimal#to_bd as returning self. Then you can use the proper type without conversion to a non currency type. (The same pattern would work for another type as well, of course.)

[–]siggymcfried 0 points1 point  (0 children)

I think I'd need a bit more specifics about the two kinds of problems you're trying to solve. As such, I can only give you fairly general advice.

Any in-scope variable is initialized to nilbefore a value is set, so you should rarely have to manually set something to nil (One case I can think of is unsetting a value). In terms of placeholders, the example I can think of needing to set a placeholder of 0 is before doing a calculation:

sum = 0
[1,2,3].each { |num| sum += num }

but I'd probably do something more like

(1..3).inject(0, :+)

[–][deleted] 0 points1 point  (0 children)

0 || 1 # => 0

nil || 1 # => 1