ponyc
ponyc copied to clipboard
Type inference for methods in union types is too simplistic
While working with union types while is seems reasonable to call u.definition()
, on a variable u
, declared with a union type, as in the code snippet below. However, this produces the following error:
ponymain.pony:26:16: a member of the union type has an incompatible method signature
let def_u = u.definition()
Code with method call on a union type
class val B
fun definition(): None => None
class val A
fun definition(): String => ""
// fun definition(): T => "" // however the type inference works if A or B use the union in the definition
type U is ( B | A)
type T is (String val | None)
interface val I
fun definition(): (String val | None)
actor Main
new create(env: Env) =>
let a: A = A
let b: B = B
let u: U = b
let i: I = u
let def_i = i.definition() // works when first referencing u using the the interface, i
let def_b = b.definition() // expected to work
let def_a = a.definition() // expected to work
let def_u = u.definition() // <--- type inference error on this line i.e. when calling 'definition' directly on the union type
Problem statement
Currently it seems that the type inference requires methods for the same dispatch target to share the same return type, or to have a return type that is a subtype of the return type of one of the methods signatures in the union.
However, in terms of the intent of structural typing, it would seem reasonable to expect that the type signature for the return type of a "common" method, in a union, would first be computed as the union of the potential return types.
@jemc commented in the Zulip chat, and provided a more details description of the potential type handling rules that might be applied, see below.
Zulip chat notes
From: Joe Eli McIlvain (via Zulip chat)
I've run into this same issue before -
ponyc
doesn't deal well with type unions as method receivers, even though it would be sound to do so. [cut] it basically boils down to what you might expect:
- a method of that name must exist on each type in the union
- each argument must be a subtype of the corresponding parameter types from the various union methods
- the return type is the union of the return types of the various union methods
@jemc , @jasoncarr0 , @SeanTAllen - following on from the Zulip chat regarding type inference for method signatures in a union, please see the issue that has been drafted.
A quick superficial look at the error message and the code leads to here:
We discussed this a bit. Refcaps will also need to match all, One edge case / compilcation could be when one of the methods is tag, and another is box/ref, so that only the latter needs automatic receiver recovery. It's probably not likely but in that case only the ref method arguments need to be sendable.