I have been trying to learn a new language recently… Ruby. Ruby is a dynamic language created in the 90’s by Yukihiro “Matz” Matsumoto. I have been using C# and .Net for over 7 years, so moving to a dynamic language has been a bit of a mind shift but I have really enjoyed the journey so far.
Here are a few of the resources I have been using to aid my learning:
I just wanted to highlight a few things that I really like about the language…
Stings are mutable in Ruby! Now this could end up being a Gothca for me, as I’m so used to strings being immutable in .Net.
The following code, shows 2 objects being created; referenceToOriginalWord
is equal to wordToMutate
. I have then changed the value of the first character and added a character to the end of the original string, effectively updating the value from Hello to Yellow.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class MutableStrings
def CanMutateAString
wordToMutate = "Hello"
referenceToOriginalWord = wordToMutate
puts "BEFORE MUTATION ---> WordToMutate: " + wordToMutate
puts "BEFORE MUTATION ---> referenceToOriginalWord: " + referenceToOriginalWord
wordToMutate[0] = "Y"
wordToMutate[5] = "w"
puts "AFTER MUTATION ---> WordToMutate: " + wordToMutate
puts "AFTER MUTATION ---> referenceToOriginalWord: " + referenceToOriginalWord
end
end
testObject = MutableStrings.new
testObject.CanMutateAString
When can see from the output that the values after mutation for both the wordToMutate
the referenceToOriginalWord
variables have changed. Ruby has not created a copy of the value like in .Net.
We can see that in C# if we try to change the character of a string at a particular position, we get a compiler error informing us that there is no setter (hence the immutable):
…and just for completeness the code below is a port of the Ruby example above, and you can see from the output that strings are immutable in C#.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class Program
{
static void Main(string[] args)
{
var wordToMutate = "Hello";
var referenceToOriginalWord = wordToMutate;
Console.WriteLine("BEFORE MUTATION ---> " + wordToMutate);
Console.WriteLine("BEFORE MUTATION ---> " + referenceToOriginalWord);
wordToMutate = wordToMutate.Replace("H", "Y");
wordToMutate += "w";
Console.WriteLine("AFTER MUTATION ---> " + wordToMutate);
Console.WriteLine("AFTER MUTATION ---> " + referenceToOriginalWord);
Console.Read();
}
}
I like how easy it is to do Mixins in Ruby… I struggled a while back to understand Mixins but had I of learnt about Mixins in Ruby first, I’m sure I would of picked it up a lot quicker! This code sample shows how you can extend the functionality of a class by mixing in a function from another module.
We start off with our class called ImGoodAtAdding
. Now, if we wanted to, for some reason or another, extend our adding class to include subtracting functionality, we simply include
that module in our class.
1
2
3
4
5
6
7
8
9
10
11
12
13
module SubtractingModule
def Subtracting(number1, number2)
number1 - number2
end
end
class ImGoodAtAdding
include SubtractingModule
def Adding (number1, number2)
number1 + number2
end
end
When we instantiate a new instance of ImGoodAtAdding
, we can now call the Subtracting
method as well as the Adding
method.
1
2
3
calculator = ImGoodAtAdding.new
puts calculator.Adding(2, 4)
puts calculator.Subtracting(2, 4)
“If it looks like a duck, quacks like a duck and walks like a duck, it’s a duck”
right? …Wrong, not in Ruby.
Let’s look at the following example:
1
2
3
4
5
6
7
8
9
10
class DuckTyping
def ThisShouldInTheoryTakeInANumber (number)
puts "BEFORE ===> number is of type: " + number.class.to_s
number = number * 2
puts number
puts "AFTER ===> number is of type: " + number.class.to_s
end
end
Reading the code block above, gives me the impression that the number
parameter should be a numeric value like an integer, and you can see from the code block below that when we instantiate a new Type of DuckTyping
and invoke the ThisShouldInTheoryTakeInANumber
method with the value 4
passed in, we get what we expect; the number 8
is printed along with the type of FixNum
.
1
2
testing = DuckTyping.new
testing.ThisShouldInTheoryTakeInANumber 4
So what happens when we pass in a string like "4"
…
1
2
testing = DuckTyping.new
testing.ThisShouldInTheoryTakeInANumber "4"
Nothing ground breaking here, a string was passed in and although we applied a “mathematical” operation, it was still valid. What was returned here was the value "44"
and a type of string
. This is because the *
operator has been overridden for the string
type. We would get a compile error in .Net unless we created our own type and overrode the *
operator ourselves.
This 3rd example shows just that, I have created a type called FooType
and have overridden the *
operator. All that is returned is 6. The interesting thing here is line 7 of the code below, I’m just passing in a new instance of the type to our DuckTyping
instance.
1
2
3
4
5
6
7
8
class FooType
def *(other)
6
end
end
testing = DuckTyping.new
testing.ThisShouldInTheoryTakeInANumber FooType.new
Before the *
operation occurred, the type was of type FooType
as expected, but after the *
it was of type FixNum
… Magic!! …not really… What these 3 examples prove is that you can pass anything into the method in Ruby, it’s not type safe, hence why it’s called a dynamic language. In order to ensure your application behaves how you expect, some checking should happen to ensure the type your method receives is what is expected.