dm-script

How can I create a function with multiple return types (number or string)?


I'm trying to implement a function that returns different types based on the input. In my case, the function should return a number when given one input and a string when given a different input. Initially, I attempted to use function overloading, but since both overloads have the same parameter type, the compiler raises an error.

What I tried:
I wrote the following code (in pseudo-code) to illustrate my approach:

Number tmp(String a) {
    if (a == "a") return 1;
    // What should be returned if condition is not met?
}

String tmp(String a) {
    if (a == "b") return "a";
    // What should be returned if condition is not met?
}

// Example calls:
Result(tmp("a"));
Result(tmp("b"));

Questions:

  1. Is there a feasible and easy way to implement a function that returns two different types based on the input in a statically typed language?
  2. What are some recommended design patterns or language features (e.g., union types, variants, or generics) that can help solve this problem?
  3. Could you suggest improvements to my approach?

Additional Context:

Any guidance or examples would be greatly appreciated!


Solution

  • You have discovered two limitations of the DM script language. The first is that functions cannot have multiple return values, as they can in Python. The other is that all functions with a given name and list of argument types (i.e. a given function prototype) can only have a single return type.

    One way to get around these limitations is to make use of the fact that DM functions can return objects, rather than just a simple numeric or string value. Since an object can contain multiple member variables (of various types), this allows one to return multiple values with a single function call. One can do this either by using TagGroup objects or by defining and using a custom script Object type.

    The following example re-implements your tmp function using a TagGroup return value:

    TagGroup tmp(String a)
    {
        TagGroup returnData = NewTagGroup();
        if (a == "a")
            returnData.TagGroupSetTagAsNumber("Returned number", 1);
        else if (a == "b")
            returnData.TagGroupSetTagAsString("Returned string", "a");
        
        return returnData;
    }
    
    Number numericResult;
    String stringResult;
    TagGroup result = tmp("a");
    if (result.TagGroupGetTagAsNumber("Returned number", numericResult))
        Result("Numeric result: " + numericResult + "\n");
    else if (result.TagGroupGetTagAsString("Returned string", stringResult))
        Result("String result: " + stringResult + "\n");
    

    Here is another example that does the equivalent with a custom script Object defined by the TmpReturnData class:

    class TmpReturnData
    {
        Number isNumeric;
        Number numericResult;
        String stringResult;
    
        Object Init(Object self, Number numericReturnValue)
        {
            isNumeric = 1;
            numericResult = numericReturnValue;
        
            return self;
        }
    
        Object Init(Object self, String stringReturnValue)
        {
            isNumeric = 0;
            stringResult = stringReturnValue;
        
            return self;
        }
    
        Number IsNumericResult(Object self)
            return isNumeric;
    
        Number GetNumericResult(Object self)
            return numericResult;
    
        String GetStringResult(Object self)
            return stringResult;
    }
    
    Object tmp(String a)
    {
        Object returnData;
        if (a == "a")
            returnData = Alloc(TmpReturnData).Init(1);
        else if (a == "b")
            returnData = Alloc(TmpReturnData).Init("a");
        
        return returnData;
    }
    
    void main()
    {
        Object result = tmp("a");
        if (result.ScriptObjectIsValid())
        {
            if (result.IsNumericResult())
                Result("Numeric result: " + result.GetNumericResult() + "\n");
            else
                Result("String result: " + result.GetStringResult() + "\n");
        }
    }
    
    main();
    

    The thing to keep in mind is that DM script is a strongly typed language. Any variable used within a given scope of a script must be declared to have a single and definite type. This is again in contrast to Python, which allows variable types to be more fluid. Some think of this as a limitation, others find this to be a strength of the DM script language because it allows the interpreter to catch many common types of bugs and helps one arrive at a correctly executing program more quickly.