In Ruby, everything is an object and responds to messages. And it turns out that we can ask a number if it’s even or odd. In other words, numbers have our criterion function built in, and we don’t have to define it at all.
I absolutely hate this. In fact, I'm not a huge believer in object-oriented programming in general.
<rant>
even?
Is not an operation that integers perform. Integers do not contain methods -- they are data. even? is function that operates on integers. Integer is a type. Objects are object types. An object which contains an integer is an object-integer type. An object-integer type is not the same as the integer type. Why do we constantly feel the need to wedge objects into everything in existence when often what we really need are algebraic data types, functions, and namespacing/modules?
Your rant isn't an argument, it's an opinion. I can just as well say integers are objects like any other and even? makes perfect sense as a method on them because that's the entire point of objects in the first place, binding state and behavior together into a single smarter package because it's a convent method of organizing code.
I agree 100% that it is my opinion. I would wager that most rants are opinions.
As far as this being the point of objects -- I see no state. I see no behavior. All I see is a function which maps the set of integers to booleans. That's it. It's just a function. The only reason we need packaging or modules at all is to prevent name collisions and importing the world.
The integer is state, deciding if it's odd or even is behavior. Yes, it's a query method, but it's a good one. The only reason we need objects is to make programming convenient. I don't want my namespace polluted by a free floating function named even?/odd? when it can be restricted to the only type it cares about by being bound to it.
In some strictly ease of use sense, object.function() is better than function(object) because it better helps me organize and scope my code; I don't have to worry about function name clashes because every instance is essentially a module for those functions.
My tools also work better when they can see I'm asking what methods are on integer rather than just show me all functions visible to me right now. It's easier to say hey Integer, what can you do rather than hey world, what works with integers.
If you can only see functions, integers, and booleans and you can't realize those are all themselves perfectly good objects, then that's a failure of your imagination. Me, I like my functions, methods, booleans, integers, and all other types being objects because it's damn useful and damn convenient in making my day to day life easier.
I completely agree. I like how most responses to your rant hone in on the fact that it's a simple syntactic change, but utterly fail to get the point that composition of stateless functions a la Joy is totally magical, and opens up a whole world of possibilities that mutating, unchainable, uncomposable methods cannot.
For starters, and given that this is a thread about scientific computing, having stateless functions that mirror mathematical ones gives you robust, efficient approaches for parallelism and optimization that are completely lost (or horribly intractable) once you start boxing numeric types just so some OOPhead can put a dot after them.
It's more consistent (no wondering what's a method and what's not for most common situations) and it stops the global namespace getting littered up with functions that are only relevant to certain types.
To a Rubyist, knowing that Ruby is an object oriented language, it is clear one is sending a message to i. With the latter example, it is not clear what even? belongs to and what it expects to receive.
Then namespace it. As I said, there's nothing stopping you from having namespaces and modules.
import Arithmetic (even)
even :: Integer -> Bool
even i
Also, you can call function calls messages or object operations or dictionary lookups, but I don't see the benefit in 99% of cases. I guess the dynamism may be useful in GUI code where it's not that hard to see if you're wrong and if you are wrong, nobody's world really ends. In most other cases functions are best described as having a formal mathematical definition so you can reason about them with some accuracy.
Your order is like English, but your semantics are the exact opposite. My code asks "is 2 even?" -- it calls the even? function to check to see if it's a boolean. If you notice, the even? object function is referentially transparant and implicitly contains its own argument. It's not really a function. It's a field. What Ruby has done is define a field of every Integer instance which returns True or False based on a separate function call that determines whether or not the object is even. It's irrelevant how the function is implemented because those are the semantics -- you have defined a method that answers if the quality of evenness applies to 2. Your own example is exactly why I dislike the semantics of this object method.
I don't get your argument. What's the difference between
module Numeric
def even?
# ...
end
end
and
even? :: (Numeric t) -> a
Both declare functions that consume something implementing the Numeric type/protocol, and return something else. I don't see the practical difference between them, other than the calling syntax of the result.
First, the even function is only defined for integers, not general numeric types. Second, type classes are not objects. Ad hoc polymorphism is used to denote different actions for different types but it doesn't break typing. The type integer is not the type object-integer:
even? :: Integer -> Bool
I am actually thoroughly confused where you got your type specification. Were you trying to say:
class Integral a where
even? :: a -> Bool
even? :: (Integral a) => a -> Bool
The reason why this disturbs me is because I find it difficult to draw the line at why even? should be even? :: (Integral a) => a -> Bool instead of even? :: Integer -> Bool. This doesn't make sense -- it's not a good use for typeclasses. There aren't many different categories of types which have a semantic notion of being even but a different implementation or type restriction. Even is just... even. It's just an operation on the integers. There's no need to bring ad hoc polymorphism in here.
I absolutely hate this. In fact, I'm not a huge believer in object-oriented programming in general.
<rant>
Is not an operation that integers perform. Integers do not contain methods -- they are data. even? is function that operates on integers. Integer is a type. Objects are object types. An object which contains an integer is an object-integer type. An object-integer type is not the same as the integer type. Why do we constantly feel the need to wedge objects into everything in existence when often what we really need are algebraic data types, functions, and namespacing/modules?</rant>
As you can tell, I like the ML family.