[squeak-dev] sane defaults and doesNotUnderstand: (was Future blah blah blah)

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
5 messages Options
Reply | Threaded
Open this post in threaded view
|

[squeak-dev] sane defaults and doesNotUnderstand: (was Future blah blah blah)

David Goehrig
On Tue, Jun 30, 2009 at 12:22 AM, Michael van der Gulik <[hidden email]> wrote:


Changing the implementation of doesNotUnderstand: is a code smell. You're starting to play with voodoo and you need a *very* good reason to do it. Otherwise you'll have made yourself a very difficult application to debug.

In the spirit of communication, I'll explain why I consider the current defaultHandler behavior of doesNotUnderstand:aMessage to be "broken".  My Very Good Reason (TM) is based upon the principles of expectation.

In the abstract, semantically if I say something like:

    anArbitraryObject someMethod ifTrue: aBlock.

I am implicitly stating that I expect anArbitraryObject will return a boolean when passed the message #someMethod.  This expectation has nothing to do with the implementation of anArbitraryObject, or with the meaning of #someMethod, only in the semantics of how it fits within the overall structure of the statement.

Similarly if I were to say something like:

    ( anArbitraryObject + 1 ) < someLimit  ifFalse: anotherBlock

I am stating that I expect anArbitraryObject to return some sort of numeric value, and that #+ will perform some sort of addition.  In this case the result of #+ should also be something that can be compared using an ordering #< message.

If any of these objects were to fall through to the defaultHandler, instead of getting what I expected, I'll get a MessageNotUnderstood dialog, which while incredibly helpful in debugging, doesn't actually "Do What I Mean".  From a pure semantics point of view, I'd rather the system honor my intent more so than confront me with implementation details.  MessageNotUnderstood is an implementation detail, and does not correspond to the semantics of the statement.  It is a literal  non sequitur.  This is doubly true in the case of methods which merely test for a capability.  If the object doesn't understand, then it clearly doesn't have the stated capability! Res ipso loquitur.

So from my point of view, the current system behavior, while useful for debugging, doesn't match the programmer's explicit semantics.  Additionally there is an entire class of application, ones that consist of lots of distributed and unique objects, in which the existing class based objects become a de-optimization. These apps are typically only implementable by rolling your own objects by hand, and attaching a custom doesNotUnderstand:aMessage handler to each.  But that's a post for another time.


--
-=-=-=-=-=-=-=-=-=-=- http://blog.dloh.org/


Reply | Threaded
Open this post in threaded view
|

[squeak-dev] Re: sane defaults and doesNotUnderstand: (was Future blah blah blah)

Klaus D. Witzel
On Tue, 30 Jun 2009 12:37:55 +0200, David Goehrig wrote:

...

> MessageNotUnderstood
> is an implementation detail, and does not correspond to the semantics of  
> the
> statement.  It is a literal  non sequitur.  This is doubly true in the  
> case
> of methods which merely test for a capability.  If the object doesn't
> understand, then it clearly doesn't have the stated capability! Res ipso
> loquitur.
>
> So from my point of view, the current system behavior, while useful for
> debugging, doesn't match the programmer's explicit semantics.

You just want this? result = (anObject.methodName ? methodName() : false)

?

/Klaus

> Additionally
> there is an entire class of application, ones that consist of lots of
> distributed and unique objects, in which the existing class based objects
> become a de-optimization. These apps are typically only implementable by
> rolling your own objects by hand, and attaching a custom
> doesNotUnderstand:aMessage handler to each.  But that's a post for  
> another
> time.
>

--
"If at first, the idea is not absurd, then there is no hope for it".
Albert Einstein


Reply | Threaded
Open this post in threaded view
|

Re: [squeak-dev] sane defaults and doesNotUnderstand: (was Future blah blah blah)

Joshua Gargus-2
In reply to this post by David Goehrig
David Goehrig wrote:
On Tue, Jun 30, 2009 at 12:22 AM, Michael van der Gulik <[hidden email]> wrote:


I am implicitly stating that I expect anArbitraryObject will return a boolean when passed the message #someMethod.  This expectation has nothing to do with the implementation of anArbitraryObject, or with the meaning of #someMethod, only in the semantics of how it fits within the overall structure of the statement.

Similarly if I were to say something like:

    ( anArbitraryObject + 1 ) < someLimit  ifFalse: anotherBlock

Taking < as an example, what do you say about these two fragments of semantically equivalent code?

anArbitraryObject < someLimit ifFalse: anotherBlock

anArbitraryObject >= someLimit ifTrue: anotherBlock


In the first case, anotherBlock is evaluated, and in the second it is not.  Making #doesNotUnderstand: return false cannot possibly "do what you expect" because it will behave differently depending on how you phrase your expectation.  Check, and mate?  :-)


Cheers,
Josh



Reply | Threaded
Open this post in threaded view
|

Re: [squeak-dev] Re: sane defaults and doesNotUnderstand: (was Future blah blah blah)

David Goehrig
In reply to this post by Klaus D. Witzel


On Tue, Jun 30, 2009 at 11:28 AM, Klaus D. Witzel <[hidden email]> wrote:

You just want this? result = (anObject.methodName ? methodName() : false)

?

Not quite.. if you were attempting to write it in javascript it would look more like:

   result = ( typeof(anObject[methodName]) == "function" ) ? anObject[methodName]() : 
        currentContext == "boolean" ?  false : 
        currentContext == "number" ? 0 :
        currentContext == "string" ? '' :
        currentContext == "array" ? [] :
        currentContext == "object" ? {} :
        undefined;

where currentContext would be an internal variable denoting the desired type / class / or traits
of the result.  The idea is that the defaultHandler of doesNotUnderstand would produce a reasonable
approximation of a result based on the semantics and context in which the call appears.

Here's the specific use case that I've been fighting with... I have a virtual world in which objects can build other objects, and most objects are unique.  Any object in the world can send any other object in the world any number of messages to interact with it.  The limitations are defined in an external dictionary of valid semantic actions, but no object is likely to implement any more than 3-8 methods.

Since there is no programmer intervention in object creation, and the messages passed need not even be sensical every action requires a sane default.  The way I deal with this is subclass all of these objects from an object that has its own custom doesNotUnderstand:aMessage.

Now the problems start to arise from users.  People are more clever than objects that randomly evolve due to sexual reproduction. (surprising I know :) and often have expectations of what the words they are typing mean, independent of the implementation details under the hood.  In order to keep in line with most of these expectations, I've hacked my doesNotUnderstand:aMessage to return false by default.  I've been experimenting with examining the context of the call as well to return sane defaults.  My current hack is to force Boolean to respond to all of the messages various strings, and numbers, also do, and return equivalent values.

At the heart of this problem is that most professional Smalltalk programmers understand the meaning of words, the semantics of messages, in terms of their implementation semantics.  To the end user, however, their mental model is based upon the semantics of both the real world, and the virtual world that they think they understand.  Etoys made the system accessible by focusing on the user's semantics over the semantics of implementation.  Well written code that is a pleasure to read typically merges the implementation and expressive aspects of the semantics into one coherent whole.



--
-=-=-=-=-=-=-=-=-=-=- http://blog.dloh.org/


Reply | Threaded
Open this post in threaded view
|

[squeak-dev] Re: sane defaults and doesNotUnderstand: (was Future blah blah blah)

Klaus D. Witzel
On Tue, 30 Jun 2009 18:24:21 +0200, David Goehrig wrote:

> On Tue, Jun 30, 2009 at 11:28 AM, Klaus D. Witzel wrote:
>>
>> You just want this? result = (anObject.methodName ? methodName() :  
>> false)
>>
>> ?
>
>
> Not quite.. if you were attempting to write it in javascript it would  
> look more like:
>
... understood ...
>
> where currentContext would be an internal variable denoting the desired  
> type / class / or traits of the result.

Can you put a method #defaultValue to the class side of String, Number,  
etc, similiar to #defaultElement that is already in the collection  
hierarchy. Then, assuming you have the below in control of  
#doesNotUnderstand:, messages like

        x zork

would return (thisContext client class defaultValue). But your  
currentContext, that's not easy to find out, even if you know what  
thisContext client is.

>  The idea is that the defaultHandler of doesNotUnderstand would
> produce a reasonable
> approximation of a result based on the semantics and context in which
> the call appears.
>
> Here's the specific use case that I've been fighting with
...

You can make your own defaultHandler of #doesNotUnderstand:, like this,  
during your computation:

  [myObject myComputation]
   on: MessageNotUnderstood
   do: [:ex | ex return: ex receiver class defaultValue]

So ('' zork) will return ('' class defaultValue),
and (123 zork) will return (123 class defaultValue), etc.

?

/Klaus