Quote:
> How do I specify, or enforce, constraints on the slot fillers of an instance?
I had always intended people to use conversion specs. However, it turns
out that there is a small buggette in ObjectClass that makes this solution
work poorly. So here's a selection of answers. Hope they help. I am
afraid none of them are very elegant because of the extremely limited
type language supported by defclass and hence ObjectClass.
(1) Use Wrappers.
Wrappers allow you to modify the behaviour of a slot. The syntax is
rather more ikky than I like. (I am currently working on a new kind
of wrapper that has much better syntax.) However, I would first create
the Ape mixin with a shared-slot for the top & bottom of the range.
Presumably the range is the same for all instances so a shared slot is
best.
Note that the height slot must be given a suitable active default! It
must be able to pick up a default that is in the correct range. Otherwise
you've got problems. Here, I ensure that it gets a sensible range by
pulling in the low end of the range.
define :mixin Ape;
shared_slot range = conspair( 0.0, 100.0 );
slot height = myself.range.front;
enddefine;
Now modify the behaviour of height on update. Note that -p- is the
procedure that will do the actual updating.
define :wrapper updaterof height( h, a:Ape, p );
lvars ( lo, hi ) = a.range.destpair;
if h < lo then
mishap( 'TOO SMALL', [ ^h ] )
elseif h > hi then
mishap( 'TOO BIG', [ ^h ] )
endif;
p( h, a )
enddefine;
Now we can instantiate the 3 classes overriding the shared slot.
define :class Dwarf;
is Ape;
shared_slot range = conspair( 0.75, 1.25 );
enddefine;
define :class Geek;
is Ape;
shared_slot range = conspair( 1.4, 1.8 );
enddefine;
define :class Giant;
is Ape;
shared_slot range = conspair( 2.0, 5.0 );
enddefine;
(2) Define A Method.
Instead of using a slot, we can implement height less directly as a
method. This simplifies things a little.
define :mixin Ape;
shared_slot range = conspair( 0.0, 100.0 );
slot lvars *_height_* = myself.range.front;
enddefine;
define :method height( a:Ape );
*_height_*( a )
enddefine;
define :method updaterof height( h, a:Ape );
lvars ( lo, hi ) = a.range.destpair;
if h < lo then
mishap( 'TOO SMALL', [ ^h ] )
elseif h > hi then
mishap( 'TOO BIG', [ ^h ] )
endif;
h -> *_height_*( a )
enddefine;
From here we can proceed as before. The niggle here is that we
might find a slot called "*_height_*" to be rather ugly. (When we
print it out, it will have this strange slot name in it.)
(3) Use Conversion Specs
Note this way doesn't work yet (but it should do).
This is the way I had intended constraints to be applied. You would
need to prepare the ground by writing a procedure that constructed
conversion specs and defaults for you. Here's the relevant thing.
define make_range( lo, hi ) -> ( lo, range );
lvars lo, hi;
lconstant range;
define lconstant range( x ); lvars x;
x
enddefine;
define updaterof range( x ); lvars x;
if lo > x then
mishap( 'TOO SMALL', [ ^x ] )
elseif x > hi then
mishap( 'TOO BIG', [ ^x ] )
else
x
endif
enddefine;
enddefine;
Obviously this is a piece of garbage. However, until typespecs get
properly sorted out, this is the kind of thing you need to write. Then
you have to introduce a bunch of identifiers because Pop11 does not
support type expressions yet (sigh!). Here, I've made them -constant-
for the sake of simplicity.
constant
( av_dwarf, dwarfish ) = make_range( 0.75, 1.25 ),
( av_geek, geekish ) = make_range( 1.4, 1.8 ),
( av_giant, giantish ) = make_range( 2.0, 5.0 )
;
Now we are in a position to elegantly define the classes.
define :mixin Ape;
slot height : float == 1.78; ;;; Average height in metres.
enddefine;
define :class Dwarf;
is Ape;
slot height : float # dwarfish == av_dwarf;
enddefine;
define :class Geek;
is Ape;
slot height : float # geekish == av_geek;
enddefine;
define :class Giant;
is Ape;
slot height : float # giantish == av_giant;
enddefine;
Sadly, this doesn't work because I messed up. Between lines 128 and 129
of "$usepop/pop/lib/proto/objectclass/src/objectclass_form.p" the
following needs to be inserted.
s.xslot_field_spec -> x.xslot_field_spec;
Without this, the stupid parser fails to "see" the field-spec of height
because the class Ape already supplies a field-spec. Dumb. I am passing
this one onto ISL (after I've tested it properly!)
Steve