0

Is there a way (I'm sure there is out of runtime check...) to specify that a parameter or a variable in general conforms to multiple types? to avoid doing something such as

work (a_printer: PRINTER; a_scanner: SCANNER)
    do
      a_printer.print
      a_scanner.scan
        -- OR without second parameter
      if attached {SCANNER} a_printer as l_scanner then
         l_scanner.scan
      else
         throw RuntimeError
      end
    end
Pipo
  • 4,653
  • 38
  • 47
  • 1
    I already saw a similar example (with two arguments) in the code of EiffelStudio, in: AST_INSTANCE_FREE_CHECKER.process_access (https://github.com/EiffelSoftware/EiffelStudio/blob/master/Src/Eiffel/eiffel/AST/visitor/ast_instance_free_checker.e#L311) – Eric Bezault Jun 28 '19 at 18:56

2 Answers2

1

I think that, if possible, you should use a common ancestor to your multiple types. If you cannot (if you are using library types), you can create descendant classes (MY_PRINTER inherit from PRINTER and DEVICE and MY_SCANNER inherit from SCANNER and DEVICE). Another way is to use ANY as the type, but it is not the best solution.

Louis M
  • 576
  • 2
  • 4
  • Thx, its useful, but nothing seems to be provided by the language or library to do that... – Pipo Jun 29 '19 at 11:52
1

If feature work belongs to a class that may have formal generic parameters, it could be defined as taking one argument of the corresponding formal generic type:

class X [D -> {PRINTER, SCANNER}] feature
    work (device: D)
        do
            device.scan
            device.print
        end
end

Then, at the caller site, one could make the call

x.work (multi_function_device)

where x has an appropriate type, e.g. X [MULTI_FUNCTION_PRINTER].

If work could also be declared and implemented as a class feature, the temporary variable could be avoided:

{X [like multi_function_device]}.work (multi_function_device)

If the auxiliary class X is not an option, the current version of the language provides no means to declare an argument as conforming to more than 1 type (e.g., work (d: {PRINTER, SCANNER})), so you would have to resort to preconditions like

work (p: PRINTER)
    require
        attached {SCANNER} p
    do
        check
            from_precondition: attached {SCANNER} p as s
        then
            s.scan
        end
        p.print
    end
Alexander Kogtenkov
  • 5,770
  • 1
  • 27
  • 35
  • Thx, nice and complete answer, what does the check do in case of assertions removal? in that case at runtime, only a print will be done in your example isn't it? So a better coverage implementation would be replacing the check by a `if attached {SCANNER} p as s then ...` or did I misunderstood something? – Pipo Jun 29 '19 at 15:17
  • Is your first implementation example a variant of implementing a new parent class inheriting from both SCANNER and PRINTER? as far as I understand its the case, or does it add something? – Pipo Jun 29 '19 at 15:19
  • 1
    The check instruction with `then` part makes the assertion mandatory, i.e. the test will always be performed, and when it evaluates to `False`, an exception will be triggered regardless of assertion monitoring settings, i.e. even in finalized system. – Alexander Kogtenkov Jun 29 '19 at 15:23
  • 1
    Class `X` is not a parent. It has a formal generic that can be instantiated with a specific actual generic type, implementing both `PRINTER` and `SCANNER`. – Alexander Kogtenkov Jun 29 '19 at 15:25