presto-main/src/main/java/io/prestosql/metadata/PolymorphicScalarFunction.java/PolymorphicScalarFunction/getScalarFunctionImplementationChoice
class PolymorphicScalarFunction:    private ScalarImplementationChoice getScalarFunctionImplementationChoice(
            BoundVariables boundVariables,
            Metadata metadata,
            PolymorphicScalarFunctionChoice choice)
    {
        Signature signature = getFunctionMetadata().getSignature();
        List<TypeSignature> resolvedParameterTypeSignatures = applyBoundVariables(signature.getArgumentTypes(), boundVariables);
        List<Type> resolvedParameterTypes = resolvedParameterTypeSignatures.stream()
                .map(metadata::getType)
                .collect(toImmutableList());
        TypeSignature resolvedReturnTypeSignature = applyBoundVariables(signature.getReturnType(), boundVariables);
        Type resolvedReturnType = metadata.getType(resolvedReturnTypeSignature);
        SpecializeContext context = new SpecializeContext(boundVariables, resolvedParameterTypes, resolvedReturnType);
        Optional<MethodAndNativeContainerTypes> matchingMethod = Optional.empty();

        Optional<MethodsGroup> matchingMethodsGroup = Optional.empty();
        for (MethodsGroup candidateMethodsGroup : choice.getMethodsGroups()) {
            for (MethodAndNativeContainerTypes candidateMethod : candidateMethodsGroup.getMethods()) {
                if (matchesParameterAndReturnTypes(candidateMethod, resolvedParameterTypes, resolvedReturnType, choice.getArgumentProperties(), choice.isNullableResult())) {
                    if (matchingMethod.isPresent()) {
                        throw new IllegalStateException("two matching methods (" + matchingMethod.get().getMethod().getName() + " and " + candidateMethod.getMethod().getName() + ") for parameter types " + resolvedParameterTypeSignatures);
                    }

                    matchingMethod = Optional.of(candidateMethod);
                    matchingMethodsGroup = Optional.of(candidateMethodsGroup);
                }
            }
        }
        checkState(matchingMethod.isPresent(), "no matching method for parameter types %s", resolvedParameterTypes);

        List<Object> extraParameters = computeExtraParameters(matchingMethodsGroup.get(), context);
        MethodHandle methodHandle = applyExtraParameters(matchingMethod.get().getMethod(), extraParameters, choice.getArgumentProperties());
        return new ScalarImplementationChoice(choice.isNullableResult(), choice.getArgumentProperties(), methodHandle, Optional.empty());
    }