Skip to content

Commit

Permalink
Support type bound in TypeVariableConstraint
Browse files Browse the repository at this point in the history
Add an is-a constraint for type variables, so we can describe generic types of a certain kind.
  • Loading branch information
daniel-ohayon authored and Rongrong Zhong committed Sep 29, 2020
1 parent 5e20973 commit 7e880e6
Show file tree
Hide file tree
Showing 5 changed files with 55 additions and 14 deletions.
7 changes: 5 additions & 2 deletions presto-docs/src/main/sphinx/develop/functions.rst
Original file line number Diff line number Diff line change
Expand Up @@ -116,8 +116,11 @@ To make our previous example work with any type we need the following:
The ``@TypeParameter`` annotation is used to declare a type parameter which can
be used in the argument types ``@SqlType`` annotation, or return type of the function.
It can also be used to annotate a parameter of type ``Type``. At runtime, the engine
will bind the concrete type to this parameter. ``@OperatorDependency`` may be used
to declare that an additional function for operating on the given type parameter is needed.
will bind the concrete type to this parameter. Optionally, the type parameter
can be constrained to descendants of a particular type by providing a ``boundedBy``
type class to ``@TypeParameter``.
``@OperatorDependency`` may be used to declare that an additional function
for operating on the given type parameter is needed.
For example, the following function will only bind to types which have an equals function
defined:

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -323,7 +323,8 @@ private boolean appendConstraintSolvers(
actualType,
typeVariableConstraint.isComparableRequired(),
typeVariableConstraint.isOrderableRequired(),
Optional.ofNullable(typeVariableConstraint.getVariadicBound())));
Optional.ofNullable(typeVariableConstraint.getVariadicBound()),
typeVariableConstraint.getTypeBound()));
return true;
}

Expand Down Expand Up @@ -595,14 +596,22 @@ private class TypeParameterSolver
private final boolean comparableRequired;
private final boolean orderableRequired;
private final Optional<String> requiredBaseName;

public TypeParameterSolver(String typeParameter, Type actualType, boolean comparableRequired, boolean orderableRequired, Optional<String> requiredBaseName)
private final Class<? extends Type> typeBound;

public TypeParameterSolver(
String typeParameter,
Type actualType,
boolean comparableRequired,
boolean orderableRequired,
Optional<String> requiredBaseName,
Class<? extends Type> typeBound)
{
this.typeParameter = typeParameter;
this.actualType = actualType;
this.comparableRequired = comparableRequired;
this.orderableRequired = orderableRequired;
this.requiredBaseName = requiredBaseName;
this.typeBound = typeBound;
}

@Override
Expand Down Expand Up @@ -639,6 +648,9 @@ private boolean satisfiesConstraints(Type type)
if (orderableRequired && !type.isOrderable()) {
return false;
}
if (!typeBound.isInstance(type)) {
return false;
}
if (requiredBaseName.isPresent() && !UNKNOWN.equals(type) && !requiredBaseName.get().equals(type.getTypeSignature().getBase())) {
// TODO: the case below should be properly handled:
// * `type` does not have the `requiredBaseName` but can be coerced to some type that has the `requiredBaseName`.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,9 +57,6 @@
import static com.facebook.presto.common.function.OperatorType.LESS_THAN_OR_EQUAL;
import static com.facebook.presto.common.function.OperatorType.NOT_EQUAL;
import static com.facebook.presto.operator.annotations.ImplementationDependency.isImplementationDependencyAnnotation;
import static com.facebook.presto.spi.function.Signature.comparableTypeParameter;
import static com.facebook.presto.spi.function.Signature.orderableTypeParameter;
import static com.facebook.presto.spi.function.Signature.typeVariable;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.collect.ImmutableList.toImmutableList;
import static com.google.common.collect.ImmutableSet.toImmutableSet;
Expand Down Expand Up @@ -112,13 +109,13 @@ public static List<TypeVariableConstraint> createTypeVariableConstraints(Iterabl
for (TypeParameter typeParameter : typeParameters) {
String name = typeParameter.value();
if (orderableRequired.contains(name)) {
typeVariableConstraints.add(orderableTypeParameter(name));
typeVariableConstraints.add(new TypeVariableConstraint(name, false, true, null, typeParameter.boundedBy()));
}
else if (comparableRequired.contains(name)) {
typeVariableConstraints.add(comparableTypeParameter(name));
typeVariableConstraints.add(new TypeVariableConstraint(name, true, false, null, typeParameter.boundedBy()));
}
else {
typeVariableConstraints.add(typeVariable(name));
typeVariableConstraints.add(new TypeVariableConstraint(name, false, false, null, typeParameter.boundedBy()));
}
}
return typeVariableConstraints.build();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
*/
package com.facebook.presto.spi.function;

import com.facebook.presto.common.type.Type;

import java.lang.annotation.Repeatable;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
Expand All @@ -28,4 +30,6 @@
public @interface TypeParameter
{
String value();

Class<? extends Type> boundedBy() default Type.class;
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,18 +27,30 @@ public class TypeVariableConstraint
private final boolean comparableRequired;
private final boolean orderableRequired;
private final String variadicBound;
private final Class<? extends Type> typeBound;

@JsonCreator
public TypeVariableConstraint(
@JsonProperty("name") String name,
@JsonProperty("comparableRequired") boolean comparableRequired,
@JsonProperty("orderableRequired") boolean orderableRequired,
@JsonProperty("variadicBound") @Nullable String variadicBound)
@JsonProperty("variadicBound") @Nullable String variadicBound,
@JsonProperty("boundedBy") Class<? extends Type> typeBound)
{
this.name = name;
this.comparableRequired = comparableRequired;
this.orderableRequired = orderableRequired;
this.variadicBound = variadicBound;
this.typeBound = typeBound;
}

public TypeVariableConstraint(
@JsonProperty("name") String name,
@JsonProperty("comparableRequired") boolean comparableRequired,
@JsonProperty("orderableRequired") boolean orderableRequired,
@JsonProperty("variadicBound") @Nullable String variadicBound)
{
this(name, comparableRequired, orderableRequired, variadicBound, Type.class);
}

@JsonProperty
Expand All @@ -65,6 +77,12 @@ public String getVariadicBound()
return variadicBound;
}

@JsonProperty
public Class<? extends Type> getTypeBound()
{
return typeBound;
}

public boolean canBind(Type type)
{
if (comparableRequired && !type.isComparable()) {
Expand All @@ -73,6 +91,9 @@ public boolean canBind(Type type)
if (orderableRequired && !type.isOrderable()) {
return false;
}
if (!typeBound.isInstance(type)) {
return false;
}
if (variadicBound != null && !type.getTypeSignature().getBase().equals(variadicBound)) {
return false;
}
Expand All @@ -92,6 +113,9 @@ public String toString()
if (variadicBound != null) {
value += ":" + variadicBound + "<*>";
}
if (!typeBound.equals(Type.class)) {
value += " extends " + typeBound.getSimpleName();
}
return value;
}

Expand All @@ -108,12 +132,13 @@ public boolean equals(Object o)
return comparableRequired == that.comparableRequired &&
orderableRequired == that.orderableRequired &&
Objects.equals(name, that.name) &&
Objects.equals(variadicBound, that.variadicBound);
Objects.equals(variadicBound, that.variadicBound) &&
Objects.equals(typeBound, that.typeBound);
}

@Override
public int hashCode()
{
return Objects.hash(name, comparableRequired, orderableRequired, variadicBound);
return Objects.hash(name, comparableRequired, orderableRequired, variadicBound, typeBound);
}
}

0 comments on commit 7e880e6

Please sign in to comment.