In Haskell (and Rust, and others) I can have instances that are constrained by other instances:
data Pair a b = Pair a b
instance (Eq a, Eq b) => Eq (Pair a b) where
Pair a b == Pair a' b' = a == a' && b == b'
With Java interfaces I can't. I must require the type parameters of Pair
to always implement Eq
, or I can't implement Eq<Pair<A, B>>
at all:
interface Eq<A> {
public boolean eq(A other);
}
class Pair<A extends Eq<A>, B extends Eq<B>> implements Eq<Pair<A, B>> {
A a;
B b;
public boolean eq(Pair<A, B> other){
return a.eq(other.a) && b.eq(other.b);
}
}
I'd like to have something like:
class Pair<A, B> implements Eq<Pair<A, B>> if (A implements Eq<A> && B implements Eq<B>) {...}
So far the Internet has told me that my desired functionality isn't directly supported in Java. Nevertheless I find this a rather critical factor in the (re)usability of interfaces. I'd like to know if there are workarounds or solutions that approximately cover the same ground.
Constrained user interfaces restrict user's access ability by not allowing them to request certain functions or information, or to have access to specific system resources. There are three major types of restricted interfaces: Menus and Shells: Database Views.
The correct response is restricts user access abilities by only allowing the user to request certain functions or have access to specific system resources. Constrained user interface limits the users environment within the system and access to objects.
Constraints inform the compiler about the capabilities a type argument must have. Without any constraints, the type argument could be any type. The compiler can only assume the members of System.Object, which is the ultimate base class for any .NET type. For more information, see Why use constraints.
The where clause in a generic definition specifies constraints on the types that are used as arguments for type parameters in a generic type, method, delegate, or local function. Constraints can specify interfaces, base classes, or require a generic type to be a reference, value, or unmanaged type.
One canonical solution that comes to my mind is to make the comparator external to the class. This is the approach that Scala takes, while making it easier digestible with the help of implicits. And they you'd have methods for constructing comparators, such as
public <A, B> Comparator<Pair<A,B>> pairCmp(Comparator<A> ca, Comparator<B> cb) { ...
This is however quite cumbersome to work with. Haskell does the same thing internally, passing dictionaries of type-classes implementitions under the hood, but the type-class interface together with type inference makes it much more pleasant.
As far as I know, in Java there is no way how to declare conditional instances. But a more OO approach would be to have a sub-class for pairs that allow equality:
class PairEq<A extends Eq<A>, B extends Eq<B>>
extends Pair<A,B>
implements Eq<Pair<A, B>> {
...
There is again some manual process involved, as you need to decide when to use Pair
and when PairEq
. But with method overloading, we could make it easier to use by declaring smart constructors. As Java always picks the most specific one, whenever we need to create a pair, we just use mkPair
and Java will pick the one returning PairEq
, if the arguments implement Eq
appropriately:
public static <A,B> Pair<A,B> mkPair(A a, B b) {
return new Pair<A,B>(a, b);
}
public static <A extends Eq<A>, B extends Eq<B>> PairEq<A,B> mkPair(A a, B b) {
return new PairEq<A,B>(a, b);
}
Complete sample code:
interface Eq<A> {
public boolean eq(A other);
}
public class Pair<A,B> {
public final A a;
public final B b;
public Pair(A a, B b) {
this.a = a;
this.b = b;
}
public static <A,B> Pair<A,B> mkPair(A a, B b) {
return new Pair<A,B>(a, b);
}
public static <A extends Eq<A>, B extends Eq<B>> PairEq<A,B> mkPair(A a, B b) {
return new PairEq<A,B>(a, b);
}
}
class PairEq<A extends Eq<A>, B extends Eq<B>>
extends Pair<A,B>
implements Eq<Pair<A,B>>
{
public PairEq(A a, B b) {
super(a, b);
}
@Override
public boolean eq(Pair<A,B> that) {
return a.eq(that.a) && b.eq(that.b);
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With