E50030: types are not compatible with each other

Category: E50xxx Common errors · Reported in phase: FULL_RESOLUTION · Classification: INCOMPATIBLE_TYPES · All error codes

What went wrong

You're trying to use one type where a different, incompatible type is expected. EK9 is strongly typed - types must match or be compatible via coercion.

The operators that return ONE of their operands - '?:', '<?', '>?' and the ternary 'condition <- this else that' - report this error at the operator when the operands share no type. Their result is one of the operands, so its type must be one the operands already have:

  1. the same type, or a subtype (including 'Any' when an operand is DECLARED as 'Any')
  2. one operand promoting ('#^') to the other's type Integer ?: Float -> Float
  3. the nearest common super class or trait KidA ?: KidB -> their super

Never an invented 'Any' and never a third type. Each of these is this error:

either <- count ?: text //Integer and String share only 'Any'
shown <- letter ?: pattern //both promote to String; neither IS one
known <- maybeCount ?: 0 //an 'Optional of Integer' is not an Integer

So are two types that each promote to the other ('ping ?: pong' - ambiguous), and two operands whose nearest common types are two unrelated traits (ambiguous too).

A List or Dict literal with no declared type (and a range, and a multi-source 'cat') takes the type its elements share by the same rule, plus one more step: a single type every element promotes to ([letter, pattern] is a List of String). Elements that share no type but 'Any', or share several equally near ones, are this error at the literal - never an invented 'List of Any':

mixed <- [1, "two"] //Integer and String share only 'Any'
byName <- {"count": 1, "name": "two"}

A declared target supplies the element type, and any element that fits it is accepted:

//A declared target supplies the element type
mixed as List of Any: [1, "two"]
byName as Dict of (String, Any): {"count": 1, "name": "two"}

A target is a declared variable, property or returning variable, an assignment to one, or an enclosing declared literal. A literal passed straight as an argument has none (its type takes part in choosing an overload): declare a variable of the parameter's type and pass that.

A copy 'target :=: source' is only between objects of the same class (a copy into a Leaf through a Base-typed reference throws when the source is a different class). When the DECLARED types already prove it can never succeed - neither is the other's type, as two sibling classes 'Leaf' and 'Sibling' of one 'Base' - it is this error:

leaf :=: sibling //no object is both a Leaf and a Sibling

Worked examples

Compilable examples demonstrating this error and the correct fix:

  • ek9 -q 39 (How do Integer and Float work in EK9?)
  • ek9 -q 50 (How do function returns work without a return statement i...)
  • ek9 -q 168 (How do I provide a fallback value when something might be...)
  • ek9 -q 2061 (What type does 'a ?: b' have when a and b are different types?)
  • ek9 -q 55 (How do I pass functions as delegates in EK9?)
  • ek9 -q 124 (How do I group and aggregate collection data in EK9?)

[EK9 AUTOMATIC TYPE COERCION] Limited automatic promotion (one step, never chained):

  • Integer → Float // OK - integers promote to floats
  • Character → String // OK - characters promote to strings
  • Byte → Integer // OK - but Byte does NOT reach Float (that would be two steps)

How to fix it

  1. Ensure types match exactly, or
  2. Use automatic coercion where supported
  3. Use explicit conversion constructors for other conversions
  4. For '?:', the '<?' family and the ternary, give both operands a type they share (each way is in good_forms.ek9 in the [EXAMPLE] section below):
  • convert one explicitly ('$count' for a String, 'Float(count)')
  • promote one explicitly with '#^' when the two types promote to each other
  • assign one operand to a variable of the super or trait type wanted first
  • for an 'Optional of T' use 'maybeCount.getOrDefault(0)' rather than '?:'
  1. For a List or Dict literal whose elements share no type, keep it to one type, or declare the element type you want: 'items as List of Any: [1, "two"]'
  2. For a copy between unrelated classes, copy from an object of the target's own class, or build the target from the source's values by hand (a constructor, or a copy you write yourself)

Common causes

  • Assigning wrong type to variable
  • Return type doesn't match declaration
  • Parameter type doesn't match method signature
  • Coalescing operators (?:) or a ternary with operands of different types
  • A List or Dict literal mixing types that share only 'Any' with no declared element type
  • A ternary in a stream 'map' returning a different type in each arm ('item > 1 <- item else "small"')
  • A copy (':=:') between two sibling classes of one hierarchy

See also

  • E50030: type mismatch (this error)
  • E50020: wrong construct genus
  • E08269: a coalescing operator mixed with another operator without parentheses

Examples

Every example is compiled by the EK9 compiler: the first fails exactly as captioned, the others fix it.

Fails with E50030: types are not compatible with each other

#!ek9
defines module error.e50030.bad

  defines class
    Base as open
      id as Integer: 0
      default operator
    Leaf extends Base as open
      default operator
    Sibling extends Base as open
      default operator

  defines function

    demo()
      <- rtn as Integer?
      text <- "42"
      rtn: text

    coalescing()
      ->
        count as Integer
        text as String
        letter as Character
        pattern as RegEx
        maybeCount as Optional of Integer
      <- rtn as String?

      either <- count ?: text //Integer and String share only 'Any'
      shown <- letter ?: pattern //both promote to String; neither IS one
      known <- maybeCount ?: 0 //an 'Optional of Integer' is not an Integer
      rtn: `${either} ${shown} ${known}`

    literals()
      <- rtn as String?
      mixed <- [1, "two"] //Integer and String share only 'Any'
      byName <- {"count": 1, "name": "two"}
      rtn: `${length mixed} ${length byName}`

    copies()
      ->
        leaf as Leaf
        sibling as Sibling

      leaf :=: sibling //no object is both a Leaf and a Sibling

Fixed: Give each value the type it is used as: convert it, declare the target, or copy from the same class

#!ek9
defines module error.e50030.good.fix

  defines class
    Base as open
      id as Integer: 0
      default operator
    Leaf extends Base as open
      default operator
    Sibling extends Base as open
      default operator

  defines function

    demo()
      <- rtn as Integer?
      text <- "42"
      rtn: Integer(text)

    coalescing()
      ->
        count as Integer
        text as String
        letter as Character
        pattern as RegEx
        maybeCount as Optional of Integer
      <- rtn as String?

      either <- $count ?: text //convert one: both are a String
      shown <- $letter ?: $pattern //convert both to a String
      known <- maybeCount.getOrDefault(0) //an Optional supplies its own default
      rtn: `${either} ${shown} ${known}`

    literals()
      <- rtn as String?
      mixed as List of Any: [1, "two"] //a declared target supplies the element type
      byName as Dict of (String, Any): {"count": 1, "name": "two"}
      rtn: `${length mixed} ${length byName}`

    copies()
      ->
        leaf as Leaf
        source as Leaf

      leaf :=: source //a copy is between objects of the same class

Fixed: Convert explicitly

#!ek9
defines module error.e50030.good

  defines function

    demo()
      <- rtn as Integer?
      text <- "42"
      rtn: Integer(text)

Fixed: Give the operands a type they share, or declare the type a literal holds

#!ek9
defines module error.e50030.good.forms

  defines function

    coalescing()
      ->
        count as Integer
        amount as Float
        text as String
        letter as Character
        pattern as RegEx
        maybeCount as Optional of Integer
      <- rtn as String?
      //Integer promotes to Float: the result is a Float
      measure <- count ?: amount
      //Convert one operand explicitly
      either <- $count ?: text
      //A List literal takes the one type every element promotes to: a List of String
      shapes <- [letter, pattern]
      //An Optional supplies its own default
      known <- maybeCount.getOrDefault(0)
      rtn: `${measure} ${either} ${shapes} ${known}`

    literals()
      <- rtn as String?
      //A declared target supplies the element type
      mixed as List of Any: [1, "two"]
      byName as Dict of (String, Any): {"count": 1, "name": "two"}
      rtn: `${length mixed} ${length byName}`

Questions and answers

These Q&A entries show E50030 and its fix: