Search code examples
compiler-errorseiffel

Eiffel: local declaration in or and and failed to compile


The compiler is complaining about an unknown identifier, seems that it does not recognize any of my multiple declarations, where am I wrong?

if attached {INTEGER_REF} field.item as l_int
        or attached {INTEGER_64} field.item as l_int
        or ( attached {TUPLE} field.item as l_tuple and then attached {INTEGER_64} l_tuple.item (1) as l_int ) then
    Result.put_integer (l_int.to_integer_64, primary_key_db_column_name)
elseif attached {STRING} field.item as l_s then
    Result.put_string (l_s, primary_key_db_column_name)
end

enter image description here

Update

As this seems a valid expression I thought that if in every branch of my or a l_int is declared I should be able to use it in the then scope.

But it seems that this expression is valid

if attached a.b as l_b and then attached l_b.c as l_c then
    l_c.is_available_in_this_scope
    l_b.is_available_in_this_scope
else
    io.putstring ("you are wrong dear and either l_b and l_c are not available!")
end

While this one not!

if attached a.b as l_b and then attached l_b.c as l_c
        or attached a.x as l_b and then attached l_x.d as l_c then
    l_c.is_available_in_this_scope -- Compiler complain about l_c
    l_b.is_available_in_this_scope -- Compiler complain about l_b
else
    io.putstring ("you are wrong dear and either l_b and l_c are not available!")
end

With my code

This compiles

    if attached {INTEGER_REF} field.item as l_int then
        Result.put_integer (l_int.to_integer_64, primary_key_db_column_name)
    elseif attached {INTEGER_64} field.item as l_int then
        Result.put_integer (l_int, primary_key_db_column_name)
    elseif attached {TUPLE} field.item as l_tuple and then attached {INTEGER_64} l_tuple.item (1) as l_int then
        Result.put_integer (l_int, primary_key_db_column_name)
    elseif attached {STRING} field.item as l_s then
        Result.put_string (l_s, primary_key_db_column_name)
    else
        logger.write_error ("to_json-> Type not found in matching:" + field.item.out)
        check
            not_found_item_type: False
        end
    end

While this not

if attached {INTEGER_REF} field.item as l_int then
    Result.put_integer (l_int.to_integer_64, primary_key_db_column_name)
elseif attached {INTEGER_64} field.item as l_int
        or attached {TUPLE} field.item as l_tuple and then attached {INTEGER_64} l_tuple.item (1) as l_int then
    Result.put_integer (l_int, primary_key_db_column_name) -- Unknown identifier `l_int`
elseif attached {STRING} field.item as l_s then
    Result.put_string (l_s, primary_key_db_column_name)
else
    logger.write_error ("to_json-> Type not found in matching:" + field.item.out)
    check
        not_found_item_type: False
    end
end

Solution

  • Object tests have scopes. Denoting an object test with OT, its scope for

    if OT         then A else B end
    if OT and ... then A else B end
    if OT or  ... then C else B end
    

    is only A. Therefore, for a disjunction, the scope is empty, and you cannot use the corresponding object test local in any of the branches.

    If there are two object tests in a conditional, their scopes may overlap or not:

    if OT1 and      OT2 then A else B end
    if OT1 and then OT2 then A else B end
    if OT1 or       OT2 then C else B end
    

    Here, as before, the object test local variable of OT1 has a scope A. In addition, for and then, the scope includes OT2, in particular, OT2 can use the local of OT1. For the same reason, OT2 cannot use the same object test local of OT1.

    For the disjunction, scopes of object test locals of both OT1 and OT2 are empty. To be more informative, the same code with mnemonic names looks like:

    if attached e1 as x and      attached e2_without_x as y then use_x_and_y else B end
    if attached e1 as x and then attached e2_with_x    as y then use_x_and_y else B end
    if attached e1 as x or       attached e2_without_x as y then no_x_no_y   else B end
    

    It would be still possible to rewrite the example with just one first branch if the type of the all involved expressions is the same (that is not the case, because there are types INTEGER_64 and INTEGER_REF):

    if attached
           if attached {INTEGER_64_REF} field.item as i then
               i
           elseif
               attached {TUPLE} field.item as t and then
               t.count > 0 and then
               attached {INTEGER_64_REF} t.item (1) as i
           then
               i
           else
               Void
           end
       as j
    then
       -- Use j
    ...
    

    but this becomes too cumbersome, and using multiple branches or temporary local variables looks like a better alternative.