Skip to content

Type coersion/multiple return types #69

Closed

Description

@hinescd

As discussed in the meeting today, we need to make a design decision on how/if we should allow functions to have multiple return types. Consider the following example:

add(a, b) {
    return a + b
}

test add(1, 2) == 3
test add(1.0, 2.0) == 3.0

The options we've identified are to either allow the function to have two different return types based on parameter type, or coerce the Integer sum for the integer binding into a Double to match the Double sum for the Double binding. Coercion would only ever occur from Integer to Double.

Activity

  1. hinescd commented on Jan 23, 2020

    @hinescd
    ContributorAuthor

    In my opinion, it makes more sense to restrict a function to having a single return type. The LJ programmer will think of the add function as one single entity, not several different entities for each parameter binding. If the add function is only one entity, then it makes sense for it to have one return type. It either returns an Integer or a Double.

  2. Zamua commented on Jan 24, 2020

    @Zamua
    Contributor

    If we believe that a function should have a single return type then we will need to revisit all of the operators. Operators are nothing more than functions with some syntactic sugar to allow infix notation.

    For example:
    Let + be the "integer addition function"
    Let ~+ be the "floating point addition function"

    1 + 1 // 2
    1 + 1.0 // type error, integer addition function expects integer parameters
    1.0 + 1.0 // type error, integer addition function expects integer parameters
    
    1.0 ~+ 1.0 // 2.0
    1 ~= 1.0 // type error, floating point addition function expects floating point parameters
    1 ~= 1 // type error, floating point addition function expects floating point parameters
    

    A similar pattern would follow for division, multiplication, and modulus.

    The other way to maintain consistency would be to treat functions similar to how operators are currently treated.

    For example:

    // This function is a wrapper around the '+' function.
    // The '+' function can have two possible return types: integer or float.
    // The return type is dependent on the input parameters, 'a' and 'b'.
    // If native operators can have multiple return types, but user defined functions cannot, then user defined functions cannot return expressions using native operators.
    // That would be extremely limiting, so user defined functions must also support multiple return types.
    add(a, b) {
        return a + b 
    }
    
    add(1,1) // 2
    add(1,1.0) // type error, can't unify integer and float
    add(1.0,1) // type error, can't unify integer and float
    add(1.0,1.0) // 2.0
    

    Java does not support overloading a function with multiple return types. To resolve this, we can mangle the function names by encoding their parameter and return types into the function name.

    For example:

    add(a, b) {
        return a + b
    }
    
    add(1,1)
    add(1.0,1.0)
    

    would become

    public class Addition {
        public static void main(String[] args) {
            addIntegerIntegerInteger(1,1)
            addDoubleDoubleDouble(1.0,1.0)
        }
    
        public Integer addIntegerIntegerInteger(Integer a, Integer b) {
            return a + b
        }
    
        public Double addDoubleDoubleDouble(Double a, Double b) {
            return a + b
        }
    }
    
  3. hinescd commented on Jan 24, 2020

    @hinescd
    ContributorAuthor

    I don't think that's the strongest argument because there are multiple languages (Java, C, etc) that only let functions have one return type but allow operators like + to evaluate to multiple types.

    Also, potentially another issue, the compiler does allow addition between an int and a double (unifying them to a double), and has for the entire time I've been working on it. Is that a bug?

  4. Zamua commented on Jan 24, 2020

    @Zamua
    Contributor

    I think that existing languages like Java, C, etc. are inconsistent and confusing. Objects in Java are supposed to be instantiated using the 'new' keyword, and Java considers strings to be objects, yet they make an exception for them and consider string literals as objects.

    Here's another example in Java that could be the basis for an argument against type coercion:

    Object o = true ? new Integer(1) : new Double(1);
    System.out.println(o); // 1.0
    

    Due to type coercion the return type of the ternary becomes a double. I assert that this is un-intuitive (at least at first glance and without the background knowledge of type coercion in java).

    I don't think we should make design decision on the basis that developers of other languages also made them. I think that we should be able to justify the decisions we make with some set of values, one of which I think should be consistency.

    Good find on the current coercion in the compiler. Whether or not it's a bug depends on if we can come to a consensus on type coercion. If we want to use type coercion, then it's not a bug. If we decide we don't want type coercion then it is a bug.

  5. hinescd commented on Jan 24, 2020

    @hinescd
    ContributorAuthor

    Regardless of whether we do coercion, I think we should allow operating on ints and doubles together. The programmer just sees them as numbers, so it would be frustrating for them to not be able to use them together, like if they had an assignment to convert integer feet + inches to a floating point representation in feet (6 feet 6 in -> 6.5 feet).

    Going back to the issue at hand, I think allowing a function to have multiple return types would be confusing to the programmer. Why do calls to the same function have different return types at different points in the program? The programmer didn't explicitly type their variables, so the differences in bindings might not be obvious to them if they pass variables as the parameters and the return type is determined by the type of the parameters.

  6. lam2mo commented on Jan 24, 2020

    @lam2mo
    Member

    I think I'm coming around to the idea of allowing int -> double coercions only. We would for the most part gloss over architectural details in an intro course, and the fact that in Java Integer is signed and 32 bits while Double is 64-bits means that it is in fact a safe coercion "under the hood." It's fairly intuitive to even a high schooler that you can go from a whole number to a fraction without data loss, but that the opposite direction is not at all safe.

    I'm less convinced that we should force single return types. One thing an instructor who inclines toward functional programming may wish to convey in an intro course is that in some sense there's little difference between operators and functions (e.g., a unary operator is basically syntactic sugar around a function with a single parameter, and a binary operator is basically syntactic sugar around a function with two parameters).

    Perhaps a path forward is to go ahead and allow the int -> double coercions as we've been doing, and let Charlie "fix" the current inference issue as he planned. Then we can add a new feature issue for supporting multiple return types via name mangling (as in C++). As I see it, this isn't really necessary for either ongoing thesis, although it would certainly be nice if Charlie could implement it as part of his static analysis work.

  7. hinescd commented on Jan 24, 2020

    @hinescd
    ContributorAuthor

    I'd be happy with taking that path forward

  8. added this to the v1.0 milestone on Feb 12, 2020
  9. lam2mo commented on Feb 26, 2020

    @lam2mo
    Member

    Fixed by d474abd

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions