<div dir="auto">IMO, it is usually fine to use the same selector with different meanings in different classes, its not always bad (IMO) and it makes code nicer to read and selectors easier to guess (for a new user). But when a subclass down the hierarchy redefines a message with a totally different meaning it is wrong (IMO). So the selectors we put in Object are kind of global, we cannot change the meaning of #printOn: or #= or #hash in one of our classes or things will break. That’s why it seems to me that its better to keep Object as small as possible and perhaps remove Object>>#value. I think we already talked about this tho, and we didnt remove it, was it because dependencies or events rely on it?</div><div><br><div class="gmail_quote"><div dir="ltr" class="gmail_attr">On Fri, Jun 14, 2024 at 19:24 Jaromir Matas via Cuis-dev <<a href="mailto:cuis-dev@lists.cuis.st">cuis-dev@lists.cuis.st</a>> wrote:<br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-style:solid;padding-left:1ex;border-left-color:rgb(204,204,204)">Hi,<br>
My understanding is that this:<br>
<br>
>> [Boris] The meaning of BlockClosure>>value and Association>>value <br>
have nothing in common!<br>
<br>
is the source of the confusion here; many definitions of #value are not <br>
"redefinitions" of Object>>value, they are often just simple accessors <br>
to instance variables called #value.<br>
<br>
Do the accessors "spoil the fun"? It's the kind of ("fake") polymorphism <br>
not available in most languages. Is it good or bad?<br>
<br>
And back to the original Mark's question (roughly) - why would it be bad <br>
to modify the compiler so that<br>
<br>
`cond ifTrue: a ifFalse: b`<br>
<br>
is treated the same way as<br>
<br>
`cond ifTrue: [a] ifFalse: [b]`<br>
<br>
i.e. optimized using bytecode jumps rather than sending #ifTrue:ifFalse: <br>
?<br>
<br>
The divergence of b problem would disappear and we'd just have to be <br>
aware of the different semantics (#value send when used without [ ])...<br>
<br>
At any rate the current behavior of #ifTrue:ifFalse: feels inconsistent <br>
- depending on the "type" of arguments (or the syntactic form).<br>
<br>
A side-effect of "legitimizing" this approach would be one could start <br>
using (or "abusing"?) it like:<br>
<br>
a := Association key: 'one' value: 1.<br>
b := Association key: 'two' value: 2.<br>
x := cond ifTrue: a ifFalse: b<br>
<br>
and expecting x = 1 or x = 2. Too bad?<br>
<br>
Or one could assign blocks to a and b:<br>
<br>
a := [1].<br>
b := [2]<br>
x := cond ifTrue: a ifFalse: b<br>
<br>
Is it bad to expect x = 1 or x = 2 ?<br>
<br>
I haven't noticed this inconsistency before Mark's question and I'm <br>
confused now - apologies in case the above doesn't make much sense :)<br>
<br>
Best,<br>
Jaromir<br>
<br>
<br>
On 14-Jun-24 11:16:28 AM, "Boris Shingarov via Cuis-dev" <br>
<<a href="mailto:cuis-dev@lists.cuis.st" target="_blank">cuis-dev@lists.cuis.st</a>> wrote:<br>
<br>
>> It is *expected* of blocks to understand *#value*. There is no concern; if<br>
>> you redefine *BlockClosure >> value*, the image will crash.<br>
><br>
>Not sure what you mean here. The very first thing we do in PreSmalltalks<br>
>before we begin to build MachineArithmetic, libGDBs etc *is* we<br>
>redefine BlockClosure>>value (to auto-curry when invoked with the wrong<br>
>number of arguments). I am guessing what you mean is "redefine in a way<br>
>*incompatible* with the live system", but that's a characteristic of<br>
>how liveness is implemented in one particular Smalltalk, not of #value.<br>
><br>
>> Right, it’s not a good idea to implement value in Object like this, it<br>
>> doesnt really generalize anything, I suggest we remove it.<br>
><br>
>This, on the other hand, *will* break all hell, because now you are<br>
>doing computation in a category without products; and one immediate<br>
>consequence of that, is that Lawvere's elements-as-morphisms no longer<br>
>work.<br>
><br>
>In more detail:<br>
><br>
>A (Smalltalk) Object (which we might want to rename to Point or at least<br>
>SetElement if we were really radical) is a morphism from the product of<br>
>zero multiplicands to that (categorical) object. In other words, it is<br>
>an entity responding to 0-arg #value. So if I were on a radical course<br>
>to clean-up Object's protocol, #value and #yourself would be the two<br>
>messages I would keep.<br>
><br>
>> x := cond ifTrue: a ifFalse: b.<br>
>><br>
>> you might be in for a surprise<br>
><br>
>Yes, exactly that.<br>
>So the problem with the above code is not that it's "twice slower" but<br>
>that it's simply not equivalent to the other one, due to order of<br>
>evaluation. On the one hand, in Smalltalk-80 there is no easy way to<br>
>tell whether a block/method is a function or a partial function or an<br>
>action; on the other hand, Smalltalk is applicative-order: so what if<br>
>say b diverges? Then<br>
><br>
> 1 < 2 ifTrue: [a] ifFalse: [b]<br>
><br>
>evaluates to ≅a, but<br>
><br>
> 1 < 2 ifTrue: a ifFalse: b<br>
><br>
>diverges. A similar trouble happens when b has a side effect.<br>
><br>
>> a surprise if you happen to pass in a or b that implements value<br>
><br>
>Hmm, yeah, that's an interesting one, let's see:<br>
><br>
> x >= 0 ifTrue: a ifFalse: b (1)<br>
><br>
>and let's assume x∈ℤ so x>=0 can't be anything other than a True<br>
>or a False (in unmodified Smalltalk-80 such as vanilla Cuis we don't<br>
>have unsaturated terms, so this works). So this endows ℤ with a<br>
>simple algebra of types:<br>
><br>
> ℤ = ( (ℤ | [ :x | x>=0 ]) + (ℤ | [ :x | x<0 ) ) (2)<br>
><br>
>so what we are doing in (1) is we are raising to the (2) degree,<br>
>and because (2) is a sum, (1) is a product of A×B (those are types<br>
>of a and b, even though Smalltalk-80 does not provide an explicit<br>
>notation for them). So we are just precomposing A>>value<br>
>and B>>value before the diagram for ifTrue:ifFalse:, and it HAS<br>
>to go from () to A / to B, and it can ONLY return self (per the<br>
>definition of "element"). Aha! So I would be inclined to guess<br>
>that we are just looking at plain bogus implementors of #value.<br>
>Let's look what we have in Cuis. Aha: Association>>value. Wow.<br>
>I can't think of a better example of what in a conversation some<br>
>time ago on this list we were calling "fake polymorphism".<br>
>The meaning of BlockClosure>>value and Association>>value have<br>
>nothing in common!<br>
><br>
>... now this got me curious... what other interesting code lurks<br>
>among the implementors of #value... ha, MessageSend>>value<br>
>special-cases nil arguments vs empty arguments, this smells to<br>
>Lisp's generalized-false, and I wonder if we can normalize that?<br>
>Just for kicks and giggles, let's see where the special-case may be<br>
>used... oh, if I remove it, Cuis dies spectacularly opening a<br>
>million debuggers in WorldMorph>>runLocalStepMethods:...<br>
>would be interesting to probe how deep that rabbit hole goes...<br>
><br>
>--<br>
>Cuis-dev mailing list<br>
><a href="mailto:Cuis-dev@lists.cuis.st" target="_blank">Cuis-dev@lists.cuis.st</a><br>
><a href="https://lists.cuis.st/mailman/listinfo/cuis-dev" rel="noreferrer" target="_blank">https://lists.cuis.st/mailman/listinfo/cuis-dev</a><br>
-- <br>
Cuis-dev mailing list<br>
<a href="mailto:Cuis-dev@lists.cuis.st" target="_blank">Cuis-dev@lists.cuis.st</a><br>
<a href="https://lists.cuis.st/mailman/listinfo/cuis-dev" rel="noreferrer" target="_blank">https://lists.cuis.st/mailman/listinfo/cuis-dev</a><br>
</blockquote></div></div>