One of the new features of REALbasic 2008r2 is the concept of "Pairs", and I wanted to take a little bit of time to discuss them.
The concept of a pair is something that comes up frequently in programming. As REALbasic programmers, you're undoubtedly familiar with the Dictionary class. That's just a fancy way to store pairs of information -- keys and values. However, it doesn't afford you an easy way to describe a single key/value pair as its own entity. That's what makes the new Pair class a very interesting concept. Let's start out on the simple side of things and say you want to represent a key/value pair in your code. To create a pair the easy way, you can do this:
dim p as Pair = 1 : 2
What this gives you is a single class whose Left value is 1, and Right value is 2. It's as easy as that! So let's take a more real-world example. Let's pick on serialization -- you want to represent a property, and its value in a generic fashion.
dim ti as Introspection.TypeInfo = Introspection.GetType( self )
dim props() as Pair
for each prop as Introspection.PropertyInfo in ti.GetProperties
props.Append( prop.Name : prop.Value( self ) )
next prop
Voila! Now you have an array of key/value pairs in a handy list. Let's say you wanted to write the data out to disk. You could do that with something like this (in CSV format):
dim out as TextOutputStream = SpecialFolder.Desktop.Child( "Test.csv" ).CreateTextFile
for each p as Pair in props
try
out.Write( p.Left.StringValue + "=" + p.Right.StringValue + ", " )
catch err as TypeMismatchException
// Just ignore the problem
end try
next p
out.Close
So it's a fancy way to deal with key/value pairs -- but that's not all you can do with it! It's also a very handy singly-linked list. Check it out:
Dim p as Pair = 1 : 2 : 3 : 4 : 5In this example, you have a singly-linked list that traverses from 1 to 5. Laying it out with temps:
Temp1: left = 1, right = Temp2
Temp2: left = 2, right = Temp3
Temp3: left = 3, right = Temp4
Temp4: left = 4, right = 5
But wait, there's more! It's not just a singly-linked list -- it can also represent a binary tree. Because there's a left and a right property, you can use the Pair class as a node in a tree with two possible children.
Hopefully this gives you a few ideas as to how you can make use of the new Pair class, and it's corresponding operator (the colon) to clean up some of your projects. It's not the most ground-breaking feature ever, but it is a neat little tool to make use of.
hi.
I'm not really sure how useful this is. Can someone help clarify by providing real world situations this would be helpful in?
I'm wondering if this makes it any easier to return multiple values from a function?
Assuming that a Pair is treated a normal class, it should be returnable from a function, right? So if you have two values to return I guess it would save creating a custom class just for that.
@Joe Huber -- yes, this does make it easier to return multiple values from a function (it is just a normal class). So, let's say you wanted to return the number two and the string "Hello world", you could just do this:
return 2 : "Hello world"
So long as the return type is a Pair, this will compile and work dandy!
I totally dig this. It's the usefulness of a Dictionary without the overhead and I know of several places where this could be used in my code. However, I'm confused as to the addition of the colon operative. Wouldn't have been just as easy to just use:
dim p as Pair = new Pair(1, 2)
On the surface, adding the colon needlessly muddies the syntax, unless y'all have other uses for the colon in mind.
Also, I've noticed that the Left and Right properties can't be changed after the object has been created. Would that be a bug, missing feature, intended feature (if so, then why), I'm doing something wrong?
But, I still totally dig the functionality.
The colon operator allows you to quickly create pairs in code, without having huge lines of messy code.
return 1 : 2 : 3 : 4
is a lot easier to read than:
return new Pair( 1, new Pair( 2, new Pair( 3, 4 ) ) )
And yes, pair values are immutable on purpose (this allows for some nice optimizations based on the assumption that it's immutable).
This was kinda confusing because of the introspection stuff. Can anybody give an extremely simple example using things like car companies or something?
I can use cars for an example, sure. :-)
Let's say you have a function that is meant to tell you how many cars the user wants to buy. You used to have to deal with this one of two ways.
1) You could use ByRef params, like this:
Sub HowManyCars( ByRef numCars as Integer, ByRef carColor as Color )
2) You could make a class/structure to represent the data and do this:
Function HowManyCars() as NumberOfCarsWithColor
Now you can do it even easier:
Sub HowManyCars() as Pair
return 2 : RGB( 255, 0, 0 )
End Sub
As a way to say you want two red cars.
Aah, I get it. The car example actually didn't quite make me, it is the fact that the value can consist of another object resp. pair, I stuidly thought at first it would only refer to strings and numbers.
Can we put classes/objects into pairs, or only RB's own variables? And speaking of which, does a pair count as a variable or an object in RB's syntax?
And how can one test for the type of a pair value?
"if p.right isA integer..."?
and
"if p isA pair...."?
I guess some overview would be nice.
In any case, thanks for showing this, Aaron!
But wouldn't it be nice if the Pair class allowed for compile-time type checking?
@Fabian -- Pair.Left and Pair.Right are just variants, so you can put whatever you'd like into them. So you can check the var type of the variant if you need to know type information.
@Charles -- certainly, and I'm sure that once the concept of Generics makes it into the RB language, you'll see functionality using it (like Pairs, or WeakRefs, etc).
Can we add the ability to use a pair expression as a LValue?
function ReturnPair() as pair
return 2 : "Hello world!"
end function
dim n as integer
dim s as string
n : s = ReturnPair()
msgbox n ' Shows 2
msgbox s ' Shows Hello world!
@Isaac -- not quite in that way. You can't assign to a side, but you could either assign to .right/.left, or you could use the colon to produce the pair. So, in your example:
n : s : ReturnPair()
That would give you this structure:
(p1).Left = n
(p1).Right = (p2)
(p2).Left = s
(p2).Right = (p3)
(p3).Left = 2
(p3).Right = "Hello world!"
It looks like you're trying to force types onto the pair, which won't work -- pair is just a simple class, like this:
Class Pair
Dim Left as Variant
Dim Right as Variant
Sub Constructor( l as Variant, r as Variant ) // I am pretty sure!
End Class
Aside from handy, what are the other motivations for this feature?
Does it simplify a VB->RB conversion?
Was it found to optimize some aspect of RB IDE or REAL SQLServer?
@DeanG -- just handy, that's all. It'll allow us to clean up some hairy areas of the IDE I would imagine, but it's by no means a "you cannot live without this" feature.
Thanks Aaron.
It is indeed a handy little class. Just used it to simplify some code that previously utilized a Dictionary.
First time I've commented on one of your blog articles, although I've read many of them.
Your writings have been a great help to me. You have the heart of a teacher and are a man of good will.
@Houston -- thank you for your kind words. :-) I'm glad I could be of some help!