Type-aware UDFs

Type awareness is implemented using the TType* userType argument of the DeclareSignature(...) and buildReturnSignature(...) methods. You can use this argument to see with which arguments a UDF function was called from YQL and build the desired signature. A user-defined type can be parsed using the ITypeInfoHelper::TPtr object, which is returned by the builder.TypeInfoHelper() method and which can be used to create TypeInspectors, such as TTupleTypeInspector, TCallableTypeInspector, TOptionalTypeInspector, and others.

Warning

For the YQL engine to pass a non-empty userType to DeclareSignature() as input, the class of your UDF must contain a subtype definition:

typedef bool TTypeAwareMarker;

Using such functions, you can:

  • Support optional or default arguments:

    SomeTypeAwareUdfFunc(Double, Double) -> Bool

    SomeTypeAwareUdfFunc(Double, Double, Double) -> Bool

    TMemberIndices TSomeTypeAwareUdfFunc::buildReturnSignature(IFunctionTypeInfoBuilder& builder, TType* userType) {
        ...
        auto userTypeInspector = TTupleTypeInspector(*typeHelper, userType);
        auto argsTypeTuple = userTypeInspector.GetElementType(0);
        auto argsTypeInspector = TTupleTypeInspector(*typeHelper, argsTypeTuple);
    
        // Build right signature by number of arguments
        if (argsTypeInspector.GetElementsCount() == 2) {
            /* Build one signature */
            ...
        } else if (argsTypeInspector.GetElementsCount() == 3) {
            /* Build other signature */
            ...
        } else {
            ...
        }
    ...
    }
    
    TUnboxedValue TSomeTypeAwareUdfFunc::Run(IValueBuilder* valueBuilder, TUnboxedValuePod* args) {
        ...
        double value = 0.0;  // default
        if (/* hasThirdElement */) {
            value = args[2].Get<double>(); // non-default
        }
        ...
    }
    
  • Support overloading:

    SomeTypeAwareUdfFunc(List<Int>) -> String

    SomeTypeAwareUdfFunc(Stream<Int>) -> String

    TMemberIndices buildReturnSignature(IFunctionTypeInfoBuilder& builder, TType* userType) {
        ...
        auto userTypeInspector = TTupleTypeInspector(*typeHelper, userType);
        auto argsTypeTuple = userTypeInspector.GetElementType(0);
        auto argsTypeInspector = TTupleTypeInspector(*typeHelper, argsTypeTuple);
    
        auto typeKind = typeHelper->GetTypeKind(argsTypeInspector.GetElementType(0));
        if (typeKind == ETypeKind::List) {
            ...
        } else if (typeKind == ETypeKind::Stream) {
            ...
        } else {
            ...
        }
        ...
    }
    
  • Support changing the response type depending on the type returned by the custom callback:

    SomeTypeAwareUdfFunc(Callable<...>->Int)->List<Int>

    SomeTypeAwareUdfFunc(Callable<...>->Double)->List<Double>

    SomeTypeAwareUdfFunc(Callable<...>->CallbackReturnType)->List<CallbackReturnType>

    TMemberIndices buildReturnSignature(IFunctionTypeInfoBuilder& builder, TType* userType) {
        ...
        auto userTypeInspector = TTupleTypeInspector(*typeHelper, userType);
        auto argsTypeTuple = userTypeInspector.GetElementType(0);
        auto argsTypeInspector = TTupleTypeInspector(*typeHelper, argsTypeTuple);
        auto callbackInspector = TCallableTypeInspector(*typeHelper, argsTypeInspector.GetElementType(0));
    
        auto resultType = builder.List()->Item(callbackInspector.GetReturnType()).Build();
        builder.Returns(resultType).Args()->Add(argsTypeInspector.GetElementType(0)).Done();
        ...
    }