Enumerations in Eiffel 
Author Message
 Enumerations in Eiffel

  I do enumerations in Eiffel in a certain way, and I was wondering
if it's really the best way.

  I create a mixin class with all the values:

class COLOUR_HANDLER

  feature{ANY}

    red, green, blue : INTEGER is unique

    colour_is_valid (Colour : INTEGER) is
      do
        Result := (
          Colour = red or
          Colour = green or
          Colour = blue
        )
      end
end

  Any class that want to use the enumeration can inherit from this
and have access to the red, green, and blue identifiers.  It can
use colour_is_valid in assertions to make sure it's getting valid
values.

  Also, I can do this:

class COLOUR

  inherit
    INTEGER
    end
    COLOUR_HANDLER
    end

  invariant
    valid: colour_is_valid (Current)

end

  Is this really the best way?

 -PD
--
--
Patrick Doyle



Sun, 09 Jan 2000 03:00:00 GMT  
 Enumerations in Eiffel

Quote:
Patrick Doyle writes:
>   I do enumerations in Eiffel in a certain way, and I was wondering
> if it's really the best way.

Your way is fine, and is what many Eiffel users would use. However, I'll
mention some caveats and present some variations that you may wish to
consider.

Quote:
>   I create a mixin class with all the values:

> class COLOUR_HANDLER
>   feature{ANY}
>     red, green, blue : INTEGER is unique

This approach is elegant when you don't care what the actual INTEGER codes
are. However, you can't take advantage of Eiffel's guaranteed initialization.
Suppose you have:

   feature {ANY}
      red, green, blue: INTEGER is unique
      my_colour: INTEGER
   ...
   invariant
      colour_is_valid(my_colour)

Attribute 'my_colour' will be initialized to zero, which is likely to fail the
invariant. Therefore, you must explicitly set 'my_colour' to a starting value
in every creation procedure. With the following scheme, every colour will be
initialized to red by default, and there is no need to set 'my_colour' in the
creation procedure:

   feature {ANY}
      red: INTEGER is 0
      green, blue: INTEGER is unique

If you are writing colour values to a disk file or across a communications
link (e.g. to exchange data between different programs, or even between
different versions of the same program) you can't count on the "unique" values
being the same from compilation to compilation. So, you need to revert to a
manual enumeration:

   feature {ANY}
      red: INTEGER is 0
      green: INTEGER is 318
      blue: INTEGER is 319

I don't simply use (0, 1, 2...), because there may be other enumerations in
the same class. For example:

   (apple, pear, banana)

If we use (0, 1, 2...) for every enumeration, then we lose any semblance of
type checking. It becomes possible, for example, to assign a pear to
'my_colour'. So, for (apple, pear, banana) I might use (0, 42, 43).

There is a weakness in this scheme because the value "0" is used for every
enumeration so it's not fully typesafe (but if it is made fully typesafe then
automatic default initialization is not possible).

Quote:
>     colour_is_valid (Colour : INTEGER) is
>       do
>         Result := (
>           Colour = red or
>           Colour = green or
>           Colour = blue
>         )
>       end

This works well for small enumerations. For larger enumerations, e.g.

   (black, brown, red, orange, yellow, green, blue, violet, grey, white)

the following scheme can be used:

      black:  INTEGER is 0
      brown:  INTEGER is 74
      red:    INTEGER is 75
      ...
      grey:   INTEGER is 81
      white:  INTEGER is 82

      colour_is_valid(colour: INTEGER): BOOLEAN is
         do
            result := colour = black or colour >= brown and colour <= white
         end

This requires that the colours brown..white have consecutive INTEGER codes,
which is the case in the example above and is also guaranteed if you are using
the "unique" keyword.

Quote:
>   Also, I can do this:

> class COLOUR

>   inherit
>     INTEGER
>     end
>     COLOUR_HANDLER
>     end

>   invariant
>     valid: colour_is_valid (Current)

> end

When you inherit from an expanded type (e.g. INTEGER), the expanded status is
not inherited. So, your invariant should probably read

   valid: colour_is_valid(current.item)

However, inheritance from basic types (e.g. INTEGER) doesn't work well on many
Eiffel compilers (and doesn't work at all on some of them) so this solution is
not portable.

On a minor note, you may wish to consider your choice of class name
("COLOUR_HANDLER") as it isn't actually doing any "handling", i.e. it has no
real functionality. Eiffel programmers often choose a name like "COLOUR_CODES"
for this kind of "mixin" class.

Now, consider what happens if you have a class that deals with both colours
and fruit. You may need both of these enumerations:

   (red, orange, green)
   (apple, orange, banana)

If these enumerations are defined by others, you can't be sure to avoid name
clashes like "orange" in this example.

Here's the scheme I use for enumerations, which resolves many of the issues
described above:

1. Write a class for each enumeration:

   class COLOUR_CODES
   feature {ANY}
      red: INTEGER is 0
      orange: INTEGER is 109
      green: INTEGER is 110
      valid_colour_code(colour_code: INTEGER): BOOLEAN is
         do
            result := colour_code = red
             or colour_code >= orange and colour_code <= green
         end
   end

   class FRUIT_CODES
   feature {ANY}
      apple: INTEGER is 0
      orange: INTEGER is 176
      banana: INTEGER is 177
      valid_fruit_code(fruit_code: INTEGER): BOOLEAN is
         do
            result := fruit_code = apple
             or fruit_code >= orange and fruit_code <= banana
         end
   end

2. Access the codes by expanded attributes rather than by inheritance:

   class APPLICATION

   creation {ANY}
      make

   feature {NONE}  -- creation
      make is
         do
            ...
            colour := cc.orange
            ...
            fruit := fc.orange
            ...
         end

   feature {ANY}  -- queries
      colour: INTEGER
      fruit: INTEGER

   feature {NONE}  -- mixins
      cc: expanded COLOUR_CODES
      fc: expanded FRUIT_CODES

   invariant
      valid_colour: cc.valid_colour_code(colour)
      valid_fruit: fc.valid_fruit_code(fruit)

   end

When you have "expanded" mixins, most Eiffel compilers will optimise away the
reference to the mixin class (because it has no attributes requiring storage),
so there is no space penalty in the client class.

Some enumerations may need to be extended. The scheme above makes this easy.

Example 1: EXTENDED_COLOUR_CODES can inherit from COLOUR_CODES, add some more
colours, and add feature 'valid_extended_colour_code'.

Example 2: PAINT_COLOUR_CODES can inherit multiply from COLOUR_CODES,
METALLIC_COLOUR_CODES and FLUORESCENT_COLOUR_CODES, and add feature
'valid_paint_colour_code'.

With the techniques above, Eiffel enumerations can provide most of the
functionality and safety offered by enumerations in languages like Ada and
Pascal. However, there are some limitations. For example, if you have a local
entity "colour: INTEGER" within some routine you cannot depend on the class
invariant to ensure that it only has valid colours assigned to it. Instead,
you can use 'check' to make this test manually, but this is tedious.

No doubt others will have some further suggestions to offer regarding
enumerated types. In particular, I know that some Eiffel programmers build
enumerations dynamically by registering new codes with an enumeration manager
class.

Regards,
Roger
--
--
-- Roger Browne, 6 Bambers Walk, Wesham, PR4 3DG, UK | Ph 01772-687525
-- Everything Eiffel: http://www.eiffel.demon.co.uk/ | +44-1772-687525



Mon, 10 Jan 2000 03:00:00 GMT  
 Enumerations in Eiffel



Quote:
>Patrick Doyle writes:

>>   I do enumerations in Eiffel in a certain way, and I was wondering
>> if it's really the best way.

>Your way is fine, and is what many Eiffel users would use. However, I'll
>mention some caveats and present some variations that you may wish to
>consider.

  Perfect.

Quote:
>>   I create a mixin class with all the values:

>> class COLOUR_HANDLER
>>   feature{ANY}
>>     red, green, blue : INTEGER is unique

>This approach is elegant when you don't care what the actual INTEGER codes
>are. However, you can't take advantage of Eiffel's guaranteed initialization.

  Hmm.  Well, I'm not terribly confortable with automatic initialization
anyway.  But perhaps that's just because I don't know exactly what the
rules are.  What are the rules, anyway?

  Any, hey, while we're at it, is there an Eiffel language reference
out there I could get so I don't have to ask you guys all these questions?
One nice big text file would be fine.

Quote:
>Attribute 'my_colour' will be initialized to zero, which is likely to fail the
>invariant. Therefore, you must explicitly set 'my_colour' to a starting value
>in every creation procedure.

  That's not necessarily a bad thing.  Make people choose a colour.

Quote:
>If we use (0, 1, 2...) for every enumeration, then we lose any semblance of
>type checking. It becomes possible, for example, to assign a pear to
>'my_colour'. So, for (apple, pear, banana) I might use (0, 42, 43).

  But not if we use a class that represents the enumerated type, right?

Quote:
>>     colour_is_valid (Colour : INTEGER) is
>>       do
>>         Result := (
>>           Colour = red or
>>           Colour = green or
>>           Colour = blue
>>         )
>>       end

>This works well for small enumerations. For larger enumerations, e.g.

>   (black, brown, red, orange, yellow, green, blue, violet, grey, white)

  Then with the unique mechanism, you can just check that it's between
black and white, right?

Quote:
>> class COLOUR

>>   inherit
>>     INTEGER
>>     end
>>     COLOUR_HANDLER
>>     end

>>   invariant
>>     valid: colour_is_valid (Current)

>> end

>When you inherit from an expanded type (e.g. INTEGER), the expanded status is
>not inherited. So, your invariant should probably read

>   valid: colour_is_valid(current.item)

  Does ".item" just refer to a member called item?

Quote:
>On a minor note, you may wish to consider your choice of class name
>("COLOUR_HANDLER") as it isn't actually doing any "handling", i.e. it has no
>real functionality. Eiffel programmers often choose a name like "COLOUR_CODES"
>for this kind of "mixin" class.

  Ok.

Quote:
>Now, consider what happens if you have a class that deals with both colours
>and fruit. You may need both of these enumerations:

>   (red, orange, green)
>   (apple, orange, banana)

>If these enumerations are defined by others, you can't be sure to avoid name
>clashes like "orange" in this example.

  Could you use a rename clause, and make them orange_colour and
orange_fruit?

Quote:
>Example 1: EXTENDED_COLOUR_CODES can inherit from COLOUR_CODES, add some more
>colours, and add feature 'valid_extended_colour_code'.

  Doesn't a subclass have to satisfy the superclass' invariant?  If so,
then using a class to represent the enumerated type, with an invariant
for the possible values, would prevent people from extending it.  Really,
I guess the extended enumeration is not a subtype *or* a supertype
of the original.  I suppose it makes sense to have to create an
entirely new type.

Quote:
>No doubt others will have some further suggestions to offer regarding
>enumerated types. In particular, I know that some Eiffel programmers build
>enumerations dynamically by registering new codes with an enumeration manager
>class.

  Why have enumerated types not simply been added to the language?

  Thanks for your suggestions.

 -PD
--
--
Patrick Doyle



Mon, 10 Jan 2000 03:00:00 GMT  
 Enumerations in Eiffel

Quote:
Patrick Doyle writes:
>   Hmm.  Well, I'm not terribly confortable with automatic initialization
> anyway.  But perhaps that's just because I don't know exactly what the
> rules are.  What are the rules, anyway?

INTEGER    -> 0
REAL       -> 0.0
DOUBLE     -> 0.0
CHARACTER  -> '%/0/'  -- the null character (ASCII 0)
BOOLEAN    -> false
POINTER    -> default_pointer
references -> void

I like automatic initialization because it reduces code clutter, and means
that many classes don't require a creation routine at all.

Automatic initialization of BOOLEANs affects the choice of feature names. For
example, a BOOLEAN attribute or function indicating whether or not an email
message has been sent might be named 'is_sent' rather than 'is_unsent' because
we want the automatic initialization to be for the unsent (false) state.

Quote:
>   Any, hey, while we're at it, is there an Eiffel language reference
> out there I could get so I don't have to ask you guys all these questions?

There's a brief but good introduction to Eiffel at
http://www.eiffel.com/doc/manuals/language/intro/

There's an online Eiffel syntax and summary of the kernel library features at
http://www.totalweb.co.uk/gustave/ref/

But every Eiffelist really needs "Eiffel The Language" by Bertrand Meyer
(Prentice-Hall, ISBN 0-13-247925-7) and "Object Oriented Software Construction
2nd Edition" (Prentice-Hall, ISBN 0-13-629155-4). If you are using Eiffel/S
1.3 then a great book is "Eiffel An Introduction" by Robert Switzer
(Prentice-Hall).

Quote:
> >If we use (0, 1, 2...) for every enumeration, then we lose any semblance of
> >type checking. It becomes possible, for example, to assign a pear to
> >'my_colour'. So, for (apple, pear, banana) I might use (0, 42, 43).

>   But not if we use a class that represents the enumerated type, right?

Right - if anyone has a good pattern for this which is reasonably efficient
and doesn't cause problems with inheritance from INTEGER, please post it!

Quote:
> >   (black, brown, red, orange, yellow, green, blue, violet, grey, white)

>   Then with the unique mechanism, you can just check that it's between
> black and white, right?

Yes - the codes for "unique" attributes declared together are guaranteed to be
contiguous and ascending.

Quote:
> >When you inherit from an expanded type (e.g. INTEGER), the expanded status
> >is not inherited. So, your invariant should probably read

> >   valid: colour_is_valid(current.item)

>   Does ".item" just refer to a member called item?

Yes. The reference equivalent of class INTEGER is called INTEGER_REF and
includes a feature 'item' that returns the (expanded) INTEGER value.

Quote:
> >Now, consider what happens if you have a class that deals with both colours
> >and fruit. You may need both of these enumerations:

> >   (red, orange, green)
> >   (apple, orange, banana)

> >If these enumerations are defined by others, you can't be sure to avoid
> >name clashes like "orange" in this example.

> Could you use a rename clause, and make them orange_colour and
> orange_fruit?

Yes, but sometimes you have a large enumeration with only one clash. It's
tedious to rename the entire enumeration, yet I find the names confusing if
some are renamed and some are not. With the expanded mixin approach you
already have name qualification, so no renaming is needed:

   fruit: expanded FRUIT_CODES
   colour: expanded COLOUR_CODES

Quote:
> >Example 1: EXTENDED_COLOUR_CODES can inherit from COLOUR_CODES, add some
> >more colours, and add feature 'valid_extended_colour_code'.

>   Doesn't a subclass have to satisfy the superclass' invariant?  If so,
> then using a class to represent the enumerated type, with an invariant
> for the possible values, would prevent people from extending it.

That's true. With your class representation, you can simply redefine
'valid_colour_code' in the descendant, instead of adding a new feature
'valid_extended_colour_code'. Then, the invariant is unchanged.

Quote:
> I guess the extended enumeration is not a subtype *or* a supertype
> of the original.  I suppose it makes sense to have to create an
> entirely new type.

In my own software, I have always treated each enumeration as a separate type,
even if I use inheritance to help build it. I've yet to see a compelling
example where a subtype relationship is appropriate (which is not to say that
such examples don't exist).

Quote:
>   Why have enumerated types not simply been added to the language?

Bertrand Meyer's explanation is on pages 658 and 659 of OOSC 2nd Edition
("Enumerated types ... do not go well with the object-oriented method...").
Personally I do not find this compelling. There's an Eiffel-like language
called "Blue" developed at the University of Sydney which includes enumerated
types (see http:www.cs.usyd.edu.au/~blue/).

"Exercise for the reader" (from OOSC-2 p660):

   Show that a Pascal enumerated type of the form

      type ERROR = (Normal, Open_error, Read_error)

   can be represented by a class with a once-function for each value of the
   type.

Regards,
Roger
--
--
-- Roger Browne, 6 Bambers Walk, Wesham, PR4 3DG, UK | Ph 01772-687525
-- Everything Eiffel: http://www.eiffel.demon.co.uk/ | +44-1772-687525



Tue, 11 Jan 2000 03:00:00 GMT  
 Enumerations in Eiffel

Quote:
Patrick Doyle writes:
> >CHARACTER  -> '%/0/'  -- the null character (ASCII 0)

>   Woah, what the heck is that?!  Ok, my guess: % means it's an escape
> sequence of some sort, and the value between the slashes is the decimal
> ASCII value?

Correct.

Quote:
> Presumably, there are more codes besides the ASCII
> values, or else either the slashes or the % would be redundant.

Correct. For example, '%U' is another way to write the ASCII null character
('%N' already having been taken for Newline).

Quote:
> >POINTER    -> default_pointer

>   Oh.  I didn't know Eiffel had pointers.

It has, but not in the C sense. They are used for interfacing with software
written in C. You can pass them around within your Eiffel code, but can't do
much else with them except compare them for equality.

Quote:
>> [description of changing an invariant in a descendant by leaving the text
>> of the invariant unchanged but redefining a function used by the invariant]
>   Hey, that's sneaky.  The subclass really doesn't obey the invariant
> of the superclass, but it seems to.  That's creepy.  Should features
> used in invariants generally be "final"? (is that the word?  I mean
> they can't be overridden.)

Don't worry, most people are uncomfortable with this when they first meet it.
It's actually clean, pure and elegant. You just need to accept that what we
are doing here is expressing the invariant in a very high-level way. Our code
is depending on the same invariant that is expressed at the end of the class,
so there's no correctness problem.

Quote:
> On second thought, I think if enumeration A is a subset of B, then A is
> also a subclass of B.  Anywhere an instance of B is required, an
> instance of A can be substituted, right?

It's rarely that simple in the "real world". For example, standard electronic
components come in several logarithmic series:

  E3 series - multiples of 1.0, 2.2 and 4.7
  E6 series - multiples of 1.0, 1.5, 2.2, 3.3, 4.7...
  E12 series - multiples of 1.0, 1.2, 1.5, 1.8, 2.2, 2.7 ...

So, it's tempting to say that anywhere an E12 is required, an E6 or E3 can be
substituted. The problem is, by definition E3 components have wider tolerances
than E6 components, which have wider tolerances than E12 etc. So, the actual
substitutability in a real circuit design is that, say, an E12 1.8, 2.2 or 2.7
can be freely substituted for an E3 2.2, but an E3 2.2 cannot be used in
place of an E12 2.2. This can be modelled in Eiffel, but not purely by using
the supertype/subtype relationship.

Quote:
> This means that enumerations could only be extended by creating a
> superclass.  Some languages support this, but not Eiffel.

Eiffel "supports" it by making you use INTEGER codes for enumerations, thereby
leaving the "type-checking" to the application code, and making it
straightforward to implement applications like the electronic component
example above.

Quote:
> I think enumerated types tend toward case statements, which are the
> antithesis of polymorphism, and generally violate lots of OOP principles.
> However, they have their place.  Certainly, once we have programming
> environments which facilitate refactorings, the transformation from
> polymorphism to equivalent case statements and back is a useful one.

Enumerated types are the antithesis of OOP if they are used to model something
that would be better modelled by polymorphism. I don't believe they violate
any OO principle when used to model a pure enumeration, e.g. the months of the
year.

After all, Eiffel already includes two built-in enumerations - BOOLEAN and
CHARACTER. I doubt you'd want to transform your applications into ones where
every character code is represented by an object of a different class.

Quote:
> ...This brings up another question.  What's the
> difference between these:

> feature
>   goober1 : INTEGER
>   goober2 : INTEGER is
>     once
>       !!Result
>     end

>   Is the second like a class variable (to borrow a Smalltalk term) rather
> than an instance variable?

There are too many side-issues here for me to give you a complete answer in
this post, but here are some hints:

  - INTEGER is an expanded type, so it doesn't make sense to create it ("!!")

  - 'goober1' can be assigned to, whereas 'goober2' is a read-only function
     result

  - "once-functions" can be used to implement shared variables, and also to
    implement pseudo-constants of complex object types. EG, for a shared
    buffer inherit this class into all classes that must share the buffer:

       class SHARED_BUFFER
       feature
          buffer: BUFFER is
             once
                !!result
                result.set_size(1024)  --or other one-time initialization
             end
       end

    For a pseudo-constant, use this pattern:

       feature complex_i: COMPLEX is
          do
             !!result.make(0, 1)  -- i.e. the complex number (0 + 1i)
          end

Regards,
Roger
--
--
-- Roger Browne, 6 Bambers Walk, Wesham, PR4 3DG, UK | Ph 01772-687525
-- Everything Eiffel: http://www.eiffel.demon.co.uk/ | +44-1772-687525



Thu, 13 Jan 2000 03:00:00 GMT  
 Enumerations in Eiffel



Quote:
>Patrick Doyle writes:

>>   Oh.  I didn't know Eiffel had pointers.

>It has, but not in the C sense. They are used for interfacing with software
>written in C. You can pass them around within your Eiffel code, but can't do
>much else with them except compare them for equality.

  That's good.  I'm in the process of trying to convince my boss that we
should do the next version of our software in Eiffel.  If it's easy to
interface to C and/or C++, that should be an easier sell.

Quote:
>>   Hey, that's sneaky.  The subclass really doesn't obey the invariant
>> of the superclass, but it seems to.  That's creepy.  Should features
>> used in invariants generally be "final"? (is that the word?  I mean
>> they can't be overridden.)

>Don't worry, most people are uncomfortable with this when they first meet it.
>It's actually clean, pure and elegant. You just need to accept that what we
>are doing here is expressing the invariant in a very high-level way. Our code
>is depending on the same invariant that is expressed at the end of the class,
>so there's no correctness problem.

  Yes, I definitely see the uses for it.  For example, in the COMPARABLE
class, there are assertions regarding the relationships among the operators.
Clearly, it makes sense to override these.

  But if my code relies on the contents of a feature used in an assertion,
I should prevent those contents from being overridden.  Just something
to keep in mind.

Quote:
>> This means that enumerations could only be extended by creating a
>> superclass.  Some languages support this, but not Eiffel.

>Eiffel "supports" it by making you use INTEGER codes for enumerations, thereby
>leaving the "type-checking" to the application code, and making it
>straightforward to implement applications like the electronic component
>example above.

  Sure.  I just meant that you can't declare a new superclass for an
existing class in Eiffel.

Quote:
>Enumerated types are the antithesis of OOP if they are used to model something
>that would be better modelled by polymorphism. I don't believe they violate
>any OO principle when used to model a pure enumeration, e.g. the months of the
>year.

  Certainly.  That's why I want to use them now.  They are appropriate.
I'm rewriting a program I wrote a while back that simulates digital
circuits.  The signals are supposed to have different states (low, high,
unknown) and I think these are best modelled with an enumeration.

Quote:
>> ...This brings up another question.  What's the
>> difference between these:

>> feature
>>   goober1 : INTEGER
>>   goober2 : INTEGER is
>>     once
>>       !!Result
>>     end

>>   Is the second like a class variable (to borrow a Smalltalk term) rather
>> than an instance variable?

>There are too many side-issues here for me to give you a complete answer in
>this post, but here are some hints:

>  - INTEGER is an expanded type, so it doesn't make sense to create it ("!!")

  Oops.  Bad example.

Quote:
>  - 'goober1' can be assigned to, whereas 'goober2' is a read-only function
>     result

>  - "once-functions" can be used to implement shared variables, and also to
>    implement pseudo-constants of complex object types.

  Good.  That's how I used them in the enumeration pattern I discuss in
the other article.

  Thanks.

 -PD

--
--
Patrick Doyle



Fri, 14 Jan 2000 03:00:00 GMT  
 Enumerations in Eiffel

Quote:

> In my own software, I have always treated each enumeration as a separate
> type, even if I use inheritance to help build it. I've yet to see a
> compelling example where a subtype relationship is appropriate (which is not
> to say that such examples don't exist).

> How about sub-categories? For, example, PRIMARY_COLOUR (red, green?, blue)
> being a sub-category of COLOUR (red, green?, blue, black, yellow, etc.).

I'm hard-pushed to think of an application where that would be appropriate.
The COLOUR enumeration certainly can't include every possible colour, so why
should it necessarily include the primary colours?

But suppose that the COLOUR enumeration did include all the primary colours.
We might reasonably want to do the following:

  c: COLOUR
  pc: PRIMARY_COLOUR
  ...
  if c = red or c = green or c = blue then
     pc := c
  end

This isn't allowed if PRIMARY_COLOUR inherits from COLOUR (and assignment
attempt doesn't help us out here either!).

If COLOUR inherits from PRIMARY_COLOUR the above is typesafe, but an unguarded
"pc := c" would be likely to cause 'pc' to fail its invariant. Besides, if we
had multiple sub-categories then class COLOUR would have to inherit from all
of them, which would soon become unmanageable.

Quote:

> ... this is getting increasingly verbose and could benefit from some type
> of language support. Perhaps an enum-like keyword could automatically
> generate the predefined operations (iteration, membership) common to all
> enumerated types ...

If we can find a good pattern within the existing language facilities, it will
be easy to write a small utility to generate the necessary classes from a list
of enumeration values.

Regards,
Roger
--
--
-- Roger Browne, 6 Bambers Walk, Wesham, PR4 3DG, UK | Ph 01772-687525
-- Everything Eiffel: http://www.eiffel.demon.co.uk/ | +44-1772-687525



Sun, 16 Jan 2000 03:00:00 GMT  
 Enumerations in Eiffel

Quote:

:Patrick Doyle writes:

:> I guess the extended enumeration is not a subtype *or* a supertype
:> of the original.  I suppose it makes sense to have to create an
:> entirely new type.
:
:In my own software, I have always treated each enumeration as a separate type,
:even if I use inheritance to help build it. I've yet to see a compelling
:example where a subtype relationship is appropriate (which is not to say that
:such examples don't exist).

How about sub-categories? For, example, PRIMARY_COLOUR (red, green?, blue)
being a sub-category of COLOUR (red, green?, blue, black, yellow, etc.).

Don.
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-



Sun, 16 Jan 2000 03:00:00 GMT  
 Enumerations in Eiffel

Quote:


> :Patrick Doyle writes:
> :> I guess the extended enumeration is not a subtype *or* a supertype
> :> of the original.  I suppose it makes sense to have to create an
> :> entirely new type.
> :
> :In my own software, I have always treated each enumeration as a separate type,
> :even if I use inheritance to help build it. I've yet to see a compelling
> :example where a subtype relationship is appropriate (which is not to say that
> :such examples don't exist).

> How about sub-categories? For, example, PRIMARY_COLOUR (red, green?, blue)
> being a sub-category of COLOUR (red, green?, blue, black, yellow, etc.).

You could always have a feature is_primary and use assertions. But that
is removing the advantage of static checking.

Looking through the code here we have very few enumerations, and those
that are like enumerations all have other features, hence them being
classes. Even just outputing the name of the enumeration as a string
has to be near universal.

--

Nick



Sun, 16 Jan 2000 03:00:00 GMT  
 Enumerations in Eiffel



Quote:

> :Patrick Doyle writes:
> :> I guess the extended enumeration is not a subtype *or* a supertype
> :> of the original.  I suppose it makes sense to have to create an
> :> entirely new type.
> :
> :In my own software, I have always treated each enumeration as a separate
type,
> :even if I use inheritance to help build it. I've yet to see a compelling
> :example where a subtype relationship is appropriate (which is not to say
that
> :such examples don't exist).

> How about sub-categories? For, example, PRIMARY_COLOUR (red, green?,
blue)
> being a sub-category of COLOUR (red, green?, blue, black, yellow, etc.).

Good example.  Building on PD's pattern in the other thread, and looking at
it from a substitutability standpoint, it looks as though PRIMARY_COLOUR
should be a supertype. But there again, it is impossible to miss the fact
that a PRIMARY_COLOUR is a specialisation of COLOUR.  Maybe the problem is
related to the old covariance vs contravariance argument: in practice
covariance is much more useful - so for Eiffel - which embraces covariance
I would say we should take that approach.

Another (completely different) approach would be to make use of a
parameterised container class SET[COLOUR] to define sub-categories.
Perhaps even making use of that design pattern (is it Composite) that makes
a container of items look like an individual item.  e.g.

class PRIMARY_COLOUR
inherit
    SET[COLOUR],
    COLOUR

However, restating something I said on the other thread, this is getting
increasingly verbose and could benefit from some type of language support.
Perhaps an enum-like keyword could automatically generate the predefined
operations (iteration, membership) common to all enumerated types (class
COLOUR enumerates COLOUR_CODES??) or perhaps it could all be done with
generic classes (class ENUMERATION[COLOUR, COLOUR_CODES]??).

These are all half-baked ideas. Does anyone else have any?

--
Gavin Collings



Sun, 16 Jan 2000 03:00:00 GMT  
 Enumerations in Eiffel



Quote:


> > How about sub-categories? For, example, PRIMARY_COLOUR (red, green?,
blue)
> > being a sub-category of COLOUR (red, green?, blue, black, yellow,
etc.).

> I'm hard-pushed to think of an application where that would be
appropriate.
> The COLOUR enumeration certainly can't include every possible colour, so
why
> should it necessarily include the primary colours?

True enough, but its just an example to help direct the discussion.

Quote:

> But suppose that the COLOUR enumeration did include all the primary
colours.
> We might reasonably want to do the following:

>   c: COLOUR
>   pc: PRIMARY_COLOUR
>   ...
>   if c = red or c = green or c = blue then
>      pc := c
>   end

> This isn't allowed if PRIMARY_COLOUR inherits from COLOUR (and assignment
> attempt doesn't help us out here either!).

Reverse assignment attempt could help us out here if we took the analogy of
subclass a step further - i.e. PRIMARY_COLOUR overrides red, green and
blue.  I am increasingly tending towards a view that subclass = subrange
could be a fruitful approach.

Quote:

> If COLOUR inherits from PRIMARY_COLOUR the above is typesafe, but an
unguarded
> "pc := c" would be likely to cause 'pc' to fail its invariant. Besides,
if we
> had multiple sub-categories then class COLOUR would have to inherit from
all
> of them, which would soon become unmanageable.

Agreed.

Quote:
> enumerated types ...

> If we can find a good pattern within the existing language facilities, it
will
> be easy to write a small utility to generate the necessary classes from a
list
> of enumeration values.

I think you're right here. For now its best to concentrate on exactly what
is a "good pattern".

Regards,

Gavin.

--
Gavin Collings



Mon, 17 Jan 2000 03:00:00 GMT  
 Enumerations in Eiffel

:> In my own software, I have always treated each enumeration as a separate
:> type, even if I use inheritance to help build it. I've yet to see a
:> compelling example where a subtype relationship is appropriate (which is not
:> to say that such examples don't exist).

Actually, this is a useful technique for applying contracts in Ada. By subtyping
enumerations, you can strengthen preconditions for specialised operations.

I guess this comes back to the question of whether strengthening preconditions
ought to be allowable or not. Like some others, I believe it should. IMO, it
falls into the same category as covariance and feature hiding and, as such,
should be covered under the system-validity rules. Accordingly, the rules
might exclude polymorphic CACTUS calls..

  CACTUS = Changed Attribute, Contract or Type Upsets System.  :)

If a CACTUS call has a polymorphic target, your program may be cactus. :)

If a precondition is strengthened, the call becomes a CACTUS call. While this
is a little tongue-in-cheek, there is a serious element behind it which I
haven't thought through yet.

:> How about sub-categories? For, example, PRIMARY_COLOUR (red, green?, blue)
:> being a sub-category of COLOUR (red, green?, blue, black, yellow, etc.).
:
:I'm hard-pushed to think of an application where that would be appropriate.
:The COLOUR enumeration certainly can't include every possible colour, so why
:should it necessarily include the primary colours?

Perhaps modelling some device that combines light of different colours to produce
other colours. A specialised version uses only primary colours as input.

:But suppose that the COLOUR enumeration did include all the primary colours.
:We might reasonably want to do the following:
:
:  c: COLOUR
:  pc: PRIMARY_COLOUR
:  ...
:  if c = red or c = green or c = blue then
:     pc := c
:  end
:
:This isn't allowed if PRIMARY_COLOUR inherits from COLOUR (and assignment
:attempt doesn't help us out here either!).

Because they're expanded?

:If COLOUR inherits from PRIMARY_COLOUR the above is typesafe, but an unguarded
:"pc := c" would be likely to cause 'pc' to fail its invariant. Besides, if we
:had multiple sub-categories then class COLOUR would have to inherit from all
:of them, which would soon become unmanageable.

From a modelling perspective, I would prefer not to have COLOUR inheriting
from PRIMARY_COLOUR because its not a specialisation of it.

:You could always have a feature is_primary and use assertions. But that
:is removing the advantage of static checking.

Agree.

Don.
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-



Mon, 17 Jan 2000 03:00:00 GMT  
 Enumerations in Eiffel

Quote:

> ... suppose that the COLOUR enumeration did include all the primary colours.
> We might reasonably want to do the following:

>   c: COLOUR
>   pc: PRIMARY_COLOUR
>   ...
>   if c = red or c = green or c = blue then
>      pc := c
>   end

> This isn't allowed if PRIMARY_COLOUR inherits from COLOUR (and assignment
> attempt doesn't help us out here either!).

> Because they're expanded?

No. Assume that COLOUR and PRIMARY_COLOUR are not expanded. If 'c' references
a "red" enumeration (of type COLOUR rather than PRIMARY_COLOUR) the assignment
attempt instruction...

   pc ?= c

..will always fail (i.e. a void reference will be assigned to 'pc') because
the runtime type of 'c' (COLOUR) does not conform to the declared type of 'pc'
(PRIMARY_COLOUR).

Regards,
Roger
--
--
-- Roger Browne, 6 Bambers Walk, Wesham, PR4 3DG, UK | Ph 01772-687525
-- Everything Eiffel: http://www.eiffel.demon.co.uk/ | +44-1772-687525



Mon, 17 Jan 2000 03:00:00 GMT  
 Enumerations in Eiffel

:> ... suppose that the COLOUR enumeration did include all the primary colours.
:> We might reasonably want to do the following:
:>
:>   c: COLOUR
:>   pc: PRIMARY_COLOUR
:>   ...
:>   if c = red or c = green or c = blue then
:>      pc := c
:>   end
:>
:> This isn't allowed if PRIMARY_COLOUR inherits from COLOUR (and assignment
:> attempt doesn't help us out here either!).
:
:> Because they're expanded?
:
:No. Assume that COLOUR and PRIMARY_COLOUR are not expanded. If 'c' references
:a "red" enumeration (of type COLOUR rather than PRIMARY_COLOUR) the assignment
:attempt instruction...
:
:   pc ?= c
:
:...will always fail (i.e. a void reference will be assigned to 'pc') because
:the runtime type of 'c' (COLOUR) does not conform to the declared type of 'pc'
:(PRIMARY_COLOUR).

Ah, yes, *that* problem! I remember trying to solve it without success. :(

Don.
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-



Tue, 18 Jan 2000 03:00:00 GMT  
 Enumerations in Eiffel


Quote:

> :> ... suppose that the COLOUR enumeration did include all the primary colours.
> :> We might reasonably want to do the following:
> :>
> :>   c: COLOUR
> :>   pc: PRIMARY_COLOUR
> :>   ...
> :>   if c = red or c = green or c = blue then
> :>      pc := c
> :>   end
> :>
> :> This isn't allowed if PRIMARY_COLOUR inherits from COLOUR (and assignment
> :> attempt doesn't help us out here either!).

[...]

Quote:
> :No. Assume that COLOUR and PRIMARY_COLOUR are not expanded. If 'c' references
> :a "red" enumeration (of type COLOUR rather than PRIMARY_COLOUR) the assignment
> :attempt instruction...
> :
> :   pc ?= c
> :
> :...will always fail (i.e. a void reference will be assigned to 'pc') because
> :the runtime type of 'c' (COLOUR) does not conform to the declared type of 'pc'
> :(PRIMARY_COLOUR).

> Ah, yes, *that* problem! I remember trying to solve it without success. :(

Isn't it possible to use a scheme where PRIMARY_COLOUR overrides the values of red,
green and blue?  Taking a quick look at your pattern, this would seem to involve
changing the once procedures inside COLOUR_ENUM to create PRIMARY_COLOURs instead of
COLOURs.  e.g.

    green : COLOUR is
        once
            !PRIMARY_COLOUR!Result
        end

Now, I believe, the ?= will work with the expected semantics.

--
Gavin Collings



Tue, 18 Jan 2000 03:00:00 GMT  
 
 [ 18 post ]  Go to page: [1] [2]

 Relevant Pages 

1. Eiffel enumerations

2. Follow up on enumerations

3. Reading ActiveX Control Enumerations

4. Fanaticism [was Re: Enumerations and Arrays Unnecessary!???]

5. Size of enumerations/subranges in Modula-2

6. Size of enumerations/subranges in Modula-2

7. Enumerations

8. enumerations

9. Enumerations - slow down a minute!

10. Pattern for enumerations? (Attn: Roger Browne)

11. Enumerations in Oberon (was: Re: Enumerated types)

12. enumerations

 

 
Powered by phpBB® Forum Software