+1 on the Association interface.

The Tuple interface looks like a Collection, even more so when it expands to more than two elements.

-Adrian

On 3/4/2011 11:24 AM, Gary Gregory wrote:
Can we talk about the class name and use cases?

For me a pair evokes similarity: a pair of shoes, a pair of hands, a pair of
coordinates. You get the idea. Having a Pair.of(name, dog) reads like
nonsense to me. A Map.Entry.of(name, dog) I understand, same for an
Association.of(name, dog) (I cannot escape from my Smalltalk heritage.)

In most cases, I deal with key-value "pairs", let's play:

new Pair(name, dog)
new KeyValue(name, dog)
new Association(name, dog)
new MapEntry(name, dog)

If we want to accommodate "real" pairs like a Point2D(x,y), which we should,
then the Pair name makes perfect sense IF it is a Pair<T>  where the x and y
are both Ts.

There are two uses cases: pairs and key-value associations.

It would then be interesting for the Pair<T>  and KeyValue(K,V) interfaces to
share a common implementing base class, a something that holds two objects
(a TwoTuple, yikes?)

Let's play (these are immutable for brevity):

public interface TwoTuple<E1,E2>  {
     E1 getFirst();
     E2 getSecond();
}

public interface Pair<T>  extends TwoTuple<T, T>  {
}

public interface Association<K, V>  extends TwoTuple<K, V>  {
     K getKey();
     V getValue();
}

Thoughts?

Gary


On Fri, Mar 4, 2011 at 1:35 PM, Matt Benson<gudnabr...@gmail.com>  wrote:

On Fri, Mar 4, 2011 at 5:41 AM, Stephen Colebourne<scolebou...@joda.org>
wrote:
I now have authoristion from OpenGamma to discuss adding a Pair class
to [lang] based on our internal classes. If necessary a CCLA can be
signed, although since we are not necessarily importing the OpenGamma
classes as is and I'd be writing code in [lang3] with my Apache hat
on, the CCLA might not be needed. I can also code it in work time :-)

The main goal would be either an abstract class or an interface for
Pair. We chose an abstract class so that it could have factory
methods:

  Pair<String, Number>  p = Pair.of("Foo", 6);

It also allowed more control over the immutability of Pair (although
because its abstract and holds references to any object, immutability
cannot be guaranteed).

We then have other concrete classes:
- ObjectsPair - two generified objects
- DoublePair - two double
- IntDoublePair - int and double
- LongDoublePair - long and double
- IntObjectPair - int and generified object
- LongObjectPair - long and generified object

Clearly there are many more possible combinations, but some make less
sense than others. (Booleans don't waste space, as they are a
singleton reference, short/float are rarely used)

Beyond this, there are some standard comparators.

Design wise, we implement Map.Entry (makes sense). The primitive
classes implement primitive map entry interfaces from another library,
but that wouldn't make sense here. They are comparable and
serializable (again, one reason for an abstract class).

We name our elements "first" and "second".

The elements are available by methods (for generics) or as public
final variables from the concrete classes (not the abstract one). The
methods are getFirst(), getSecond() plus getKey() and getValue() for
Map compatibility.

The pairs are implemented as immutable. I saw someone mention the
possibility of a mutable pair, so perhaps we consider that.

I don't want this to be a long process of design or implementation! If
there isn't rapid consensus, I'd suggest either shipping [lang3] with
or without the existing class.

Opinions?
I agree that it would be nice to do whatever we're going to do
quickly, and ship with *something*.  On the other hand, I don't want
to ship the existing class without consensus on design, only to give
ourselves (and users) headaches trying to replace it in a subsequent
release.

I also had the thought that the abstract class would be necessary for
the factory methods.  It doesn't seem important, but I'd really like
to be able to say Pair.of(X, Y).  Semantically it'd also be nice to be
able to use fields on the immutable variety of Pair (it's perhaps
debatable in light of JIT whether the final field access yields better
performance, so I won't address it--but it *looks* faster :P ), while
still requiring the client to know as little as possible about the RT
type of the Pair.  Is it possible to accomplish all these things?

abstract class Pair<L, R>  implements Map.Entry<L, R>  {
  abstract L getLeft();
  abstract R getRight();
  final L getKey() { return getLeft(); }
  final R getValue() { return getRight(); }
  static<L, R>  ImmutablePair<L, R>  of(L, R) {}
}

class ImmutablePair<L, R>  extends Pair<L, R>  {
  final L left;
  final R right;
  ImmutablePair(L left, R right) { this.left = left; this.right = right; }
  L getLeft() { return left; }
  R getRight() { return right; }
  static<L, R>  ImmutablePair<L, R>  of(L, R) {}
}

class MutablePair<L, R>  extends Pair<L, R>  {
  private L left;
  private R right;

  MutablePair(L left, R right) { setLeft(left); setRight(right); }
  L getLeft() { return left; }
  setLeft(L left) { this.left = left; }
  R getRight() { return right; }
  setRight(R right) { this.right = right; }
  static<L, R>  MutablePair<L, R>  of(L, R) {}
}

In the examples above I continue to use the left/right idiom for
reasons of inertia; in the end, I don't *really* care.  It seems
examples abound of the various proposed paired names in other
programming contexts, so this becomes a simple matter of taste and/or
majority rules.  Personally I prefer left/right as there is less
connotation of priority given either member of the pair as (IMO) in
the case of first/second.

If we want to extend ImmutablePair for the wrapper types (it wouldn't
seem to make sense to provide access to the primitive equivalent
values in the MutablePair variety), where does it end?  If we provide
any such pair types, IMO we should use some predictable rule to
define, for a given wrapper type, what combinations are available,
e.g.:

* X Double
* X Boolean
* X Object
* X self
* X * ?

I'm sure I don't have to tell any of my fellow Commons committers that
our components may well have to provide more implementations, or none
at all, compared to equivalent proprietary code, for reasons of
perceived "completeness."  If anything this is even more so in the
case of [lang] than perhaps some other Commons components.

Matt

Stephen

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscr...@commons.apache.org
For additional commands, e-mail: dev-h...@commons.apache.org


---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscr...@commons.apache.org
For additional commands, e-mail: dev-h...@commons.apache.org




---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscr...@commons.apache.org
For additional commands, e-mail: dev-h...@commons.apache.org

Reply via email to