nevalang / neva

🌊 Dataflow programming language with static types and implicit parallelism. Compiles to machine code and Go
https://nevalang.org
MIT License
128 stars 8 forks source link

[TypeSystem]: Compatibility between union argument and non-union constraint (Ternary) #737

Open emil14 opened 4 weeks ago

emil14 commented 4 weeks ago

Generic Problem

This is valid TypeScript code:

const f = (a: {}) => console.log(a)
let x: {a: any} | {b: any} = {a: 42}
f(x)

But in Nevalang it won't compile according to current type-system implementation:

    isConstraintInstance := constr.Lit.Empty()
    areKindsDifferent := expr.Lit.Empty() != isConstraintInstance
    isConstraintUnion := constr.Lit != nil &&
        constr.Lit.Type() == UnionLitType

    if areKindsDifferent && !isConstraintUnion {
        return fmt.Errorf(
            "%w: expression %v, constraint %v",
            ErrDiffKinds,
            expr.String(),
            constr.String(),
        )
    }

I.e. we only support non-union arg + union constr but not vice versa

Ternary Case

Proper Ternary typing would be <T, Y> -> T | Y but that lead to problem - receiver won't be compatible with the union. Example: true ? {} : {a: 42} -> .... Let's say ... accepts {} (any structure). Compiler will return error because {} | {a int} is union argument and {} is non-union constraint - this is unsupported. Meanwhile it's obvious from common sense point of view that {} | {a int} is perfectly valid because both of them compatible with {}.

Possible Solution

Support areKindsDifferent not just when isConstraintUnion but also when isArgUnion. In that case what we should do is to iterate over each union member and check compatibility with the constraint. Kinda the same way we do right now for inverted case.