About KCP and automatic initialize

ducasse ducasse at iam.unibe.ch
Fri Sep 12 06:11:47 UTC 2003


Hi richard

I know well your problem. We had been discussing it for over more than 
two years. Even before nathanael
started to work on traits. The problem is that automatic initialize is 
not only about class invariant, it is about providing a good way of 
initializing the default value of objects. Nathanael is working on 
another model but as squeak evolves really slowly (the proof is in this 
thread) this will never be for Squeak ;) because first we have to be 
sure that this is really cool and second that it does not impose too 
much change (no iv access, automatic generation of accessors....).

Automatic initialization does not solve all the problems 
(unfortunately). Now when we program in Smalltalk we are programming as 
in Eiffel we do not have support for class invariant. When I code a 
breakOut I do not know what is
my class invariant and too lazy to think about it but I need a way to 
provide default value of the instance variable and
Note that this is not because the language does not offer that that we 
should not do it. I read a PhD where the guy shows that just having 
simple method precondition was a huge factor in quality. After reading 
it I'm convinced that an extremely cheap way to ensure quality and 
considering to see how to had that simply to my own code.

However automatic initialize is one simple way to provide a common 
framework for initializing object and it has the key advantages that 
novices do not have to:
	- duplicate code
	- look up in the superclass to know whether the same code is there 
(strange in system that promote reuse)
	- understand metaclasses
	- mess up everything (forgetting ^)
	during the ***FIRST*** lecture
	- then when a student is clever he will ask why this is not done in 
Object and he is RIGHT

Stef


On Vendredi, sep 12, 2003, at 01:39 Europe/Zurich, Richard A. O'Keefe 
wrote:

> ducasse <ducasse at iam.unibe.ch> continues the super new initialize 
> thread.
> By the way, I searched the Squeak mail archive, and I see that this was
> being discussed back in 1999 (and by some of the same people...).
>
> 	But still I'm puzzled. Why would you need that to avoid one call to
> 	initialize that is empty????
> 	Really one call in Squeak. Eliot says that in VW the costs would be
> 	completely invisible because of the
> 	iniline cache, the benchmark that noury sent prove that without any
> 	tricks we can really good
> 	result.
> 	
> It really has nothing to do with efficiency.
> It's about good design.
> Making the system call #initialize automatically is a way of
> encouraging people to *design* for #initialize, and the more
> Smalltalk code I look at, the more that seems to me to lead
> to bad design.
>
> If I were making such radical changes to Squeak,
> I would probably ban zero-argument #initialize entirely.
>
> Let's consider just one example, from a tutorial on the Swiki.
> I'm experimenting with a style for displaying classes which is
> a bit more readable than change-sets.
>
>   Object subclass: #TaxRule
>     instanceVariableNames: 'breakPoints rates'
>     comment:
>       'I represent a tax table.
>        Given a yearly taxable income, I can give the tax rate for it.
>        I store the tax table as a sequence of amounts,
>        and the tax rate for that amount.
>        Example: in 1994 the tax rate for a single person (Schedule X) 
> was
> 	$     0 15%
> 	$ 22750 28%
> 	$ 55100 31%
> 	$115000 36%
> 	$250000 39.6%
>       '
>     category: 'initialize-release' methods:
>       initialize
>         breakPoints := SortedSequence new.
>         rates := OrderedCollection new.
>
>     category: 'accessing' methods:
>       breakPoint: aNumber rate: anotherNumber
>         "Add an entry to the tax table"
>         |index|
>         index := breakPoints indexOfStartOfIntervalContaining: aNumber.
>         breakPoints add: aNumber.
>         rates add: anotherNumber beforeIndex: index + 1.
>       rateFor: aNumber
>         "Return the tax rate for someone whose taxable income is
>          aNumber dollars."
> 	^rates at: (breakPoints indexOfStartofIntervalContaining: aNumber)
>
>     class category: 'instance creation' methods:
>       new
>         ^super new initialize
>
>
> Now, this is just such a class as would be simplified by the proposed
> change to Object.  If Object new sent #initialize, there would be no 
> need
> at all for #new in this class.
>
> But there's something rather upsetting about this class.
> The object returned by #new IS NOT IN FACT USABLE, despite having
> been "initialized".  Let's suppose we want to make a scheduleX object.
> Here's what we have to do:
>
>     scheduleX := TaxRule new.	"Not usable at all".
>     scheduleX
>       breakPoint:      0 rate: 0.15;  "usable but wrong"
>       breakPoint:  22750 rate: 0.28;  "usable but wrong"
>       breakPoint:  55100 rate: 0.31;  "usable but wrong"
>       breakPoint: 115000 rate: 0.36;  "usable but wrong"
>       breakPoint: 250000 rate: 0.396. "NOW it's initialized".
>
> What's a better approach for this class?
> Well, these tables aren't *supposed* to change.  Once you've created
> one, it had better not be tampered with.  The only reason for having
> #breakPoint:rate: is so that the thing can *really* be initialised
> (as opposed to what #initialize does).  It would be nice to design it
> so that we can provide the table declaratively:
>
>     scheduleX := TaxRule forTable:
>       #((     0 0.15)
>         ( 22750 0.28)
>         ( 55100 0.31)
>         (115000 0.36)
>         (250000 0.396)).
>
> So let's do that.
>
>   Object subclass: #TaxRule
>     instanceVariableNames: 'breakPoints rates'
>     comment:
>       'I represent a tax table.
>        Given a yearly taxable income, I can give the tax rate for it.
>        I store the tax table as a sequence of amounts,
>        and the tax rate for that amount.
>        Example: in 1994 the tax rate for a single person (Schedule X) 
> was
> 	$     0 15%
> 	$ 22750 28%
> 	$ 55100 31%
> 	$115000 36%
> 	$250000 39.6%
>       '
>     category: 'private' methods:
>       initializeFromTable: anArrayOfPairs
> 	|sortedPairs|
> 	"Check that the table is well formed, has lowest break 0,
> 	 and non-decreasing rates (after sorting)."
>
> 	(anArrayOfPairs allSatisfy: [:each |
> 	  each size == 2 and: [
> 	  each first >= 0 and: [
> 	  each second between: 0 and 1]]])
> 	    ifFalse: [self error: 'ill-formed tax table'].
> 	sortedPairs :=
> 	  anArrayOfPairs asSortedCollection: [:x :y | x first <= y first].
> 	(sortedPairs first = 0)
> 	  ifFalse: [self error: 'lowest break-point is not zero'].
> 	breakPoints :=
> 	  (sortedPairs collect: [:each | each first]) asSortedCollection.
> 	rates :=
> 	  (sortedPairs collect: [:each | each second]) asArray.
> 	2 to: rates size do: [:i |
> 	  ((rates at: i-1) > (rates at: i))
> 	    ifTrue: [self error: 'rates not in order']].
> 	^self
>
>     category: 'accessing' methods:
>       rateFor: aNumber
>         "Return the tax rate for someone whose taxable income is
>          aNumber dollars."
> 	^rates at: (breakPoints indexOfStartofIntervalContaining: aNumber)
>
>     class category: 'instance creation' methods:
>       new
>         self shouldNotImplement.
>       forTable: anArrayOfPairs
>         ^super new initializeFromTable: anArrayOfPairs
>
> Now there is no possibility of working with a TaxRule that has been
> #initialized but not initialized (a class invariant is that the 
> breakpoint
> table is non-empty and starts with 0, which #initialize did not 
> ensure),
> or partly (and thus wrongly) initialized.
>
> There are far too many classes in Squeak which *have* a #new method
> which can create an unusable object.  It seems most unwise to do 
> anything
> to encourage this.
>
> (By the way, this particular example just happened to be the first bit
> of someone else's code that I happened to look at after this topic came
> up.  It is far too easy to find examples like this.)
>



More information about the Squeak-dev mailing list