In my experience, all languages I know have a sort of "positive" type system. What I mean by "positive type system" is that when you are writing the source code, you always specify what types your function/objects accept, like:
function f(SomeType argument) { ... }
But I have never seen something like:
function f(AbstractType<Not ConcreteTypeFoo> argument) { ... }
I think it would be very useful in scenarios where you need to specify the behavior for some families of types without the need to write a lot of boilerplate code to implement some type exclusion logic.
By reading this wikipedia article abou type systems, I can see that maybe gradual type systems would be some sort of equivalent to this, but not exactly the same thing. I don`t think Union Types solve the same class of problem and saying that when you specify a type you are excluding other types is not the same thing as well.
// addition type system: argument can only be of type SomeType
function foo (SomeType argument) { ... }
// union types: argument can only be of type SomeType or FooType
function foo (SomeType | FooType argument) { ... }
// intersection types: argument must conform to both SomeType and FooType
function foo (SomeType & FooType argument) { ... }
// exclusion type: argument can be ANYTHING except ConcreteTypeFoo
function foo (AbstractType<Not ConcreteTypeFoo> argument) { ... }
An example scenario about where this would be useful:
interface Transaction() { ... }
class TransactionTypeA implements Transaction { ... }
class TransactionTypeB imnplements Transaction { ... }
class TransactionTypeC implements Transaction { ... }
interface TransactionRule() {
boolean validate(Transaction t);
}
class RuleA implements TransactionRule {
boolean validate(Transaction<Not TransactionTypeA> t) { ... }
boolean validate(TransactionTypeA t) { ... }
}
class RuleB implements TransactionRule {
boolean validate(Transaction<Not TransactionTypeB> t) { ... }
boolean validate(TransactionTypeB t) { ... }
}
class RuleC implements TransactionRule {
boolean validate(Transaction<Not TransactionTypeC> t) { ... }
boolean validate(TransactionTypeC t) { ... }
}
The equivalent code without an exclusion type system would be like this:
interface Transaction() { ... }
class TransactionTypeA implements Transaction { ... }
class TransactionTypeB imnplements Transaction { ... }
class TransactionTypeC implements Transaction { ... }
interface TransactionRule() {
boolean validate(Transaction t);
}
interface ValidationStrategy() {
boolean validate(Transaction t);
}
interface ValidationStrategyFactory {
ValidationStrategy create(Transaction t);
}
class RuleA implements TransactionRule {
RuleAValidationStratetyFactory strategyFactory;
constructor(RuleAValidationStratetyFactory f) {
strategyFactory = f;
}
boolean validate(Transaction t) {
ValidationStrategy s = strategyFactory.create(t);
return s.validate();
}
}
class RuleAValidationStratetyFactory implements ValidationStrategyFactory {
ValidationStrategy create(Transaction t) {
if (t.type == TransactionTypeA) {
return new RuleATransactionTypeAValidationStrategy();
} else {
return new RuleATransactionTypeBAndCValidationStrategy();
}
}
}
class RuleATransactionTypeAValidationStrategy implements ValidationStrategy {
boolean validate(Transaction t) { ... }
}
class RuleATransactionTypeBAndCValidationStrategy implements ValidationStrategy {
boolean validate(Transaction t) { ... }
}
class RuleB implements TransactionRule {
RuleBValidationStratetyFactory strategyFactory;
constructor(RuleBValidationStratetyFactory f) {
strategyFactory = f;
}
boolean validate(Transaction t) {
ValidationStrategy s = strategyFactory.create(t);
return s.validate();
}
}
class RuleBValidationStratetyFactory implements ValidationStrategyFactory {
ValidationStrategy create(Transaction t) {
if (t.type == TransactionTypeA) {
return new RuleBTransactionTypeBValidationStrategy();
} else {
return new RuleBTransactionTypeAAndCValidationStrategy();
}
}
}
class RuleBTransactionTypeBValidationStrategy implements ValidationStrategy {
boolean validate(Transaction t) { ... }
}
class RuleBTransactionTypeAAndCValidationStrategy implements ValidationStrategy {
boolean validate(Transaction t) { ... }
}
class RuleC implements TransactionRule {
RuleCValidationStratetyFactory strategyFactory;
constructor(RuleCValidationStratetyFactory f) {
strategyFactory = f;
}
boolean validate(Transaction t) {
ValidationStrategy s = strategyFactory.create(t);
return s.validate();
}
}
class RuleCValidationStratetyFactory implements ValidationStrategyFactory {
ValidationStrategy create(Transaction t) {
if (t.type == TransactionTypeA) {
return new RuleCTransactionTypeCValidationStrategy();
} else {
return new RuleCTransactionTypeAAndBValidationStrategy();
}
}
}
class RuleCTransactionTypeCValidationStrategy implements ValidationStrategy {
boolean validate(Transaction t) { ... }
}
class RuleCTransactionTypeAAndBValidationStrategy implements ValidationStrategy {
boolean validate(Transaction t) { ... }
}
So, the question is: How can I simulate such scenario in a language like Java? It would be also helpful to get some background information of how this is solved in other programming languages.
SomeType argument, you are already excluding all other types. Otherwise, you would just passObjector somesuch.FooBaris actually!(Everything \ FooBar)