The Inbox: HelpSystem-Core-ct.123.mcz

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

The Inbox: HelpSystem-Core-ct.123.mcz

commits-2
A new version of HelpSystem-Core was added to project The Inbox:
http://source.squeak.org/inbox/HelpSystem-Core-ct.123.mcz

==================== Summary ====================

Name: HelpSystem-Core-ct.123
Author: ct
Time: 13 October 2019, 9:04:08.373932 pm
UUID: dec7ceca-320f-d945-8d2a-c2f6a5e49a52
Ancestors: HelpSystem-Core-ct.120

Refactors HelpBrowser menu: Move menu stuff from HelpBrowser into HelpTopic hierarchy in favor of a better object design

Thanks again, Marcel :-)

=============== Diff against HelpSystem-Core-ct.120 ===============

Item was added:
+ ----- Method: AbstractHelpTopic>>browseTopicFromParent: (in category 'tools') -----
+ browseTopicFromParent: parentTopic
+
+ self canBrowseTopic
+ ifTrue: [^ self browseTopic].
+ parentTopic canBrowseSubtopic
+ ifTrue: [^ parentTopic browseSubtopic: self].
+ !

Item was added:
+ ----- Method: AbstractHelpTopic>>canBrowseSubtopic (in category 'testing') -----
+ canBrowseSubtopic
+
+ ^ false!

Item was added:
+ ----- Method: AbstractHelpTopic>>canBrowseTopic (in category 'testing') -----
+ canBrowseTopic
+
+ ^ false!

Item was added:
+ ----- Method: AbstractHelpTopic>>canBrowseTopicFromParent: (in category 'testing') -----
+ canBrowseTopicFromParent: parentTopic
+
+ ^ self canBrowseTopic or: [
+ parentTopic ifNotNil: #canBrowseSubtopic ifNil: [false]]!

Item was added:
+ ----- Method: AbstractHelpTopic>>topicMenu:parentTopic: (in category 'menus') -----
+ topicMenu: aMenu parentTopic: parentTopic
+
+ aMenu
+ add: 'Inspect (i)' translated target: self action: #inspect;
+ add: 'Explore (I)' translated target: self action: #explore.
+ (self canBrowseTopicFromParent: parentTopic)
+ ifTrue: [
+ aMenu add: 'Browse (b)' translated
+ target: self
+ selector: #browseTopicFromParent:
+ argumentList: {parentTopic} ].
+
+ ^ aMenu!

Item was added:
+ ----- Method: AbstractHelpTopic>>topicMenuKey:fromParent: (in category 'menus') -----
+ topicMenuKey: aChar fromParent: parentTopic
+
+ aChar
+ caseOf: {
+ [$b] -> [(self canBrowseTopicFromParent: parentTopic)
+ ifTrue: [ self browseTopicFromParent: parentTopic ]].
+ [$i] -> [self inspect].
+ [$I] -> [self explore] }
+ otherwise: [^ false].
+ ^ true!

Item was added:
+ ----- Method: ClassAPIHelpTopic>>canBrowseTopic (in category 'testing') -----
+ canBrowseTopic
+
+ ^ true!

Item was added:
+ ----- Method: ClassBasedHelpTopic>>canBrowseSubtopic (in category 'testing') -----
+ canBrowseSubtopic
+
+ ^ true!

Item was added:
+ ----- Method: ClassBasedHelpTopic>>canBrowseTopic (in category 'testing') -----
+ canBrowseTopic
+
+ ^ true!

Item was added:
+ ----- Method: DirectoryBasedHelpTopic>>canBrowseTopic (in category 'testing') -----
+ canBrowseTopic
+
+ ^ true!

Item was added:
+ ----- Method: FileBasedHelpTopic>>canBrowseTopic (in category 'testing') -----
+ canBrowseTopic
+
+ ^ true!

Item was removed:
- ----- Method: HelpBrowser>>browseTopic (in category 'actions') -----
- browseTopic
-
- ^ (self currentTopic respondsTo: #browseTopic)
- ifTrue: [self currentTopic browseTopic]
- ifFalse: [self currentParentTopic browseSubtopic: self currentTopic]!

Item was removed:
- ----- Method: HelpBrowser>>canBrowseTopic (in category 'testing') -----
- canBrowseTopic
-
- ^ (self currentTopic respondsTo: #browseTopic)
- or: [self currentParentTopic respondsTo: #browseSubtopic:]!

Item was removed:
- ----- Method: HelpBrowser>>exploreTopic (in category 'actions') -----
- exploreTopic
-
- ^ self currentTopic explore!

Item was removed:
- ----- Method: HelpBrowser>>inspectTopic (in category 'actions') -----
- inspectTopic
-
- ^ self currentTopic inspect!

Item was changed:
  ----- Method: HelpBrowser>>treeKey:from:event: (in category 'menus') -----
  treeKey: aChar from: aView event: anEvent
 
  anEvent anyModifierKeyPressed ifFalse: [^ false].
+ ^ (self currentTopic topicMenuKey: aChar fromParent: self currentParentTopic)!
- aChar
- caseOf: {
- [$b] -> [self browseTopic].
- [$i] -> [self inspectTopic].
- [$I] -> [self exploreTopic]. }
- otherwise: [^ false].
- ^ true!

Item was changed:
  ----- Method: HelpBrowser>>treeListMenu: (in category 'menus') -----
  treeListMenu: aMenu
  <treeListMenu>
 
+ ^ self currentTopic
+ ifNil: [aMenu]
+ ifNotNil: [:topic | topic
+ topicMenu: aMenu
+ parentTopic: self currentParentTopic]!
- self currentTopic ifNil: [^ aMenu].
-
- aMenu
- add: 'Inspect (i)' action: #inspectTopic;
- add: 'Explore (I)' action: #exploreTopic.
-
- self canBrowseTopic ifTrue: [
- aMenu
- addLine;
- add: 'Browse (b)' action: #browseTopic].
-
- ^ aMenu!

Item was added:
+ ----- Method: MethodListHelpTopic>>canBrowseTopic (in category 'testing') -----
+ canBrowseTopic
+
+ ^ true!

Item was added:
+ ----- Method: PackageAPIHelpTopic>>canBrowseTopic (in category 'testing') -----
+ canBrowseTopic
+
+ ^ true!


Reply | Threaded
Open this post in threaded view
|

Re: The Inbox: HelpSystem-Core-ct.123.mcz

marcel.taeumel
Quick suggestion on the formatting. This one:

^ self canBrowseTopic or: [
   parentTopic ifNotNil: #canBrowseSubtopic ifNil: [false]]

could become:

^ self canBrowseTopic or: [
   parentTopic
ifNil: [false]
ifNotNil: [:topic | topic canBrowseSubtopic]]

or:

^ self canBrowseTopic
      or: [parentTopic notNil and: [parentTopic canBrowseSubtopic]]

Hmmm... what are other opinions on this? There is no need for #ifNil/ifNotNil in such a boolean expression?

Best,
Marcel


Am 13.10.2019 21:04:19 schrieb [hidden email] <[hidden email]>:

A new version of HelpSystem-Core was added to project The Inbox:
http://source.squeak.org/inbox/HelpSystem-Core-ct.123.mcz

==================== Summary ====================

Name: HelpSystem-Core-ct.123
Author: ct
Time: 13 October 2019, 9:04:08.373932 pm
UUID: dec7ceca-320f-d945-8d2a-c2f6a5e49a52
Ancestors: HelpSystem-Core-ct.120

Refactors HelpBrowser menu: Move menu stuff from HelpBrowser into HelpTopic hierarchy in favor of a better object design

Thanks again, Marcel :-)

=============== Diff against HelpSystem-Core-ct.120 ===============

Item was added:
+ ----- Method: AbstractHelpTopic>>browseTopicFromParent: (in category 'tools') -----
+ browseTopicFromParent: parentTopic
+
+ self canBrowseTopic
+ ifTrue: [^ self browseTopic].
+ parentTopic canBrowseSubtopic
+ ifTrue: [^ parentTopic browseSubtopic: self].
+ !

Item was added:
+ ----- Method: AbstractHelpTopic>>canBrowseSubtopic (in category 'testing') -----
+ canBrowseSubtopic
+
+ ^ false!

Item was added:
+ ----- Method: AbstractHelpTopic>>canBrowseTopic (in category 'testing') -----
+ canBrowseTopic
+
+ ^ false!

Item was added:
+ ----- Method: AbstractHelpTopic>>canBrowseTopicFromParent: (in category 'testing') -----
+ canBrowseTopicFromParent: parentTopic
+
+ ^ self canBrowseTopic or: [
+ parentTopic ifNotNil: #canBrowseSubtopic ifNil: [false]]!

Item was added:
+ ----- Method: AbstractHelpTopic>>topicMenu:parentTopic: (in category 'menus') -----
+ topicMenu: aMenu parentTopic: parentTopic
+
+ aMenu
+ add: 'Inspect (i)' translated target: self action: #inspect;
+ add: 'Explore (I)' translated target: self action: #explore.
+ (self canBrowseTopicFromParent: parentTopic)
+ ifTrue: [
+ aMenu add: 'Browse (b)' translated
+ target: self
+ selector: #browseTopicFromParent:
+ argumentList: {parentTopic} ].
+
+ ^ aMenu!

Item was added:
+ ----- Method: AbstractHelpTopic>>topicMenuKey:fromParent: (in category 'menus') -----
+ topicMenuKey: aChar fromParent: parentTopic
+
+ aChar
+ caseOf: {
+ [$b] -> [(self canBrowseTopicFromParent: parentTopic)
+ ifTrue: [ self browseTopicFromParent: parentTopic ]].
+ [$i] -> [self inspect].
+ [$I] -> [self explore] }
+ otherwise: [^ false].
+ ^ true!

Item was added:
+ ----- Method: ClassAPIHelpTopic>>canBrowseTopic (in category 'testing') -----
+ canBrowseTopic
+
+ ^ true!

Item was added:
+ ----- Method: ClassBasedHelpTopic>>canBrowseSubtopic (in category 'testing') -----
+ canBrowseSubtopic
+
+ ^ true!

Item was added:
+ ----- Method: ClassBasedHelpTopic>>canBrowseTopic (in category 'testing') -----
+ canBrowseTopic
+
+ ^ true!

Item was added:
+ ----- Method: DirectoryBasedHelpTopic>>canBrowseTopic (in category 'testing') -----
+ canBrowseTopic
+
+ ^ true!

Item was added:
+ ----- Method: FileBasedHelpTopic>>canBrowseTopic (in category 'testing') -----
+ canBrowseTopic
+
+ ^ true!

Item was removed:
- ----- Method: HelpBrowser>>browseTopic (in category 'actions') -----
- browseTopic
-
- ^ (self currentTopic respondsTo: #browseTopic)
- ifTrue: [self currentTopic browseTopic]
- ifFalse: [self currentParentTopic browseSubtopic: self currentTopic]!

Item was removed:
- ----- Method: HelpBrowser>>canBrowseTopic (in category 'testing') -----
- canBrowseTopic
-
- ^ (self currentTopic respondsTo: #browseTopic)
- or: [self currentParentTopic respondsTo: #browseSubtopic:]!

Item was removed:
- ----- Method: HelpBrowser>>exploreTopic (in category 'actions') -----
- exploreTopic
-
- ^ self currentTopic explore!

Item was removed:
- ----- Method: HelpBrowser>>inspectTopic (in category 'actions') -----
- inspectTopic
-
- ^ self currentTopic inspect!

Item was changed:
----- Method: HelpBrowser>>treeKey:from:event: (in category 'menus') -----
treeKey: aChar from: aView event: anEvent

anEvent anyModifierKeyPressed ifFalse: [^ false].
+ ^ (self currentTopic topicMenuKey: aChar fromParent: self currentParentTopic)!
- aChar
- caseOf: {
- [$b] -> [self browseTopic].
- [$i] -> [self inspectTopic].
- [$I] -> [self exploreTopic]. }
- otherwise: [^ false].
- ^ true!

Item was changed:
----- Method: HelpBrowser>>treeListMenu: (in category 'menus') -----
treeListMenu: aMenu


+ ^ self currentTopic
+ ifNil: [aMenu]
+ ifNotNil: [:topic | topic
+ topicMenu: aMenu
+ parentTopic: self currentParentTopic]!
- self currentTopic ifNil: [^ aMenu].
-
- aMenu
- add: 'Inspect (i)' action: #inspectTopic;
- add: 'Explore (I)' action: #exploreTopic.
-
- self canBrowseTopic ifTrue: [
- aMenu
- addLine;
- add: 'Browse (b)' action: #browseTopic].
-
- ^ aMenu!

Item was added:
+ ----- Method: MethodListHelpTopic>>canBrowseTopic (in category 'testing') -----
+ canBrowseTopic
+
+ ^ true!

Item was added:
+ ----- Method: PackageAPIHelpTopic>>canBrowseTopic (in category 'testing') -----
+ canBrowseTopic
+
+ ^ true!




Reply | Threaded
Open this post in threaded view
|

Re: The Inbox: HelpSystem-Core-ct.123.mcz

Christoph Thiede

If you're needing a decision, I'd vote for option 3.


Von: Squeak-dev <[hidden email]> im Auftrag von Taeumel, Marcel
Gesendet: Mittwoch, 16. Oktober 2019 11:14:15
An: gettimothy via Squeak-dev
Betreff: Re: [squeak-dev] The Inbox: HelpSystem-Core-ct.123.mcz
 
Quick suggestion on the formatting. This one:

^ self canBrowseTopic or: [
   parentTopic ifNotNil: #canBrowseSubtopic ifNil: [false]]

could become:

^ self canBrowseTopic or: [
   parentTopic
ifNil: [false]
ifNotNil: [:topic | topic canBrowseSubtopic]]

or:

^ self canBrowseTopic
      or: [parentTopic notNil and: [parentTopic canBrowseSubtopic]]

Hmmm... what are other opinions on this? There is no need for #ifNil/ifNotNil in such a boolean expression?

Best,
Marcel


Am 13.10.2019 21:04:19 schrieb [hidden email] <[hidden email]>:

A new version of HelpSystem-Core was added to project The Inbox:
http://source.squeak.org/inbox/HelpSystem-Core-ct.123.mcz

==================== Summary ====================

Name: HelpSystem-Core-ct.123
Author: ct
Time: 13 October 2019, 9:04:08.373932 pm
UUID: dec7ceca-320f-d945-8d2a-c2f6a5e49a52
Ancestors: HelpSystem-Core-ct.120

Refactors HelpBrowser menu: Move menu stuff from HelpBrowser into HelpTopic hierarchy in favor of a better object design

Thanks again, Marcel :-)

=============== Diff against HelpSystem-Core-ct.120 ===============

Item was added:
+ ----- Method: AbstractHelpTopic>>browseTopicFromParent: (in category 'tools') -----
+ browseTopicFromParent: parentTopic
+
+ self canBrowseTopic
+ ifTrue: [^ self browseTopic].
+ parentTopic canBrowseSubtopic
+ ifTrue: [^ parentTopic browseSubtopic: self].
+ !

Item was added:
+ ----- Method: AbstractHelpTopic>>canBrowseSubtopic (in category 'testing') -----
+ canBrowseSubtopic
+
+ ^ false!

Item was added:
+ ----- Method: AbstractHelpTopic>>canBrowseTopic (in category 'testing') -----
+ canBrowseTopic
+
+ ^ false!

Item was added:
+ ----- Method: AbstractHelpTopic>>canBrowseTopicFromParent: (in category 'testing') -----
+ canBrowseTopicFromParent: parentTopic
+
+ ^ self canBrowseTopic or: [
+ parentTopic ifNotNil: #canBrowseSubtopic ifNil: [false]]!

Item was added:
+ ----- Method: AbstractHelpTopic>>topicMenu:parentTopic: (in category 'menus') -----
+ topicMenu: aMenu parentTopic: parentTopic
+
+ aMenu
+ add: 'Inspect (i)' translated target: self action: #inspect;
+ add: 'Explore (I)' translated target: self action: #explore.
+ (self canBrowseTopicFromParent: parentTopic)
+ ifTrue: [
+ aMenu add: 'Browse (b)' translated
+ target: self
+ selector: #browseTopicFromParent:
+ argumentList: {parentTopic} ].
+
+ ^ aMenu!

Item was added:
+ ----- Method: AbstractHelpTopic>>topicMenuKey:fromParent: (in category 'menus') -----
+ topicMenuKey: aChar fromParent: parentTopic
+
+ aChar
+ caseOf: {
+ [$b] -> [(self canBrowseTopicFromParent: parentTopic)
+ ifTrue: [ self browseTopicFromParent: parentTopic ]].
+ [$i] -> [self inspect].
+ [$I] -> [self explore] }
+ otherwise: [^ false].
+ ^ true!

Item was added:
+ ----- Method: ClassAPIHelpTopic>>canBrowseTopic (in category 'testing') -----
+ canBrowseTopic
+
+ ^ true!

Item was added:
+ ----- Method: ClassBasedHelpTopic>>canBrowseSubtopic (in category 'testing') -----
+ canBrowseSubtopic
+
+ ^ true!

Item was added:
+ ----- Method: ClassBasedHelpTopic>>canBrowseTopic (in category 'testing') -----
+ canBrowseTopic
+
+ ^ true!

Item was added:
+ ----- Method: DirectoryBasedHelpTopic>>canBrowseTopic (in category 'testing') -----
+ canBrowseTopic
+
+ ^ true!

Item was added:
+ ----- Method: FileBasedHelpTopic>>canBrowseTopic (in category 'testing') -----
+ canBrowseTopic
+
+ ^ true!

Item was removed:
- ----- Method: HelpBrowser>>browseTopic (in category 'actions') -----
- browseTopic
-
- ^ (self currentTopic respondsTo: #browseTopic)
- ifTrue: [self currentTopic browseTopic]
- ifFalse: [self currentParentTopic browseSubtopic: self currentTopic]!

Item was removed:
- ----- Method: HelpBrowser>>canBrowseTopic (in category 'testing') -----
- canBrowseTopic
-
- ^ (self currentTopic respondsTo: #browseTopic)
- or: [self currentParentTopic respondsTo: #browseSubtopic:]!

Item was removed:
- ----- Method: HelpBrowser>>exploreTopic (in category 'actions') -----
- exploreTopic
-
- ^ self currentTopic explore!

Item was removed:
- ----- Method: HelpBrowser>>inspectTopic (in category 'actions') -----
- inspectTopic
-
- ^ self currentTopic inspect!

Item was changed:
----- Method: HelpBrowser>>treeKey:from:event: (in category 'menus') -----
treeKey: aChar from: aView event: anEvent

anEvent anyModifierKeyPressed ifFalse: [^ false].
+ ^ (self currentTopic topicMenuKey: aChar fromParent: self currentParentTopic)!
- aChar
- caseOf: {
- [$b] -> [self browseTopic].
- [$i] -> [self inspectTopic].
- [$I] -> [self exploreTopic]. }
- otherwise: [^ false].
- ^ true!

Item was changed:
----- Method: HelpBrowser>>treeListMenu: (in category 'menus') -----
treeListMenu: aMenu


+ ^ self currentTopic
+ ifNil: [aMenu]
+ ifNotNil: [:topic | topic
+ topicMenu: aMenu
+ parentTopic: self currentParentTopic]!
- self currentTopic ifNil: [^ aMenu].
-
- aMenu
- add: 'Inspect (i)' action: #inspectTopic;
- add: 'Explore (I)' action: #exploreTopic.
-
- self canBrowseTopic ifTrue: [
- aMenu
- addLine;
- add: 'Browse (b)' action: #browseTopic].
-
- ^ aMenu!

Item was added:
+ ----- Method: MethodListHelpTopic>>canBrowseTopic (in category 'testing') -----
+ canBrowseTopic
+
+ ^ true!

Item was added:
+ ----- Method: PackageAPIHelpTopic>>canBrowseTopic (in category 'testing') -----
+ canBrowseTopic
+
+ ^ true!




Reply | Threaded
Open this post in threaded view
|

#isNilOr:, #notNilAnd:

Christoph Thiede

Hi all, just another idea for a hypothetic extension to ProtoObject:


ProtoObject >> #isNilOr: aBlock

^ aBlock cull: self

UndefinedObject >> #isNilOr: aBlock

^ true

ProtoObject >> #notNilAnd: aBlock

^ aBlock cull: self

UndefinedObject >> #notNilAnd: aBlock

^ false


Examples:

parentObject notNilAnd: #canBrowseSubtopic.

helpClass notNilAnd: [:hC| hC usesCodeStyling].

self selectionIndex isNilOr: [:index | index > 2].

anObject isNilOr: #isZero.

foo isNilOr: #isNotEmpty.


Just a bit syntactic sugar for idiomatic problems such as below. In particular, I found several possible applications for #notNilAnd: in my image.


Go ahead and tell me why it would be a bad idea :)


Best,

Christoph



Von: Squeak-dev <[hidden email]> im Auftrag von Thiede, Christoph
Gesendet: Freitag, 25. Oktober 2019 19:21 Uhr
An: gettimothy via Squeak-dev
Betreff: Re: [squeak-dev] The Inbox: HelpSystem-Core-ct.123.mcz
 

If you're needing a decision, I'd vote for option 3.


Von: Squeak-dev <[hidden email]> im Auftrag von Taeumel, Marcel
Gesendet: Mittwoch, 16. Oktober 2019 11:14:15
An: gettimothy via Squeak-dev
Betreff: Re: [squeak-dev] The Inbox: HelpSystem-Core-ct.123.mcz
 
Quick suggestion on the formatting. This one:

^ self canBrowseTopic or: [
   parentTopic ifNotNil: #canBrowseSubtopic ifNil: [false]]

could become:

^ self canBrowseTopic or: [
   parentTopic
ifNil: [false]
ifNotNil: [:topic | topic canBrowseSubtopic]]

or:

^ self canBrowseTopic
      or: [parentTopic notNil and: [parentTopic canBrowseSubtopic]]

Hmmm... what are other opinions on this? There is no need for #ifNil/ifNotNil in such a boolean expression?

Best,
Marcel


Am 13.10.2019 21:04:19 schrieb [hidden email] <[hidden email]>:

A new version of HelpSystem-Core was added to project The Inbox:
http://source.squeak.org/inbox/HelpSystem-Core-ct.123.mcz

==================== Summary ====================

Name: HelpSystem-Core-ct.123
Author: ct
Time: 13 October 2019, 9:04:08.373932 pm
UUID: dec7ceca-320f-d945-8d2a-c2f6a5e49a52
Ancestors: HelpSystem-Core-ct.120

Refactors HelpBrowser menu: Move menu stuff from HelpBrowser into HelpTopic hierarchy in favor of a better object design

Thanks again, Marcel :-)

=============== Diff against HelpSystem-Core-ct.120 ===============

Item was added:
+ ----- Method: AbstractHelpTopic>>browseTopicFromParent: (in category 'tools') -----
+ browseTopicFromParent: parentTopic
+
+ self canBrowseTopic
+ ifTrue: [^ self browseTopic].
+ parentTopic canBrowseSubtopic
+ ifTrue: [^ parentTopic browseSubtopic: self].
+ !

Item was added:
+ ----- Method: AbstractHelpTopic>>canBrowseSubtopic (in category 'testing') -----
+ canBrowseSubtopic
+
+ ^ false!

Item was added:
+ ----- Method: AbstractHelpTopic>>canBrowseTopic (in category 'testing') -----
+ canBrowseTopic
+
+ ^ false!

Item was added:
+ ----- Method: AbstractHelpTopic>>canBrowseTopicFromParent: (in category 'testing') -----
+ canBrowseTopicFromParent: parentTopic
+
+ ^ self canBrowseTopic or: [
+ parentTopic ifNotNil: #canBrowseSubtopic ifNil: [false]]!

Item was added:
+ ----- Method: AbstractHelpTopic>>topicMenu:parentTopic: (in category 'menus') -----
+ topicMenu: aMenu parentTopic: parentTopic
+
+ aMenu
+ add: 'Inspect (i)' translated target: self action: #inspect;
+ add: 'Explore (I)' translated target: self action: #explore.
+ (self canBrowseTopicFromParent: parentTopic)
+ ifTrue: [
+ aMenu add: 'Browse (b)' translated
+ target: self
+ selector: #browseTopicFromParent:
+ argumentList: {parentTopic} ].
+
+ ^ aMenu!

Item was added:
+ ----- Method: AbstractHelpTopic>>topicMenuKey:fromParent: (in category 'menus') -----
+ topicMenuKey: aChar fromParent: parentTopic
+
+ aChar
+ caseOf: {
+ [$b] -> [(self canBrowseTopicFromParent: parentTopic)
+ ifTrue: [ self browseTopicFromParent: parentTopic ]].
+ [$i] -> [self inspect].
+ [$I] -> [self explore] }
+ otherwise: [^ false].
+ ^ true!

Item was added:
+ ----- Method: ClassAPIHelpTopic>>canBrowseTopic (in category 'testing') -----
+ canBrowseTopic
+
+ ^ true!

Item was added:
+ ----- Method: ClassBasedHelpTopic>>canBrowseSubtopic (in category 'testing') -----
+ canBrowseSubtopic
+
+ ^ true!

Item was added:
+ ----- Method: ClassBasedHelpTopic>>canBrowseTopic (in category 'testing') -----
+ canBrowseTopic
+
+ ^ true!

Item was added:
+ ----- Method: DirectoryBasedHelpTopic>>canBrowseTopic (in category 'testing') -----
+ canBrowseTopic
+
+ ^ true!

Item was added:
+ ----- Method: FileBasedHelpTopic>>canBrowseTopic (in category 'testing') -----
+ canBrowseTopic
+
+ ^ true!

Item was removed:
- ----- Method: HelpBrowser>>browseTopic (in category 'actions') -----
- browseTopic
-
- ^ (self currentTopic respondsTo: #browseTopic)
- ifTrue: [self currentTopic browseTopic]
- ifFalse: [self currentParentTopic browseSubtopic: self currentTopic]!

Item was removed:
- ----- Method: HelpBrowser>>canBrowseTopic (in category 'testing') -----
- canBrowseTopic
-
- ^ (self currentTopic respondsTo: #browseTopic)
- or: [self currentParentTopic respondsTo: #browseSubtopic:]!

Item was removed:
- ----- Method: HelpBrowser>>exploreTopic (in category 'actions') -----
- exploreTopic
-
- ^ self currentTopic explore!

Item was removed:
- ----- Method: HelpBrowser>>inspectTopic (in category 'actions') -----
- inspectTopic
-
- ^ self currentTopic inspect!

Item was changed:
----- Method: HelpBrowser>>treeKey:from:event: (in category 'menus') -----
treeKey: aChar from: aView event: anEvent

anEvent anyModifierKeyPressed ifFalse: [^ false].
+ ^ (self currentTopic topicMenuKey: aChar fromParent: self currentParentTopic)!
- aChar
- caseOf: {
- [$b] -> [self browseTopic].
- [$i] -> [self inspectTopic].
- [$I] -> [self exploreTopic]. }
- otherwise: [^ false].
- ^ true!

Item was changed:
----- Method: HelpBrowser>>treeListMenu: (in category 'menus') -----
treeListMenu: aMenu


+ ^ self currentTopic
+ ifNil: [aMenu]
+ ifNotNil: [:topic | topic
+ topicMenu: aMenu
+ parentTopic: self currentParentTopic]!
- self currentTopic ifNil: [^ aMenu].
-
- aMenu
- add: 'Inspect (i)' action: #inspectTopic;
- add: 'Explore (I)' action: #exploreTopic.
-
- self canBrowseTopic ifTrue: [
- aMenu
- addLine;
- add: 'Browse (b)' action: #browseTopic].
-
- ^ aMenu!

Item was added:
+ ----- Method: MethodListHelpTopic>>canBrowseTopic (in category 'testing') -----
+ canBrowseTopic
+
+ ^ true!

Item was added:
+ ----- Method: PackageAPIHelpTopic>>canBrowseTopic (in category 'testing') -----
+ canBrowseTopic
+
+ ^ true!





ATT00001.txt (6 bytes) Download Attachment
Reply | Threaded
Open this post in threaded view
|

Re: #isNilOr:, #notNilAnd:

Christoph Thiede

Hi all,


after the release has been managed successfully, I would like to kindly push this proposal. In the latest months, I found myself to really often desiring #isNilOr: or #notNilAnd:. How would you think about these convenience selectors? :-)


Best,

Christoph


Von: Thiede, Christoph
Gesendet: Mittwoch, 11. Dezember 2019 11:01:44
An: Squeak Dev
Betreff: #isNilOr:, #notNilAnd:
 

Hi all, just another idea for a hypothetic extension to ProtoObject:


ProtoObject >> #isNilOr: aBlock

^ aBlock cull: self

UndefinedObject >> #isNilOr: aBlock

^ true

ProtoObject >> #notNilAnd: aBlock

^ aBlock cull: self

UndefinedObject >> #notNilAnd: aBlock

^ false


Examples:

parentObject notNilAnd: #canBrowseSubtopic.

helpClass notNilAnd: [:hC| hC usesCodeStyling].

self selectionIndex isNilOr: [:index | index > 2].

anObject isNilOr: #isZero.

foo isNilOr: #isNotEmpty.


Just a bit syntactic sugar for idiomatic problems such as below. In particular, I found several possible applications for #notNilAnd: in my image.


Go ahead and tell me why it would be a bad idea :)


Best,

Christoph



Von: Squeak-dev <[hidden email]> im Auftrag von Thiede, Christoph
Gesendet: Freitag, 25. Oktober 2019 19:21 Uhr
An: gettimothy via Squeak-dev
Betreff: Re: [squeak-dev] The Inbox: HelpSystem-Core-ct.123.mcz
 

If you're needing a decision, I'd vote for option 3.


Von: Squeak-dev <[hidden email]> im Auftrag von Taeumel, Marcel
Gesendet: Mittwoch, 16. Oktober 2019 11:14:15
An: gettimothy via Squeak-dev
Betreff: Re: [squeak-dev] The Inbox: HelpSystem-Core-ct.123.mcz
 
Quick suggestion on the formatting. This one:

^ self canBrowseTopic or: [
   parentTopic ifNotNil: #canBrowseSubtopic ifNil: [false]]

could become:

^ self canBrowseTopic or: [
   parentTopic
ifNil: [false]
ifNotNil: [:topic | topic canBrowseSubtopic]]

or:

^ self canBrowseTopic
      or: [parentTopic notNil and: [parentTopic canBrowseSubtopic]]

Hmmm... what are other opinions on this? There is no need for #ifNil/ifNotNil in such a boolean expression?

Best,
Marcel


Am 13.10.2019 21:04:19 schrieb [hidden email] <[hidden email]>:

A new version of HelpSystem-Core was added to project The Inbox:
http://source.squeak.org/inbox/HelpSystem-Core-ct.123.mcz

==================== Summary ====================

Name: HelpSystem-Core-ct.123
Author: ct
Time: 13 October 2019, 9:04:08.373932 pm
UUID: dec7ceca-320f-d945-8d2a-c2f6a5e49a52
Ancestors: HelpSystem-Core-ct.120

Refactors HelpBrowser menu: Move menu stuff from HelpBrowser into HelpTopic hierarchy in favor of a better object design

Thanks again, Marcel :-)

=============== Diff against HelpSystem-Core-ct.120 ===============

Item was added:
+ ----- Method: AbstractHelpTopic>>browseTopicFromParent: (in category 'tools') -----
+ browseTopicFromParent: parentTopic
+
+ self canBrowseTopic
+ ifTrue: [^ self browseTopic].
+ parentTopic canBrowseSubtopic
+ ifTrue: [^ parentTopic browseSubtopic: self].
+ !

Item was added:
+ ----- Method: AbstractHelpTopic>>canBrowseSubtopic (in category 'testing') -----
+ canBrowseSubtopic
+
+ ^ false!

Item was added:
+ ----- Method: AbstractHelpTopic>>canBrowseTopic (in category 'testing') -----
+ canBrowseTopic
+
+ ^ false!

Item was added:
+ ----- Method: AbstractHelpTopic>>canBrowseTopicFromParent: (in category 'testing') -----
+ canBrowseTopicFromParent: parentTopic
+
+ ^ self canBrowseTopic or: [
+ parentTopic ifNotNil: #canBrowseSubtopic ifNil: [false]]!

Item was added:
+ ----- Method: AbstractHelpTopic>>topicMenu:parentTopic: (in category 'menus') -----
+ topicMenu: aMenu parentTopic: parentTopic
+
+ aMenu
+ add: 'Inspect (i)' translated target: self action: #inspect;
+ add: 'Explore (I)' translated target: self action: #explore.
+ (self canBrowseTopicFromParent: parentTopic)
+ ifTrue: [
+ aMenu add: 'Browse (b)' translated
+ target: self
+ selector: #browseTopicFromParent:
+ argumentList: {parentTopic} ].
+
+ ^ aMenu!

Item was added:
+ ----- Method: AbstractHelpTopic>>topicMenuKey:fromParent: (in category 'menus') -----
+ topicMenuKey: aChar fromParent: parentTopic
+
+ aChar
+ caseOf: {
+ [$b] -> [(self canBrowseTopicFromParent: parentTopic)
+ ifTrue: [ self browseTopicFromParent: parentTopic ]].
+ [$i] -> [self inspect].
+ [$I] -> [self explore] }
+ otherwise: [^ false].
+ ^ true!

Item was added:
+ ----- Method: ClassAPIHelpTopic>>canBrowseTopic (in category 'testing') -----
+ canBrowseTopic
+
+ ^ true!

Item was added:
+ ----- Method: ClassBasedHelpTopic>>canBrowseSubtopic (in category 'testing') -----
+ canBrowseSubtopic
+
+ ^ true!

Item was added:
+ ----- Method: ClassBasedHelpTopic>>canBrowseTopic (in category 'testing') -----
+ canBrowseTopic
+
+ ^ true!

Item was added:
+ ----- Method: DirectoryBasedHelpTopic>>canBrowseTopic (in category 'testing') -----
+ canBrowseTopic
+
+ ^ true!

Item was added:
+ ----- Method: FileBasedHelpTopic>>canBrowseTopic (in category 'testing') -----
+ canBrowseTopic
+
+ ^ true!

Item was removed:
- ----- Method: HelpBrowser>>browseTopic (in category 'actions') -----
- browseTopic
-
- ^ (self currentTopic respondsTo: #browseTopic)
- ifTrue: [self currentTopic browseTopic]
- ifFalse: [self currentParentTopic browseSubtopic: self currentTopic]!

Item was removed:
- ----- Method: HelpBrowser>>canBrowseTopic (in category 'testing') -----
- canBrowseTopic
-
- ^ (self currentTopic respondsTo: #browseTopic)
- or: [self currentParentTopic respondsTo: #browseSubtopic:]!

Item was removed:
- ----- Method: HelpBrowser>>exploreTopic (in category 'actions') -----
- exploreTopic
-
- ^ self currentTopic explore!

Item was removed:
- ----- Method: HelpBrowser>>inspectTopic (in category 'actions') -----
- inspectTopic
-
- ^ self currentTopic inspect!

Item was changed:
----- Method: HelpBrowser>>treeKey:from:event: (in category 'menus') -----
treeKey: aChar from: aView event: anEvent

anEvent anyModifierKeyPressed ifFalse: [^ false].
+ ^ (self currentTopic topicMenuKey: aChar fromParent: self currentParentTopic)!
- aChar
- caseOf: {
- [$b] -> [self browseTopic].
- [$i] -> [self inspectTopic].
- [$I] -> [self exploreTopic]. }
- otherwise: [^ false].
- ^ true!

Item was changed:
----- Method: HelpBrowser>>treeListMenu: (in category 'menus') -----
treeListMenu: aMenu


+ ^ self currentTopic
+ ifNil: [aMenu]
+ ifNotNil: [:topic | topic
+ topicMenu: aMenu
+ parentTopic: self currentParentTopic]!
- self currentTopic ifNil: [^ aMenu].
-
- aMenu
- add: 'Inspect (i)' action: #inspectTopic;
- add: 'Explore (I)' action: #exploreTopic.
-
- self canBrowseTopic ifTrue: [
- aMenu
- addLine;
- add: 'Browse (b)' action: #browseTopic].
-
- ^ aMenu!

Item was added:
+ ----- Method: MethodListHelpTopic>>canBrowseTopic (in category 'testing') -----
+ canBrowseTopic
+
+ ^ true!

Item was added:
+ ----- Method: PackageAPIHelpTopic>>canBrowseTopic (in category 'testing') -----
+ canBrowseTopic
+
+ ^ true!




Reply | Threaded
Open this post in threaded view
|

Re: The Inbox: HelpSystem-Core-ct.123.mcz

Eliot Miranda-2
In reply to this post by Christoph Thiede


On Oct 25, 2019, at 10:21 AM, Thiede, Christoph <[hidden email]> wrote:



If you're needing a decision, I'd vote for option 3.


+1.  See Rectangular Block in Beck’s Smalltalk Best Patterns Practice




Von: Squeak-dev <[hidden email]> im Auftrag von Taeumel, Marcel
Gesendet: Mittwoch, 16. Oktober 2019 11:14:15
An: gettimothy via Squeak-dev
Betreff: Re: [squeak-dev] The Inbox: HelpSystem-Core-ct.123.mcz
 
Quick suggestion on the formatting. This one:

^ self canBrowseTopic or: [
   parentTopic ifNotNil: #canBrowseSubtopic ifNil: [false]]

could become:

^ self canBrowseTopic or: [
   parentTopic
ifNil: [false]
ifNotNil: [:topic | topic canBrowseSubtopic]]

or:

^ self canBrowseTopic
      or: [parentTopic notNil and: [parentTopic canBrowseSubtopic]]

Hmmm... what are other opinions on this? There is no need for #ifNil/ifNotNil in such a boolean expression?

Best,
Marcel


Am 13.10.2019 21:04:19 schrieb [hidden email] <[hidden email]>:

A new version of HelpSystem-Core was added to project The Inbox:
http://source.squeak.org/inbox/HelpSystem-Core-ct.123.mcz

==================== Summary ====================

Name: HelpSystem-Core-ct.123
Author: ct
Time: 13 October 2019, 9:04:08.373932 pm
UUID: dec7ceca-320f-d945-8d2a-c2f6a5e49a52
Ancestors: HelpSystem-Core-ct.120

Refactors HelpBrowser menu: Move menu stuff from HelpBrowser into HelpTopic hierarchy in favor of a better object design

Thanks again, Marcel :-)

=============== Diff against HelpSystem-Core-ct.120 ===============

Item was added:
+ ----- Method: AbstractHelpTopic>>browseTopicFromParent: (in category 'tools') -----
+ browseTopicFromParent: parentTopic
+
+ self canBrowseTopic
+ ifTrue: [^ self browseTopic].
+ parentTopic canBrowseSubtopic
+ ifTrue: [^ parentTopic browseSubtopic: self].
+ !

Item was added:
+ ----- Method: AbstractHelpTopic>>canBrowseSubtopic (in category 'testing') -----
+ canBrowseSubtopic
+
+ ^ false!

Item was added:
+ ----- Method: AbstractHelpTopic>>canBrowseTopic (in category 'testing') -----
+ canBrowseTopic
+
+ ^ false!

Item was added:
+ ----- Method: AbstractHelpTopic>>canBrowseTopicFromParent: (in category 'testing') -----
+ canBrowseTopicFromParent: parentTopic
+
+ ^ self canBrowseTopic or: [
+ parentTopic ifNotNil: #canBrowseSubtopic ifNil: [false]]!

Item was added:
+ ----- Method: AbstractHelpTopic>>topicMenu:parentTopic: (in category 'menus') -----
+ topicMenu: aMenu parentTopic: parentTopic
+
+ aMenu
+ add: 'Inspect (i)' translated target: self action: #inspect;
+ add: 'Explore (I)' translated target: self action: #explore.
+ (self canBrowseTopicFromParent: parentTopic)
+ ifTrue: [
+ aMenu add: 'Browse (b)' translated
+ target: self
+ selector: #browseTopicFromParent:
+ argumentList: {parentTopic} ].
+
+ ^ aMenu!

Item was added:
+ ----- Method: AbstractHelpTopic>>topicMenuKey:fromParent: (in category 'menus') -----
+ topicMenuKey: aChar fromParent: parentTopic
+
+ aChar
+ caseOf: {
+ [$b] -> [(self canBrowseTopicFromParent: parentTopic)
+ ifTrue: [ self browseTopicFromParent: parentTopic ]].
+ [$i] -> [self inspect].
+ [$I] -> [self explore] }
+ otherwise: [^ false].
+ ^ true!

Item was added:
+ ----- Method: ClassAPIHelpTopic>>canBrowseTopic (in category 'testing') -----
+ canBrowseTopic
+
+ ^ true!

Item was added:
+ ----- Method: ClassBasedHelpTopic>>canBrowseSubtopic (in category 'testing') -----
+ canBrowseSubtopic
+
+ ^ true!

Item was added:
+ ----- Method: ClassBasedHelpTopic>>canBrowseTopic (in category 'testing') -----
+ canBrowseTopic
+
+ ^ true!

Item was added:
+ ----- Method: DirectoryBasedHelpTopic>>canBrowseTopic (in category 'testing') -----
+ canBrowseTopic
+
+ ^ true!

Item was added:
+ ----- Method: FileBasedHelpTopic>>canBrowseTopic (in category 'testing') -----
+ canBrowseTopic
+
+ ^ true!

Item was removed:
- ----- Method: HelpBrowser>>browseTopic (in category 'actions') -----
- browseTopic
-
- ^ (self currentTopic respondsTo: #browseTopic)
- ifTrue: [self currentTopic browseTopic]
- ifFalse: [self currentParentTopic browseSubtopic: self currentTopic]!

Item was removed:
- ----- Method: HelpBrowser>>canBrowseTopic (in category 'testing') -----
- canBrowseTopic
-
- ^ (self currentTopic respondsTo: #browseTopic)
- or: [self currentParentTopic respondsTo: #browseSubtopic:]!

Item was removed:
- ----- Method: HelpBrowser>>exploreTopic (in category 'actions') -----
- exploreTopic
-
- ^ self currentTopic explore!

Item was removed:
- ----- Method: HelpBrowser>>inspectTopic (in category 'actions') -----
- inspectTopic
-
- ^ self currentTopic inspect!

Item was changed:
----- Method: HelpBrowser>>treeKey:from:event: (in category 'menus') -----
treeKey: aChar from: aView event: anEvent

anEvent anyModifierKeyPressed ifFalse: [^ false].
+ ^ (self currentTopic topicMenuKey: aChar fromParent: self currentParentTopic)!
- aChar
- caseOf: {
- [$b] -> [self browseTopic].
- [$i] -> [self inspectTopic].
- [$I] -> [self exploreTopic]. }
- otherwise: [^ false].
- ^ true!

Item was changed:
----- Method: HelpBrowser>>treeListMenu: (in category 'menus') -----
treeListMenu: aMenu


+ ^ self currentTopic
+ ifNil: [aMenu]
+ ifNotNil: [:topic | topic
+ topicMenu: aMenu
+ parentTopic: self currentParentTopic]!
- self currentTopic ifNil: [^ aMenu].
-
- aMenu
- add: 'Inspect (i)' action: #inspectTopic;
- add: 'Explore (I)' action: #exploreTopic.
-
- self canBrowseTopic ifTrue: [
- aMenu
- addLine;
- add: 'Browse (b)' action: #browseTopic].
-
- ^ aMenu!

Item was added:
+ ----- Method: MethodListHelpTopic>>canBrowseTopic (in category 'testing') -----
+ canBrowseTopic
+
+ ^ true!

Item was added:
+ ----- Method: PackageAPIHelpTopic>>canBrowseTopic (in category 'testing') -----
+ canBrowseTopic
+
+ ^ true!





Reply | Threaded
Open this post in threaded view
|

Re: #isNilOr:, #notNilAnd:

Jakob Reschke-2
In reply to this post by Christoph Thiede
My opinion: for scripting ok, for the general language not yet convinced. `notNil and: []` + `isNil or: []` are only slightly longer and somehow easier to read for me. Remain the block arguments; I also find myself avoiding temporaries sometimes, but Levente is right that they are the idiomatic way to to this, not in:-like constructs: (xyz := ...) notNil and: [xyz ...]

Maybe one should rather refactor the candidate senders to use less nils? Or guard against nil at the top if possible?

Have you had a look at Java 8's Optional class? Nil can receive messages in Smalltalk, which Java null can't. Is nil therefore a good empty Optional or should these concepts be separate?

Thiede, Christoph <[hidden email]> schrieb am Mo., 16. März 2020, 19:58:

Hi all,


after the release has been managed successfully, I would like to kindly push this proposal. In the latest months, I found myself to really often desiring #isNilOr: or #notNilAnd:. How would you think about these convenience selectors? :-)


Best,

Christoph


Von: Thiede, Christoph
Gesendet: Mittwoch, 11. Dezember 2019 11:01:44
An: Squeak Dev
Betreff: #isNilOr:, #notNilAnd:
 

Hi all, just another idea for a hypothetic extension to ProtoObject:


ProtoObject >> #isNilOr: aBlock

^ aBlock cull: self

UndefinedObject >> #isNilOr: aBlock

^ true

ProtoObject >> #notNilAnd: aBlock

^ aBlock cull: self

UndefinedObject >> #notNilAnd: aBlock

^ false


Examples:

parentObject notNilAnd: #canBrowseSubtopic.

helpClass notNilAnd: [:hC| hC usesCodeStyling].

self selectionIndex isNilOr: [:index | index > 2].

anObject isNilOr: #isZero.

foo isNilOr: #isNotEmpty.


Just a bit syntactic sugar for idiomatic problems such as below. In particular, I found several possible applications for #notNilAnd: in my image.


Go ahead and tell me why it would be a bad idea :)


Best,

Christoph



Von: Squeak-dev <[hidden email]> im Auftrag von Thiede, Christoph
Gesendet: Freitag, 25. Oktober 2019 19:21 Uhr
An: gettimothy via Squeak-dev
Betreff: Re: [squeak-dev] The Inbox: HelpSystem-Core-ct.123.mcz
 

If you're needing a decision, I'd vote for option 3.


Von: Squeak-dev <[hidden email]> im Auftrag von Taeumel, Marcel
Gesendet: Mittwoch, 16. Oktober 2019 11:14:15
An: gettimothy via Squeak-dev
Betreff: Re: [squeak-dev] The Inbox: HelpSystem-Core-ct.123.mcz
 
Quick suggestion on the formatting. This one:

^ self canBrowseTopic or: [
   parentTopic ifNotNil: #canBrowseSubtopic ifNil: [false]]

could become:

^ self canBrowseTopic or: [
   parentTopic
ifNil: [false]
ifNotNil: [:topic | topic canBrowseSubtopic]]

or:

^ self canBrowseTopic
      or: [parentTopic notNil and: [parentTopic canBrowseSubtopic]]

Hmmm... what are other opinions on this? There is no need for #ifNil/ifNotNil in such a boolean expression?

Best,
Marcel


Am 13.10.2019 21:04:19 schrieb [hidden email] <[hidden email]>:

A new version of HelpSystem-Core was added to project The Inbox:
http://source.squeak.org/inbox/HelpSystem-Core-ct.123.mcz

==================== Summary ====================

Name: HelpSystem-Core-ct.123
Author: ct
Time: 13 October 2019, 9:04:08.373932 pm
UUID: dec7ceca-320f-d945-8d2a-c2f6a5e49a52
Ancestors: HelpSystem-Core-ct.120

Refactors HelpBrowser menu: Move menu stuff from HelpBrowser into HelpTopic hierarchy in favor of a better object design

Thanks again, Marcel :-)

=============== Diff against HelpSystem-Core-ct.120 ===============

Item was added:
+ ----- Method: AbstractHelpTopic>>browseTopicFromParent: (in category 'tools') -----
+ browseTopicFromParent: parentTopic
+
+ self canBrowseTopic
+ ifTrue: [^ self browseTopic].
+ parentTopic canBrowseSubtopic
+ ifTrue: [^ parentTopic browseSubtopic: self].
+ !

Item was added:
+ ----- Method: AbstractHelpTopic>>canBrowseSubtopic (in category 'testing') -----
+ canBrowseSubtopic
+
+ ^ false!

Item was added:
+ ----- Method: AbstractHelpTopic>>canBrowseTopic (in category 'testing') -----
+ canBrowseTopic
+
+ ^ false!

Item was added:
+ ----- Method: AbstractHelpTopic>>canBrowseTopicFromParent: (in category 'testing') -----
+ canBrowseTopicFromParent: parentTopic
+
+ ^ self canBrowseTopic or: [
+ parentTopic ifNotNil: #canBrowseSubtopic ifNil: [false]]!

Item was added:
+ ----- Method: AbstractHelpTopic>>topicMenu:parentTopic: (in category 'menus') -----
+ topicMenu: aMenu parentTopic: parentTopic
+
+ aMenu
+ add: 'Inspect (i)' translated target: self action: #inspect;
+ add: 'Explore (I)' translated target: self action: #explore.
+ (self canBrowseTopicFromParent: parentTopic)
+ ifTrue: [
+ aMenu add: 'Browse (b)' translated
+ target: self
+ selector: #browseTopicFromParent:
+ argumentList: {parentTopic} ].
+
+ ^ aMenu!

Item was added:
+ ----- Method: AbstractHelpTopic>>topicMenuKey:fromParent: (in category 'menus') -----
+ topicMenuKey: aChar fromParent: parentTopic
+
+ aChar
+ caseOf: {
+ [$b] -> [(self canBrowseTopicFromParent: parentTopic)
+ ifTrue: [ self browseTopicFromParent: parentTopic ]].
+ [$i] -> [self inspect].
+ [$I] -> [self explore] }
+ otherwise: [^ false].
+ ^ true!

Item was added:
+ ----- Method: ClassAPIHelpTopic>>canBrowseTopic (in category 'testing') -----
+ canBrowseTopic
+
+ ^ true!

Item was added:
+ ----- Method: ClassBasedHelpTopic>>canBrowseSubtopic (in category 'testing') -----
+ canBrowseSubtopic
+
+ ^ true!

Item was added:
+ ----- Method: ClassBasedHelpTopic>>canBrowseTopic (in category 'testing') -----
+ canBrowseTopic
+
+ ^ true!

Item was added:
+ ----- Method: DirectoryBasedHelpTopic>>canBrowseTopic (in category 'testing') -----
+ canBrowseTopic
+
+ ^ true!

Item was added:
+ ----- Method: FileBasedHelpTopic>>canBrowseTopic (in category 'testing') -----
+ canBrowseTopic
+
+ ^ true!

Item was removed:
- ----- Method: HelpBrowser>>browseTopic (in category 'actions') -----
- browseTopic
-
- ^ (self currentTopic respondsTo: #browseTopic)
- ifTrue: [self currentTopic browseTopic]
- ifFalse: [self currentParentTopic browseSubtopic: self currentTopic]!

Item was removed:
- ----- Method: HelpBrowser>>canBrowseTopic (in category 'testing') -----
- canBrowseTopic
-
- ^ (self currentTopic respondsTo: #browseTopic)
- or: [self currentParentTopic respondsTo: #browseSubtopic:]!

Item was removed:
- ----- Method: HelpBrowser>>exploreTopic (in category 'actions') -----
- exploreTopic
-
- ^ self currentTopic explore!

Item was removed:
- ----- Method: HelpBrowser>>inspectTopic (in category 'actions') -----
- inspectTopic
-
- ^ self currentTopic inspect!

Item was changed:
----- Method: HelpBrowser>>treeKey:from:event: (in category 'menus') -----
treeKey: aChar from: aView event: anEvent

anEvent anyModifierKeyPressed ifFalse: [^ false].
+ ^ (self currentTopic topicMenuKey: aChar fromParent: self currentParentTopic)!
- aChar
- caseOf: {
- [$b] -> [self browseTopic].
- [$i] -> [self inspectTopic].
- [$I] -> [self exploreTopic]. }
- otherwise: [^ false].
- ^ true!

Item was changed:
----- Method: HelpBrowser>>treeListMenu: (in category 'menus') -----
treeListMenu: aMenu


+ ^ self currentTopic
+ ifNil: [aMenu]
+ ifNotNil: [:topic | topic
+ topicMenu: aMenu
+ parentTopic: self currentParentTopic]!
- self currentTopic ifNil: [^ aMenu].
-
- aMenu
- add: 'Inspect (i)' action: #inspectTopic;
- add: 'Explore (I)' action: #exploreTopic.
-
- self canBrowseTopic ifTrue: [
- aMenu
- addLine;
- add: 'Browse (b)' action: #browseTopic].
-
- ^ aMenu!

Item was added:
+ ----- Method: MethodListHelpTopic>>canBrowseTopic (in category 'testing') -----
+ canBrowseTopic
+
+ ^ true!

Item was added:
+ ----- Method: PackageAPIHelpTopic>>canBrowseTopic (in category 'testing') -----
+ canBrowseTopic
+
+ ^ true!





Reply | Threaded
Open this post in threaded view
|

Re: #isNilOr:, #notNilAnd:

Levente Uzonyi
In reply to this post by Christoph Thiede
Hi Christoph,

They are okay for scripts, but we know that methods like that always end
up being used out of scripts, and are even misused every now and then.
They are especially susceptible to misuse when their return value is
ignored; in which case they are both equivalent to #ifNotNil:.

I'm also not sure whether these methods belong to ProtoObject or not.
I know the other nil handling methods are there as well, but that doesn't
mean all such methods belong there.
E.g. #ifNil:/#ifNotNil: should be there because the current compiler and
VM will make those available for all objects provided the methods are
inlined. But #isNil and #notNil have no such properties, so they belong
more to Object than ProtoObject in my opinion.


Levente

On Mon, 16 Mar 2020, Thiede, Christoph wrote:

>
> Hi all,
>
>
> after the release has been managed successfully, I would like to kindly push this proposal. In the latest months, I found myself to really often desiring #isNilOr: or #notNilAnd:. How would you think about these convenience
> selectors? :-)
>
>
> Best,
>
> Christoph
>
> _________________________________________________________________________________________________________________________________________________________________________________________________________________________________
> Von: Thiede, Christoph
> Gesendet: Mittwoch, 11. Dezember 2019 11:01:44
> An: Squeak Dev
> Betreff: #isNilOr:, #notNilAnd:  
>
> Hi all, just another idea for a hypothetic extension to ProtoObject:
>
>
> ProtoObject >> #isNilOr: aBlock
>
> ^ aBlock cull: self
>
> UndefinedObject >> #isNilOr: aBlock
>
> ^ true
>
> ProtoObject >> #notNilAnd: aBlock
>
> ^ aBlock cull: self
>
> UndefinedObject >> #notNilAnd: aBlock
>
> ^ false
>
>
> Examples:
>
> parentObject notNilAnd: #canBrowseSubtopic.
>
> helpClass notNilAnd: [:hC| hC usesCodeStyling].
>
> self selectionIndex isNilOr: [:index | index > 2].
>
> anObject isNilOr: #isZero.
>
> foo isNilOr: #isNotEmpty.
>
>
> Just a bit syntactic sugar for idiomatic problems such as below. In particular, I found several possible applications for #notNilAnd: in my image.
>
>
> Go ahead and tell me why it would be a bad idea :)
>
>
> Best,
>
> Christoph
>
>
> _________________________________________________________________________________________________________________________________________________________________________________________________________________________________
> Von: Squeak-dev <[hidden email]> im Auftrag von Thiede, Christoph
> Gesendet: Freitag, 25. Oktober 2019 19:21 Uhr
> An: gettimothy via Squeak-dev
> Betreff: Re: [squeak-dev] The Inbox: HelpSystem-Core-ct.123.mcz  
>
> If you're needing a decision, I'd vote for option 3.
>
> _________________________________________________________________________________________________________________________________________________________________________________________________________________________________
> Von: Squeak-dev <[hidden email]> im Auftrag von Taeumel, Marcel
> Gesendet: Mittwoch, 16. Oktober 2019 11:14:15
> An: gettimothy via Squeak-dev
> Betreff: Re: [squeak-dev] The Inbox: HelpSystem-Core-ct.123.mcz  
> Quick suggestion on the formatting. This one:
> ^ self canBrowseTopic or: [
>    parentTopic ifNotNil: #canBrowseSubtopic ifNil: [false]]
>
> could become:
>
> ^ self canBrowseTopic or: [
>    parentTopic
>    ifNil: [false]
>    ifNotNil: [:topic | topic canBrowseSubtopic]]
>
> or:
>
> ^ self canBrowseTopic
>       or: [parentTopic notNil and: [parentTopic canBrowseSubtopic]]
>
> Hmmm... what are other opinions on this? There is no need for #ifNil/ifNotNil in such a boolean expression?
>
> Best,
> Marcel
>
>
>       Am 13.10.2019 21:04:19 schrieb [hidden email] <[hidden email]>:
>
>       A new version of HelpSystem-Core was added to project The Inbox:
>       http://source.squeak.org/inbox/HelpSystem-Core-ct.123.mcz
>
>       ==================== Summary ====================
>
>       Name: HelpSystem-Core-ct.123
>       Author: ct
>       Time: 13 October 2019, 9:04:08.373932 pm
>       UUID: dec7ceca-320f-d945-8d2a-c2f6a5e49a52
>       Ancestors: HelpSystem-Core-ct.120
>
>       Refactors HelpBrowser menu: Move menu stuff from HelpBrowser into HelpTopic hierarchy in favor of a better object design
>
>       Thanks again, Marcel :-)
>
>       =============== Diff against HelpSystem-Core-ct.120 ===============
>
>       Item was added:
>       + ----- Method: AbstractHelpTopic>>browseTopicFromParent: (in category 'tools') -----
>       + browseTopicFromParent: parentTopic
>       +
>       + self canBrowseTopic
>       + ifTrue: [^ self browseTopic].
>       + parentTopic canBrowseSubtopic
>       + ifTrue: [^ parentTopic browseSubtopic: self].
>       + !
>
>       Item was added:
>       + ----- Method: AbstractHelpTopic>>canBrowseSubtopic (in category 'testing') -----
>       + canBrowseSubtopic
>       +
>       + ^ false!
>
>       Item was added:
>       + ----- Method: AbstractHelpTopic>>canBrowseTopic (in category 'testing') -----
>       + canBrowseTopic
>       +
>       + ^ false!
>
>       Item was added:
>       + ----- Method: AbstractHelpTopic>>canBrowseTopicFromParent: (in category 'testing') -----
>       + canBrowseTopicFromParent: parentTopic
>       +
>       + ^ self canBrowseTopic or: [
>       + parentTopic ifNotNil: #canBrowseSubtopic ifNil: [false]]!
>
>       Item was added:
>       + ----- Method: AbstractHelpTopic>>topicMenu:parentTopic: (in category 'menus') -----
>       + topicMenu: aMenu parentTopic: parentTopic
>       +
>       + aMenu
>       + add: 'Inspect (i)' translated target: self action: #inspect;
>       + add: 'Explore (I)' translated target: self action: #explore.
>       + (self canBrowseTopicFromParent: parentTopic)
>       + ifTrue: [
>       + aMenu add: 'Browse (b)' translated
>       + target: self
>       + selector: #browseTopicFromParent:
>       + argumentList: {parentTopic} ].
>       +
>       + ^ aMenu!
>
>       Item was added:
>       + ----- Method: AbstractHelpTopic>>topicMenuKey:fromParent: (in category 'menus') -----
>       + topicMenuKey: aChar fromParent: parentTopic
>       +
>       + aChar
>       + caseOf: {
>       + [$b] -> [(self canBrowseTopicFromParent: parentTopic)
>       + ifTrue: [ self browseTopicFromParent: parentTopic ]].
>       + [$i] -> [self inspect].
>       + [$I] -> [self explore] }
>       + otherwise: [^ false].
>       + ^ true!
>
>       Item was added:
>       + ----- Method: ClassAPIHelpTopic>>canBrowseTopic (in category 'testing') -----
>       + canBrowseTopic
>       +
>       + ^ true!
>
>       Item was added:
>       + ----- Method: ClassBasedHelpTopic>>canBrowseSubtopic (in category 'testing') -----
>       + canBrowseSubtopic
>       +
>       + ^ true!
>
>       Item was added:
>       + ----- Method: ClassBasedHelpTopic>>canBrowseTopic (in category 'testing') -----
>       + canBrowseTopic
>       +
>       + ^ true!
>
>       Item was added:
>       + ----- Method: DirectoryBasedHelpTopic>>canBrowseTopic (in category 'testing') -----
>       + canBrowseTopic
>       +
>       + ^ true!
>
>       Item was added:
>       + ----- Method: FileBasedHelpTopic>>canBrowseTopic (in category 'testing') -----
>       + canBrowseTopic
>       +
>       + ^ true!
>
>       Item was removed:
>       - ----- Method: HelpBrowser>>browseTopic (in category 'actions') -----
>       - browseTopic
>       -
>       - ^ (self currentTopic respondsTo: #browseTopic)
>       - ifTrue: [self currentTopic browseTopic]
>       - ifFalse: [self currentParentTopic browseSubtopic: self currentTopic]!
>
>       Item was removed:
>       - ----- Method: HelpBrowser>>canBrowseTopic (in category 'testing') -----
>       - canBrowseTopic
>       -
>       - ^ (self currentTopic respondsTo: #browseTopic)
>       - or: [self currentParentTopic respondsTo: #browseSubtopic:]!
>
>       Item was removed:
>       - ----- Method: HelpBrowser>>exploreTopic (in category 'actions') -----
>       - exploreTopic
>       -
>       - ^ self currentTopic explore!
>
>       Item was removed:
>       - ----- Method: HelpBrowser>>inspectTopic (in category 'actions') -----
>       - inspectTopic
>       -
>       - ^ self currentTopic inspect!
>
>       Item was changed:
>       ----- Method: HelpBrowser>>treeKey:from:event: (in category 'menus') -----
>       treeKey: aChar from: aView event: anEvent
>
>       anEvent anyModifierKeyPressed ifFalse: [^ false].
>       + ^ (self currentTopic topicMenuKey: aChar fromParent: self currentParentTopic)!
>       - aChar
>       - caseOf: {
>       - [$b] -> [self browseTopic].
>       - [$i] -> [self inspectTopic].
>       - [$I] -> [self exploreTopic]. }
>       - otherwise: [^ false].
>       - ^ true!
>
>       Item was changed:
>       ----- Method: HelpBrowser>>treeListMenu: (in category 'menus') -----
>       treeListMenu: aMenu
>
>
>       + ^ self currentTopic
>       + ifNil: [aMenu]
>       + ifNotNil: [:topic | topic
>       + topicMenu: aMenu
>       + parentTopic: self currentParentTopic]!
>       - self currentTopic ifNil: [^ aMenu].
>       -
>       - aMenu
>       - add: 'Inspect (i)' action: #inspectTopic;
>       - add: 'Explore (I)' action: #exploreTopic.
>       -
>       - self canBrowseTopic ifTrue: [
>       - aMenu
>       - addLine;
>       - add: 'Browse (b)' action: #browseTopic].
>       -
>       - ^ aMenu!
>
>       Item was added:
>       + ----- Method: MethodListHelpTopic>>canBrowseTopic (in category 'testing') -----
>       + canBrowseTopic
>       +
>       + ^ true!
>
>       Item was added:
>       + ----- Method: PackageAPIHelpTopic>>canBrowseTopic (in category 'testing') -----
>       + canBrowseTopic
>       +
>       + ^ true!
>
>
>
>

Reply | Threaded
Open this post in threaded view
|

Re: #isNilOr:, #notNilAnd:

Christoph Thiede

Hi all,


I don't see why you would not like to see #isNilOr: or #notNilAnd: in "production" code? I think their names are self-explaining.


(xyz := ...) notNil and: [xyz ...]

To me, this reads rather ugly. Assignment expressions are always a bit counter-intuitive as multiple things happen at a time. They're fine in order to optimize *really* performance-critical things, but I always try to avoid them in favor of overall readability.
Furthermore, I see a big advantage in avoiding temps wherever you can use block args: It helps you to minimize the scope of variables that are only useful to be accessed in a certain area. It just increases the defect potential if you have a not-very-short method like this:

someNotVeryShortMethod
| foo graffle bar baz |
foo := self someOperation.
graffle := foo asBarCompatibleThing.
bar := self someOtherOperation.
((baz := bar property) notNil and: [baz meetsConditionOne or: [baz meetsConditionTwo]])
    ifFalse: [^ foo].
^ foo mixWith: baz "Hm, do I need bar or baz here? Actually I meant bar, but that overcrowed temp list confused me so I just produced a bug"

Compared to this, a minimized scope can make things clearer:

someNotVeryShortMethod
| foo graffle bar |
foo := self someOperation.
graffle := foo convertForBarCompatibility.
bar := self someOtherOperation.
(bar property notNilAnd: [:baz | meetsConditionOne or: [baz meetsConditionTwo]])
    ifFalse: [^ foo].
^ foo mixWith: bar "Ah, baz is not known as this place and I got a compiler error. So this must be bar!"

Some other thing I see quite often is the following:

checkArg: arg
^ arg simpleProperty notNil and: [
    self basicCheckArg: arg simpleProperty]

While this is fine for most cases, this makes it harder to implement #simpleProperty as an expensive or non-side-effect-free operation. Thus it would be better to write the following instead:

checkArg: arg
property := arg simpleProperty.
^ property notNil and: [
    self basicCheckArg: property]

But programmers are lazy, so they often don't introduce an extra tempvar ... Instead, any protocol that supplies arguments to the caller encourages you to use these arguments:

checkArg: arg
^ arg simpleProperty notNilAnd: [:property |
    self basicCheckArg: property]

Plus, it's just shorter :-)

They are especially susceptible to misuse when their return value is ignored; in which case they are both equivalent to #ifNotNil:.

What return value are you talking about? Did you mean the block argument?

Have you had a look at Java 8's Optional class? Nil can receive messages in Smalltalk, which Java null can't. Is nil therefore a good empty Optional or should these concepts be separate?

Are you talking about some kind of automatic null object that ignores every message? Or do you complain that "nil handles: #foo" does not raise an error, just for example? :-)

Best,
Christoph


Von: Squeak-dev <[hidden email]> im Auftrag von Levente Uzonyi <[hidden email]>
Gesendet: Samstag, 21. März 2020 20:25 Uhr
An: The general-purpose Squeak developers list
Betreff: Re: [squeak-dev] #isNilOr:, #notNilAnd:
 
Hi Christoph,

They are okay for scripts, but we know that methods like that always end
up being used out of scripts, and are even misused every now and then.
They are especially susceptible to misuse when their return value is
ignored; in which case they are both equivalent to #ifNotNil:.

I'm also not sure whether these methods belong to ProtoObject or not.
I know the other nil handling methods are there as well, but that doesn't
mean all such methods belong there.
E.g. #ifNil:/#ifNotNil: should be there because the current compiler and
VM will make those available for all objects provided the methods are
inlined. But #isNil and #notNil have no such properties, so they belong
more to Object than ProtoObject in my opinion.


Levente

On Mon, 16 Mar 2020, Thiede, Christoph wrote:

>
> Hi all,
>
>
> after the release has been managed successfully, I would like to kindly push this proposal. In the latest months, I found myself to really often desiring #isNilOr: or #notNilAnd:. How would you think about these convenience
> selectors? :-)
>
>
> Best,
>
> Christoph
>
> _________________________________________________________________________________________________________________________________________________________________________________________________________________________________
> Von: Thiede, Christoph
> Gesendet: Mittwoch, 11. Dezember 2019 11:01:44
> An: Squeak Dev
> Betreff: #isNilOr:, #notNilAnd:  
>
> Hi all, just another idea for a hypothetic extension to ProtoObject:
>
>
> ProtoObject >> #isNilOr: aBlock
>
> ^ aBlock cull: self
>
> UndefinedObject >> #isNilOr: aBlock
>
> ^ true
>
> ProtoObject >> #notNilAnd: aBlock
>
> ^ aBlock cull: self
>
> UndefinedObject >> #notNilAnd: aBlock
>
> ^ false
>
>
> Examples:
>
> parentObject notNilAnd: #canBrowseSubtopic.
>
> helpClass notNilAnd: [:hC| hC usesCodeStyling].
>
> self selectionIndex isNilOr: [:index | index > 2].
>
> anObject isNilOr: #isZero.
>
> foo isNilOr: #isNotEmpty.
>
>
> Just a bit syntactic sugar for idiomatic problems such as below. In particular, I found several possible applications for #notNilAnd: in my image.
>
>
> Go ahead and tell me why it would be a bad idea :)
>
>
> Best,
>
> Christoph
>
>
> _________________________________________________________________________________________________________________________________________________________________________________________________________________________________
> Von: Squeak-dev <[hidden email]> im Auftrag von Thiede, Christoph
> Gesendet: Freitag, 25. Oktober 2019 19:21 Uhr
> An: gettimothy via Squeak-dev
> Betreff: Re: [squeak-dev] The Inbox: HelpSystem-Core-ct.123.mcz  
>
> If you're needing a decision, I'd vote for option 3.
>
> _________________________________________________________________________________________________________________________________________________________________________________________________________________________________
> Von: Squeak-dev <[hidden email]> im Auftrag von Taeumel, Marcel
> Gesendet: Mittwoch, 16. Oktober 2019 11:14:15
> An: gettimothy via Squeak-dev
> Betreff: Re: [squeak-dev] The Inbox: HelpSystem-Core-ct.123.mcz  
> Quick suggestion on the formatting. This one:
> ^ self canBrowseTopic or: [
>    parentTopic ifNotNil: #canBrowseSubtopic ifNil: [false]]
>
> could become:
>
> ^ self canBrowseTopic or: [
>    parentTopic
>    ifNil: [false]
>    ifNotNil: [:topic | topic canBrowseSubtopic]]
>
> or:
>
> ^ self canBrowseTopic
>       or: [parentTopic notNil and: [parentTopic canBrowseSubtopic]]
>
> Hmmm... what are other opinions on this? There is no need for #ifNil/ifNotNil in such a boolean expression?
>
> Best,
> Marcel
>
>
>       Am 13.10.2019 21:04:19 schrieb [hidden email] <[hidden email]>:
>
>       A new version of HelpSystem-Core was added to project The Inbox:
>       http://source.squeak.org/inbox/HelpSystem-Core-ct.123.mcz
>
>       ==================== Summary ====================
>
>       Name: HelpSystem-Core-ct.123
>       Author: ct
>       Time: 13 October 2019, 9:04:08.373932 pm
>       UUID: dec7ceca-320f-d945-8d2a-c2f6a5e49a52
>       Ancestors: HelpSystem-Core-ct.120
>
>       Refactors HelpBrowser menu: Move menu stuff from HelpBrowser into HelpTopic hierarchy in favor of a better object design
>
>       Thanks again, Marcel :-)
>
>       =============== Diff against HelpSystem-Core-ct.120 ===============
>
>       Item was added:
>       + ----- Method: AbstractHelpTopic>>browseTopicFromParent: (in category 'tools') -----
>       + browseTopicFromParent: parentTopic
>       +
>       + self canBrowseTopic
>       + ifTrue: [^ self browseTopic].
>       + parentTopic canBrowseSubtopic
>       + ifTrue: [^ parentTopic browseSubtopic: self].
>       + !
>
>       Item was added:
>       + ----- Method: AbstractHelpTopic>>canBrowseSubtopic (in category 'testing') -----
>       + canBrowseSubtopic
>       +
>       + ^ false!
>
>       Item was added:
>       + ----- Method: AbstractHelpTopic>>canBrowseTopic (in category 'testing') -----
>       + canBrowseTopic
>       +
>       + ^ false!
>
>       Item was added:
>       + ----- Method: AbstractHelpTopic>>canBrowseTopicFromParent: (in category 'testing') -----
>       + canBrowseTopicFromParent: parentTopic
>       +
>       + ^ self canBrowseTopic or: [
>       + parentTopic ifNotNil: #canBrowseSubtopic ifNil: [false]]!
>
>       Item was added:
>       + ----- Method: AbstractHelpTopic>>topicMenu:parentTopic: (in category 'menus') -----
>       + topicMenu: aMenu parentTopic: parentTopic
>       +
>       + aMenu
>       + add: 'Inspect (i)' translated target: self action: #inspect;
>       + add: 'Explore (I)' translated target: self action: #explore.
>       + (self canBrowseTopicFromParent: parentTopic)
>       + ifTrue: [
>       + aMenu add: 'Browse (b)' translated
>       + target: self
>       + selector: #browseTopicFromParent:
>       + argumentList: {parentTopic} ].
>       +
>       + ^ aMenu!
>
>       Item was added:
>       + ----- Method: AbstractHelpTopic>>topicMenuKey:fromParent: (in category 'menus') -----
>       + topicMenuKey: aChar fromParent: parentTopic
>       +
>       + aChar
>       + caseOf: {
>       + [$b] -> [(self canBrowseTopicFromParent: parentTopic)
>       + ifTrue: [ self browseTopicFromParent: parentTopic ]].
>       + [$i] -> [self inspect].
>       + [$I] -> [self explore] }
>       + otherwise: [^ false].
>       + ^ true!
>
>       Item was added:
>       + ----- Method: ClassAPIHelpTopic>>canBrowseTopic (in category 'testing') -----
>       + canBrowseTopic
>       +
>       + ^ true!
>
>       Item was added:
>       + ----- Method: ClassBasedHelpTopic>>canBrowseSubtopic (in category 'testing') -----
>       + canBrowseSubtopic
>       +
>       + ^ true!
>
>       Item was added:
>       + ----- Method: ClassBasedHelpTopic>>canBrowseTopic (in category 'testing') -----
>       + canBrowseTopic
>       +
>       + ^ true!
>
>       Item was added:
>       + ----- Method: DirectoryBasedHelpTopic>>canBrowseTopic (in category 'testing') -----
>       + canBrowseTopic
>       +
>       + ^ true!
>
>       Item was added:
>       + ----- Method: FileBasedHelpTopic>>canBrowseTopic (in category 'testing') -----
>       + canBrowseTopic
>       +
>       + ^ true!
>
>       Item was removed:
>       - ----- Method: HelpBrowser>>browseTopic (in category 'actions') -----
>       - browseTopic
>       -
>       - ^ (self currentTopic respondsTo: #browseTopic)
>       - ifTrue: [self currentTopic browseTopic]
>       - ifFalse: [self currentParentTopic browseSubtopic: self currentTopic]!
>
>       Item was removed:
>       - ----- Method: HelpBrowser>>canBrowseTopic (in category 'testing') -----
>       - canBrowseTopic
>       -
>       - ^ (self currentTopic respondsTo: #browseTopic)
>       - or: [self currentParentTopic respondsTo: #browseSubtopic:]!
>
>       Item was removed:
>       - ----- Method: HelpBrowser>>exploreTopic (in category 'actions') -----
>       - exploreTopic
>       -
>       - ^ self currentTopic explore!
>
>       Item was removed:
>       - ----- Method: HelpBrowser>>inspectTopic (in category 'actions') -----
>       - inspectTopic
>       -
>       - ^ self currentTopic inspect!
>
>       Item was changed:
>       ----- Method: HelpBrowser>>treeKey:from:event: (in category 'menus') -----
>       treeKey: aChar from: aView event: anEvent
>
>       anEvent anyModifierKeyPressed ifFalse: [^ false].
>       + ^ (self currentTopic topicMenuKey: aChar fromParent: self currentParentTopic)!
>       - aChar
>       - caseOf: {
>       - [$b] -> [self browseTopic].
>       - [$i] -> [self inspectTopic].
>       - [$I] -> [self exploreTopic]. }
>       - otherwise: [^ false].
>       - ^ true!
>
>       Item was changed:
>       ----- Method: HelpBrowser>>treeListMenu: (in category 'menus') -----
>       treeListMenu: aMenu
>
>
>       + ^ self currentTopic
>       + ifNil: [aMenu]
>       + ifNotNil: [:topic | topic
>       + topicMenu: aMenu
>       + parentTopic: self currentParentTopic]!
>       - self currentTopic ifNil: [^ aMenu].
>       -
>       - aMenu
>       - add: 'Inspect (i)' action: #inspectTopic;
>       - add: 'Explore (I)' action: #exploreTopic.
>       -
>       - self canBrowseTopic ifTrue: [
>       - aMenu
>       - addLine;
>       - add: 'Browse (b)' action: #browseTopic].
>       -
>       - ^ aMenu!
>
>       Item was added:
>       + ----- Method: MethodListHelpTopic>>canBrowseTopic (in category 'testing') -----
>       + canBrowseTopic
>       +
>       + ^ true!
>
>       Item was added:
>       + ----- Method: PackageAPIHelpTopic>>canBrowseTopic (in category 'testing') -----
>       + canBrowseTopic
>       +
>       + ^ true!
>
>
>
>


Reply | Threaded
Open this post in threaded view
|

ProtoObject (was: #isNilOr:, #notNilAnd:)

Christoph Thiede
In reply to this post by Levente Uzonyi

(Forking this thread due to difference of concerns ... :-))


> I'm also not sure whether these methods belong to ProtoObject or not.

This is an interesting problem, see also the recent thread about cleaning up ProtoObject. I agree with Eliot that a good ProtoObject is as small and stupid as possible in order to be really useful for writing powerful proxies or decorators. It should be possible to wrap nil into an ObjectTracer, for example! To put the issue in a nutshell, try out the following:

newNil := UndefinedObject basicNew.

nilTracer := ObjectTracer on: newNil.

nilTracer isNil. "false"

nilTracer perform: #isNil. "true"


Here's a similar issue:

specialClass := Object newSubclass.
specialClass compile: 'class ^42'.
specialObj := specialClass new.
specialObj class. "Object1"
specialObj perform: #class. "42"

(By the way, we are not even consistent with ourself in this concern:

([specialObj class] newProcess runUntil: #willReturn) suspendedContext top. "42"


Pooh, that's both really not nice ... Basically, inlining of #isNil & Co. is an optimization and IMHO it should not affect the basic concept of how messages are dispatched. Too less inlining affects performance, but too much inlining introduces conceptional errors.
Would it be a big performance impact if we made inlining optional? Here is a short proposal:

We could tell every bytecode interpreter to check for any receiver if it agrees on being deprived of messages that can be inlined. We could decide this per receiver class and cache these answers in the VM. Hypothetical example:

Object >> #acceptsInlining

    "This message is never sent, but it's existance signalizes the VM that it is okay to inline popular messages such as #isNil or #ifTrue:."


If this concept is too expensive or too confusing, we could also add an instance variable to the class object. Anyway, something like this. That way we could make the concept of inlining optional. If an interpreter evaluated {nilTracer isNil}, it would have to do the following (I'm following the simulation code in #interpretNextSistaV1InstructionFor:. Disclaimer: Absolutely free of optimizations!)

...
"short sends"
(div16 = 6 and: [rcvr respondsTo: #acceptsInlining]) ifTrue:
[^client
send: (Smalltalk specialSelectorAt: offset + 1)
super: false
numArgs: (Smalltalk specialNargsAt: offset + 1)].
...
"otherwise, message will be dispatched regularly"

Some similar checks would be required in case of primitives that are generated for inlined methods, such as 257f.:

...
((primIndex := meth primitive) > 0 and: [
        (self isInliningPrimitive: primitive) not or: [rcvr respondsTo: #acceptsInlining]])
ifTrue:
    [val := self doPrimitive: primIndex method: meth receiver: rcvr args: arguments.
    (self isPrimFailToken: val) ifFalse:
        [^val]]
...
"otherwise, message will be dispatched regularly"

How would you think of this concept in general? I'm excited to hear your feedback! :-)

Best,
Christoph



Von: Squeak-dev <[hidden email]> im Auftrag von Levente Uzonyi <[hidden email]>
Gesendet: Samstag, 21. März 2020 20:25 Uhr
An: The general-purpose Squeak developers list
Betreff: Re: [squeak-dev] #isNilOr:, #notNilAnd:
 
Hi Christoph,

They are okay for scripts, but we know that methods like that always end
up being used out of scripts, and are even misused every now and then.
They are especially susceptible to misuse when their return value is
ignored; in which case they are both equivalent to #ifNotNil:.

I'm also not sure whether these methods belong to ProtoObject or not.
I know the other nil handling methods are there as well, but that doesn't
mean all such methods belong there.
E.g. #ifNil:/#ifNotNil: should be there because the current compiler and
VM will make those available for all objects provided the methods are
inlined. But #isNil and #notNil have no such properties, so they belong
more to Object than ProtoObject in my opinion.


Levente

On Mon, 16 Mar 2020, Thiede, Christoph wrote:

>
> Hi all,
>
>
> after the release has been managed successfully, I would like to kindly push this proposal. In the latest months, I found myself to really often desiring #isNilOr: or #notNilAnd:. How would you think about these convenience
> selectors? :-)
>
>
> Best,
>
> Christoph
>
> _________________________________________________________________________________________________________________________________________________________________________________________________________________________________
> Von: Thiede, Christoph
> Gesendet: Mittwoch, 11. Dezember 2019 11:01:44
> An: Squeak Dev
> Betreff: #isNilOr:, #notNilAnd:  
>
> Hi all, just another idea for a hypothetic extension to ProtoObject:
>
>
> ProtoObject >> #isNilOr: aBlock
>
> ^ aBlock cull: self
>
> UndefinedObject >> #isNilOr: aBlock
>
> ^ true
>
> ProtoObject >> #notNilAnd: aBlock
>
> ^ aBlock cull: self
>
> UndefinedObject >> #notNilAnd: aBlock
>
> ^ false
>
>
> Examples:
>
> parentObject notNilAnd: #canBrowseSubtopic.
>
> helpClass notNilAnd: [:hC| hC usesCodeStyling].
>
> self selectionIndex isNilOr: [:index | index > 2].
>
> anObject isNilOr: #isZero.
>
> foo isNilOr: #isNotEmpty.
>
>
> Just a bit syntactic sugar for idiomatic problems such as below. In particular, I found several possible applications for #notNilAnd: in my image.
>
>
> Go ahead and tell me why it would be a bad idea :)
>
>
> Best,
>
> Christoph
>
>
> _________________________________________________________________________________________________________________________________________________________________________________________________________________________________
> Von: Squeak-dev <[hidden email]> im Auftrag von Thiede, Christoph
> Gesendet: Freitag, 25. Oktober 2019 19:21 Uhr
> An: gettimothy via Squeak-dev
> Betreff: Re: [squeak-dev] The Inbox: HelpSystem-Core-ct.123.mcz  
>
> If you're needing a decision, I'd vote for option 3.
>
> _________________________________________________________________________________________________________________________________________________________________________________________________________________________________
> Von: Squeak-dev <[hidden email]> im Auftrag von Taeumel, Marcel
> Gesendet: Mittwoch, 16. Oktober 2019 11:14:15
> An: gettimothy via Squeak-dev
> Betreff: Re: [squeak-dev] The Inbox: HelpSystem-Core-ct.123.mcz  
> Quick suggestion on the formatting. This one:
> ^ self canBrowseTopic or: [
>    parentTopic ifNotNil: #canBrowseSubtopic ifNil: [false]]
>
> could become:
>
> ^ self canBrowseTopic or: [
>    parentTopic
>    ifNil: [false]
>    ifNotNil: [:topic | topic canBrowseSubtopic]]
>
> or:
>
> ^ self canBrowseTopic
>       or: [parentTopic notNil and: [parentTopic canBrowseSubtopic]]
>
> Hmmm... what are other opinions on this? There is no need for #ifNil/ifNotNil in such a boolean expression?
>
> Best,
> Marcel
>
>
>       Am 13.10.2019 21:04:19 schrieb [hidden email] <[hidden email]>:
>
>       A new version of HelpSystem-Core was added to project The Inbox:
>       http://source.squeak.org/inbox/HelpSystem-Core-ct.123.mcz
>
>       ==================== Summary ====================
>
>       Name: HelpSystem-Core-ct.123
>       Author: ct
>       Time: 13 October 2019, 9:04:08.373932 pm
>       UUID: dec7ceca-320f-d945-8d2a-c2f6a5e49a52
>       Ancestors: HelpSystem-Core-ct.120
>
>       Refactors HelpBrowser menu: Move menu stuff from HelpBrowser into HelpTopic hierarchy in favor of a better object design
>
>       Thanks again, Marcel :-)
>
>       =============== Diff against HelpSystem-Core-ct.120 ===============
>
>       Item was added:
>       + ----- Method: AbstractHelpTopic>>browseTopicFromParent: (in category 'tools') -----
>       + browseTopicFromParent: parentTopic
>       +
>       + self canBrowseTopic
>       + ifTrue: [^ self browseTopic].
>       + parentTopic canBrowseSubtopic
>       + ifTrue: [^ parentTopic browseSubtopic: self].
>       + !
>
>       Item was added:
>       + ----- Method: AbstractHelpTopic>>canBrowseSubtopic (in category 'testing') -----
>       + canBrowseSubtopic
>       +
>       + ^ false!
>
>       Item was added:
>       + ----- Method: AbstractHelpTopic>>canBrowseTopic (in category 'testing') -----
>       + canBrowseTopic
>       +
>       + ^ false!
>
>       Item was added:
>       + ----- Method: AbstractHelpTopic>>canBrowseTopicFromParent: (in category 'testing') -----
>       + canBrowseTopicFromParent: parentTopic
>       +
>       + ^ self canBrowseTopic or: [
>       + parentTopic ifNotNil: #canBrowseSubtopic ifNil: [false]]!
>
>       Item was added:
>       + ----- Method: AbstractHelpTopic>>topicMenu:parentTopic: (in category 'menus') -----
>       + topicMenu: aMenu parentTopic: parentTopic
>       +
>       + aMenu
>       + add: 'Inspect (i)' translated target: self action: #inspect;
>       + add: 'Explore (I)' translated target: self action: #explore.
>       + (self canBrowseTopicFromParent: parentTopic)
>       + ifTrue: [
>       + aMenu add: 'Browse (b)' translated
>       + target: self
>       + selector: #browseTopicFromParent:
>       + argumentList: {parentTopic} ].
>       +
>       + ^ aMenu!
>
>       Item was added:
>       + ----- Method: AbstractHelpTopic>>topicMenuKey:fromParent: (in category 'menus') -----
>       + topicMenuKey: aChar fromParent: parentTopic
>       +
>       + aChar
>       + caseOf: {
>       + [$b] -> [(self canBrowseTopicFromParent: parentTopic)
>       + ifTrue: [ self browseTopicFromParent: parentTopic ]].
>       + [$i] -> [self inspect].
>       + [$I] -> [self explore] }
>       + otherwise: [^ false].
>       + ^ true!
>
>       Item was added:
>       + ----- Method: ClassAPIHelpTopic>>canBrowseTopic (in category 'testing') -----
>       + canBrowseTopic
>       +
>       + ^ true!
>
>       Item was added:
>       + ----- Method: ClassBasedHelpTopic>>canBrowseSubtopic (in category 'testing') -----
>       + canBrowseSubtopic
>       +
>       + ^ true!
>
>       Item was added:
>       + ----- Method: ClassBasedHelpTopic>>canBrowseTopic (in category 'testing') -----
>       + canBrowseTopic
>       +
>       + ^ true!
>
>       Item was added:
>       + ----- Method: DirectoryBasedHelpTopic>>canBrowseTopic (in category 'testing') -----
>       + canBrowseTopic
>       +
>       + ^ true!
>
>       Item was added:
>       + ----- Method: FileBasedHelpTopic>>canBrowseTopic (in category 'testing') -----
>       + canBrowseTopic
>       +
>       + ^ true!
>
>       Item was removed:
>       - ----- Method: HelpBrowser>>browseTopic (in category 'actions') -----
>       - browseTopic
>       -
>       - ^ (self currentTopic respondsTo: #browseTopic)
>       - ifTrue: [self currentTopic browseTopic]
>       - ifFalse: [self currentParentTopic browseSubtopic: self currentTopic]!
>
>       Item was removed:
>       - ----- Method: HelpBrowser>>canBrowseTopic (in category 'testing') -----
>       - canBrowseTopic
>       -
>       - ^ (self currentTopic respondsTo: #browseTopic)
>       - or: [self currentParentTopic respondsTo: #browseSubtopic:]!
>
>       Item was removed:
>       - ----- Method: HelpBrowser>>exploreTopic (in category 'actions') -----
>       - exploreTopic
>       -
>       - ^ self currentTopic explore!
>
>       Item was removed:
>       - ----- Method: HelpBrowser>>inspectTopic (in category 'actions') -----
>       - inspectTopic
>       -
>       - ^ self currentTopic inspect!
>
>       Item was changed:
>       ----- Method: HelpBrowser>>treeKey:from:event: (in category 'menus') -----
>       treeKey: aChar from: aView event: anEvent
>
>       anEvent anyModifierKeyPressed ifFalse: [^ false].
>       + ^ (self currentTopic topicMenuKey: aChar fromParent: self currentParentTopic)!
>       - aChar
>       - caseOf: {
>       - [$b] -> [self browseTopic].
>       - [$i] -> [self inspectTopic].
>       - [$I] -> [self exploreTopic]. }
>       - otherwise: [^ false].
>       - ^ true!
>
>       Item was changed:
>       ----- Method: HelpBrowser>>treeListMenu: (in category 'menus') -----
>       treeListMenu: aMenu
>
>
>       + ^ self currentTopic
>       + ifNil: [aMenu]
>       + ifNotNil: [:topic | topic
>       + topicMenu: aMenu
>       + parentTopic: self currentParentTopic]!
>       - self currentTopic ifNil: [^ aMenu].
>       -
>       - aMenu
>       - add: 'Inspect (i)' action: #inspectTopic;
>       - add: 'Explore (I)' action: #exploreTopic.
>       -
>       - self canBrowseTopic ifTrue: [
>       - aMenu
>       - addLine;
>       - add: 'Browse (b)' action: #browseTopic].
>       -
>       - ^ aMenu!
>
>       Item was added:
>       + ----- Method: MethodListHelpTopic>>canBrowseTopic (in category 'testing') -----
>       + canBrowseTopic
>       +
>       + ^ true!
>
>       Item was added:
>       + ----- Method: PackageAPIHelpTopic>>canBrowseTopic (in category 'testing') -----
>       + canBrowseTopic
>       +
>       + ^ true!
>
>
>
>


Reply | Threaded
Open this post in threaded view
|

Re: #isNilOr:, #notNilAnd:

Levente Uzonyi
In reply to this post by Christoph Thiede
Hi Christoph,

On Sat, 21 Mar 2020, Thiede, Christoph wrote:

>
> Hi all,
>
>
> I don't see why you would not like to see #isNilOr: or #notNilAnd: in "production" code? I think their names are self-explaining.

I agree about the names. I base my opinion on the fact that I never felt
the need for such methods, though the pattern are fairly common. But, it's
just my opinion.

>
>
> > (xyz := ...) notNil and: [xyz ...]
>
> To me, this reads rather ugly. Assignment expressions are always a bit counter-intuitive as multiple things happen at a time. They're fine in order to optimize *really* performance-critical things, but I always try to avoid
> them in favor of overall readability.
> Furthermore, I see a big advantage in avoiding temps wherever you can use block args: It helps you to minimize the scope of variables that are only useful to be accessed in a certain area. It just increases the defect
> potential if you have a not-very-short method like this:
>
>       someNotVeryShortMethod
> | foo graffle bar baz |
> foo := self someOperation.
> graffle := foo asBarCompatibleThing.
> bar := self someOtherOperation.
> ((baz := bar property) notNil and: [baz meetsConditionOne or: [baz meetsConditionTwo]])
>     ifFalse: [^ foo].
> ^ foo mixWith: baz "Hm, do I need bar or baz here? Actually I meant bar, but that overcrowed temp list confused me so I just produced a bug"
>
>
> Compared to this, a minimized scope can make things clearer:
>
>       someNotVeryShortMethod
> | foo graffle bar |
> foo := self someOperation.
> graffle := foo convertForBarCompatibility.
> bar := self someOtherOperation.
> (bar property notNilAnd: [:baz | meetsConditionOne or: [baz meetsConditionTwo]])
>     ifFalse: [^ foo].
> ^ foo mixWith: bar "Ah, baz is not known as this place and I got a compiler error. So this must be bar!"
>
>
> Some other thing I see quite often is the following:
>
>       checkArg: arg
> ^ arg simpleProperty notNil and: [
>     self basicCheckArg: arg simpleProperty]
>
>
> While this is fine for most cases, this makes it harder to implement #simpleProperty as an expensive or non-side-effect-free operation. Thus it would be better to write the following instead:
>
>       checkArg: arg
> property := arg simpleProperty.
> ^ property notNil and: [
>     self basicCheckArg: property]
>
>
> But programmers are lazy, so they often don't introduce an extra tempvar ... Instead, any protocol that supplies arguments to the caller encourages you to use these arguments:
>
>       checkArg: arg
> ^ arg simpleProperty notNilAnd: [:property |
>     self basicCheckArg: property]
>
>
> Plus, it's just shorter :-)
>
> > They are especially susceptible to misuse when their return value is ignored; in which case they are both equivalent to #ifNotNil:.
>
> What return value are you talking about? Did you mean the block argument?
Both #isNilOr: and #notNilAnd: return a boolean value. It is possible to
use these methods and ignore their return values. E.g.:

object ifNotNil: [ collection add: object ].

can be written as:

object isNilOr: [ collection add: object ].

or as:

object notNilAnd: [ collection add: object ].


Levente

>
> > Have you had a look at Java 8's Optional class? Nil can receive messages in Smalltalk, which Java null can't. Is nil therefore a good empty Optional or should these concepts be separate?
> Are you talking about some kind of automatic null object that ignores every message? Or do you complain that "nil handles: #foo" does not raise an error, just for example? :-)
>
> Best,
> Christoph
>
> _________________________________________________________________________________________________________________________________________________________________________________________________________________________________
> Von: Squeak-dev <[hidden email]> im Auftrag von Levente Uzonyi <[hidden email]>
> Gesendet: Samstag, 21. März 2020 20:25 Uhr
> An: The general-purpose Squeak developers list
> Betreff: Re: [squeak-dev] #isNilOr:, #notNilAnd:  
> Hi Christoph,
>
> They are okay for scripts, but we know that methods like that always end
> up being used out of scripts, and are even misused every now and then.
> They are especially susceptible to misuse when their return value is
> ignored; in which case they are both equivalent to #ifNotNil:.
>
> I'm also not sure whether these methods belong to ProtoObject or not.
> I know the other nil handling methods are there as well, but that doesn't
> mean all such methods belong there.
> E.g. #ifNil:/#ifNotNil: should be there because the current compiler and
> VM will make those available for all objects provided the methods are
> inlined. But #isNil and #notNil have no such properties, so they belong
> more to Object than ProtoObject in my opinion.
>
>
> Levente
>
> On Mon, 16 Mar 2020, Thiede, Christoph wrote:
>
> >
> > Hi all,
> >
> >
> > after the release has been managed successfully, I would like to kindly push this proposal. In the latest months, I found myself to really often desiring #isNilOr: or #notNilAnd:. How would you think about these convenience
> > selectors? :-)
> >
> >
> > Best,
> >
> > Christoph
> >
> >________________________________________________________________________________________________________________________________________________________________________________________________________________________________
> _
> > Von: Thiede, Christoph
> > Gesendet: Mittwoch, 11. Dezember 2019 11:01:44
> > An: Squeak Dev
> > Betreff: #isNilOr:, #notNilAnd:  
> >
> > Hi all, just another idea for a hypothetic extension to ProtoObject:
> >
> >
> > ProtoObject >> #isNilOr: aBlock
> >
> > ^ aBlock cull: self
> >
> > UndefinedObject >> #isNilOr: aBlock
> >
> > ^ true
> >
> > ProtoObject >> #notNilAnd: aBlock
> >
> > ^ aBlock cull: self
> >
> > UndefinedObject >> #notNilAnd: aBlock
> >
> > ^ false
> >
> >
> > Examples:
> >
> > parentObject notNilAnd: #canBrowseSubtopic.
> >
> > helpClass notNilAnd: [:hC| hC usesCodeStyling].
> >
> > self selectionIndex isNilOr: [:index | index > 2].
> >
> > anObject isNilOr: #isZero.
> >
> > foo isNilOr: #isNotEmpty.
> >
> >
> > Just a bit syntactic sugar for idiomatic problems such as below. In particular, I found several possible applications for #notNilAnd: in my image.
> >
> >
> > Go ahead and tell me why it would be a bad idea :)
> >
> >
> > Best,
> >
> > Christoph
> >
> >
> >________________________________________________________________________________________________________________________________________________________________________________________________________________________________
> _
> > Von: Squeak-dev <[hidden email]> im Auftrag von Thiede, Christoph
> > Gesendet: Freitag, 25. Oktober 2019 19:21 Uhr
> > An: gettimothy via Squeak-dev
> > Betreff: Re: [squeak-dev] The Inbox: HelpSystem-Core-ct.123.mcz  
> >
> > If you're needing a decision, I'd vote for option 3.
> >
> >________________________________________________________________________________________________________________________________________________________________________________________________________________________________
> _
> > Von: Squeak-dev <[hidden email]> im Auftrag von Taeumel, Marcel
> > Gesendet: Mittwoch, 16. Oktober 2019 11:14:15
> > An: gettimothy via Squeak-dev
> > Betreff: Re: [squeak-dev] The Inbox: HelpSystem-Core-ct.123.mcz  
> > Quick suggestion on the formatting. This one:
> > ^ self canBrowseTopic or: [
> >    parentTopic ifNotNil: #canBrowseSubtopic ifNil: [false]]
> >
> > could become:
> >
> > ^ self canBrowseTopic or: [
> >    parentTopic
> >    ifNil: [false]
> >    ifNotNil: [:topic | topic canBrowseSubtopic]]
> >
> > or:
> >
> > ^ self canBrowseTopic
> >       or: [parentTopic notNil and: [parentTopic canBrowseSubtopic]]
> >
> > Hmmm... what are other opinions on this? There is no need for #ifNil/ifNotNil in such a boolean expression?
> >
> > Best,
> > Marcel
> >
> >
> >       Am 13.10.2019 21:04:19 schrieb [hidden email] <[hidden email]>:
> >
> >       A new version of HelpSystem-Core was added to project The Inbox:
> >       http://source.squeak.org/inbox/HelpSystem-Core-ct.123.mcz
> >
> >       ==================== Summary ====================
> >
> >       Name: HelpSystem-Core-ct.123
> >       Author: ct
> >       Time: 13 October 2019, 9:04:08.373932 pm
> >       UUID: dec7ceca-320f-d945-8d2a-c2f6a5e49a52
> >       Ancestors: HelpSystem-Core-ct.120
> >
> >       Refactors HelpBrowser menu: Move menu stuff from HelpBrowser into HelpTopic hierarchy in favor of a better object design
> >
> >       Thanks again, Marcel :-)
> >
> >       =============== Diff against HelpSystem-Core-ct.120 ===============
> >
> >       Item was added:
> >       + ----- Method: AbstractHelpTopic>>browseTopicFromParent: (in category 'tools') -----
> >       + browseTopicFromParent: parentTopic
> >       +
> >       + self canBrowseTopic
> >       + ifTrue: [^ self browseTopic].
> >       + parentTopic canBrowseSubtopic
> >       + ifTrue: [^ parentTopic browseSubtopic: self].
> >       + !
> >
> >       Item was added:
> >       + ----- Method: AbstractHelpTopic>>canBrowseSubtopic (in category 'testing') -----
> >       + canBrowseSubtopic
> >       +
> >       + ^ false!
> >
> >       Item was added:
> >       + ----- Method: AbstractHelpTopic>>canBrowseTopic (in category 'testing') -----
> >       + canBrowseTopic
> >       +
> >       + ^ false!
> >
> >       Item was added:
> >       + ----- Method: AbstractHelpTopic>>canBrowseTopicFromParent: (in category 'testing') -----
> >       + canBrowseTopicFromParent: parentTopic
> >       +
> >       + ^ self canBrowseTopic or: [
> >       + parentTopic ifNotNil: #canBrowseSubtopic ifNil: [false]]!
> >
> >       Item was added:
> >       + ----- Method: AbstractHelpTopic>>topicMenu:parentTopic: (in category 'menus') -----
> >       + topicMenu: aMenu parentTopic: parentTopic
> >       +
> >       + aMenu
> >       + add: 'Inspect (i)' translated target: self action: #inspect;
> >       + add: 'Explore (I)' translated target: self action: #explore.
> >       + (self canBrowseTopicFromParent: parentTopic)
> >       + ifTrue: [
> >       + aMenu add: 'Browse (b)' translated
> >       + target: self
> >       + selector: #browseTopicFromParent:
> >       + argumentList: {parentTopic} ].
> >       +
> >       + ^ aMenu!
> >
> >       Item was added:
> >       + ----- Method: AbstractHelpTopic>>topicMenuKey:fromParent: (in category 'menus') -----
> >       + topicMenuKey: aChar fromParent: parentTopic
> >       +
> >       + aChar
> >       + caseOf: {
> >       + [$b] -> [(self canBrowseTopicFromParent: parentTopic)
> >       + ifTrue: [ self browseTopicFromParent: parentTopic ]].
> >       + [$i] -> [self inspect].
> >       + [$I] -> [self explore] }
> >       + otherwise: [^ false].
> >       + ^ true!
> >
> >       Item was added:
> >       + ----- Method: ClassAPIHelpTopic>>canBrowseTopic (in category 'testing') -----
> >       + canBrowseTopic
> >       +
> >       + ^ true!
> >
> >       Item was added:
> >       + ----- Method: ClassBasedHelpTopic>>canBrowseSubtopic (in category 'testing') -----
> >       + canBrowseSubtopic
> >       +
> >       + ^ true!
> >
> >       Item was added:
> >       + ----- Method: ClassBasedHelpTopic>>canBrowseTopic (in category 'testing') -----
> >       + canBrowseTopic
> >       +
> >       + ^ true!
> >
> >       Item was added:
> >       + ----- Method: DirectoryBasedHelpTopic>>canBrowseTopic (in category 'testing') -----
> >       + canBrowseTopic
> >       +
> >       + ^ true!
> >
> >       Item was added:
> >       + ----- Method: FileBasedHelpTopic>>canBrowseTopic (in category 'testing') -----
> >       + canBrowseTopic
> >       +
> >       + ^ true!
> >
> >       Item was removed:
> >       - ----- Method: HelpBrowser>>browseTopic (in category 'actions') -----
> >       - browseTopic
> >       -
> >       - ^ (self currentTopic respondsTo: #browseTopic)
> >       - ifTrue: [self currentTopic browseTopic]
> >       - ifFalse: [self currentParentTopic browseSubtopic: self currentTopic]!
> >
> >       Item was removed:
> >       - ----- Method: HelpBrowser>>canBrowseTopic (in category 'testing') -----
> >       - canBrowseTopic
> >       -
> >       - ^ (self currentTopic respondsTo: #browseTopic)
> >       - or: [self currentParentTopic respondsTo: #browseSubtopic:]!
> >
> >       Item was removed:
> >       - ----- Method: HelpBrowser>>exploreTopic (in category 'actions') -----
> >       - exploreTopic
> >       -
> >       - ^ self currentTopic explore!
> >
> >       Item was removed:
> >       - ----- Method: HelpBrowser>>inspectTopic (in category 'actions') -----
> >       - inspectTopic
> >       -
> >       - ^ self currentTopic inspect!
> >
> >       Item was changed:
> >       ----- Method: HelpBrowser>>treeKey:from:event: (in category 'menus') -----
> >       treeKey: aChar from: aView event: anEvent
> >
> >       anEvent anyModifierKeyPressed ifFalse: [^ false].
> >       + ^ (self currentTopic topicMenuKey: aChar fromParent: self currentParentTopic)!
> >       - aChar
> >       - caseOf: {
> >       - [$b] -> [self browseTopic].
> >       - [$i] -> [self inspectTopic].
> >       - [$I] -> [self exploreTopic]. }
> >       - otherwise: [^ false].
> >       - ^ true!
> >
> >       Item was changed:
> >       ----- Method: HelpBrowser>>treeListMenu: (in category 'menus') -----
> >       treeListMenu: aMenu
> >
> >
> >       + ^ self currentTopic
> >       + ifNil: [aMenu]
> >       + ifNotNil: [:topic | topic
> >       + topicMenu: aMenu
> >       + parentTopic: self currentParentTopic]!
> >       - self currentTopic ifNil: [^ aMenu].
> >       -
> >       - aMenu
> >       - add: 'Inspect (i)' action: #inspectTopic;
> >       - add: 'Explore (I)' action: #exploreTopic.
> >       -
> >       - self canBrowseTopic ifTrue: [
> >       - aMenu
> >       - addLine;
> >       - add: 'Browse (b)' action: #browseTopic].
> >       -
> >       - ^ aMenu!
> >
> >       Item was added:
> >       + ----- Method: MethodListHelpTopic>>canBrowseTopic (in category 'testing') -----
> >       + canBrowseTopic
> >       +
> >       + ^ true!
> >
> >       Item was added:
> >       + ----- Method: PackageAPIHelpTopic>>canBrowseTopic (in category 'testing') -----
> >       + canBrowseTopic
> >       +
> >       + ^ true!
> >
> >
> >
> >
>
>

Reply | Threaded
Open this post in threaded view
|

Re: #isNilOr:, #notNilAnd:

Eliot Miranda-2
In reply to this post by Christoph Thiede
Hi Christoph,


On Dec 11, 2019, at 2:01 AM, Thiede, Christoph <[hidden email]> wrote:



Hi all, just another idea for a hypothetic extension to ProtoObject:



Well, personally I don’t find the abbreviation compelling but would counsel against this for two  reasons:

1. (most important) ProtoObject exists to provide the absolute minimum protocol an object needs to be debugged by the system.  Ideally this would mean that ProtoObject *only* implement doesNotUnderstand: such that when sent a message a proto object or incomplete proxy (one whose doesNotUnderstand: method is yet to be defined) a debugger is raised rather then the VM aborting with a recursive not understood error.  ProtoObject is *not* a place to put protocol understood by all normal objects.

The whole point of ProtoObject is to support transparent proxies that raise doesNotUnderstand: when sent any message. All the protocol in ProtoObject other than doesNotUnderstand: is a colossal mistake and the result of serious misunderstanding of the utility and implementation of transparent forwarders.

2. we have a prototype adaptive optimizer/speculative inliner, Scorch, written by Clément Béra, with my having designed them lower levels of the architecture. I am currebtly making space in my work life to port this to Squeak and productise it.  This optimizer will automatically create inlined code that is much more efficient than these helped methods.  So these are a premature optimization.  

So please, no.

ProtoObject >> #isNilOr: aBlock

^ aBlock cull: self

UndefinedObject >> #isNilOr: aBlock

^ true

ProtoObject >> #notNilAnd: aBlock

^ aBlock cull: self

UndefinedObject >> #notNilAnd: aBlock

^ false


Examples:

parentObject notNilAnd: #canBrowseSubtopic.

helpClass notNilAnd: [:hC| hC usesCodeStyling].

self selectionIndex isNilOr: [:index | index > 2].

anObject isNilOr: #isZero.

foo isNilOr: #isNotEmpty.


Just a bit syntactic sugar for idiomatic problems such as below. In particular, I found several possible applications for #notNilAnd: in my image.


Go ahead and tell me why it would be a bad idea :)


Best,

Christoph



Von: Squeak-dev <[hidden email]> im Auftrag von Thiede, Christoph
Gesendet: Freitag, 25. Oktober 2019 19:21 Uhr
An: gettimothy via Squeak-dev
Betreff: Re: [squeak-dev] The Inbox: HelpSystem-Core-ct.123.mcz
 

If you're needing a decision, I'd vote for option 3.


Von: Squeak-dev <[hidden email]> im Auftrag von Taeumel, Marcel
Gesendet: Mittwoch, 16. Oktober 2019 11:14:15
An: gettimothy via Squeak-dev
Betreff: Re: [squeak-dev] The Inbox: HelpSystem-Core-ct.123.mcz
 
Quick suggestion on the formatting. This one:

^ self canBrowseTopic or: [
   parentTopic ifNotNil: #canBrowseSubtopic ifNil: [false]]

could become:

^ self canBrowseTopic or: [
   parentTopic
ifNil: [false]
ifNotNil: [:topic | topic canBrowseSubtopic]]

or:

^ self canBrowseTopic
      or: [parentTopic notNil and: [parentTopic canBrowseSubtopic]]

Hmmm... what are other opinions on this? There is no need for #ifNil/ifNotNil in such a boolean expression?

Best,
Marcel


Am 13.10.2019 21:04:19 schrieb [hidden email] <[hidden email]>:

A new version of HelpSystem-Core was added to project The Inbox:
http://source.squeak.org/inbox/HelpSystem-Core-ct.123.mcz

==================== Summary ====================

Name: HelpSystem-Core-ct.123
Author: ct
Time: 13 October 2019, 9:04:08.373932 pm
UUID: dec7ceca-320f-d945-8d2a-c2f6a5e49a52
Ancestors: HelpSystem-Core-ct.120

Refactors HelpBrowser menu: Move menu stuff from HelpBrowser into HelpTopic hierarchy in favor of a better object design

Thanks again, Marcel :-)

=============== Diff against HelpSystem-Core-ct.120 ===============

Item was added:
+ ----- Method: AbstractHelpTopic>>browseTopicFromParent: (in category 'tools') -----
+ browseTopicFromParent: parentTopic
+
+ self canBrowseTopic
+ ifTrue: [^ self browseTopic].
+ parentTopic canBrowseSubtopic
+ ifTrue: [^ parentTopic browseSubtopic: self].
+ !

Item was added:
+ ----- Method: AbstractHelpTopic>>canBrowseSubtopic (in category 'testing') -----
+ canBrowseSubtopic
+
+ ^ false!

Item was added:
+ ----- Method: AbstractHelpTopic>>canBrowseTopic (in category 'testing') -----
+ canBrowseTopic
+
+ ^ false!

Item was added:
+ ----- Method: AbstractHelpTopic>>canBrowseTopicFromParent: (in category 'testing') -----
+ canBrowseTopicFromParent: parentTopic
+
+ ^ self canBrowseTopic or: [
+ parentTopic ifNotNil: #canBrowseSubtopic ifNil: [false]]!

Item was added:
+ ----- Method: AbstractHelpTopic>>topicMenu:parentTopic: (in category 'menus') -----
+ topicMenu: aMenu parentTopic: parentTopic
+
+ aMenu
+ add: 'Inspect (i)' translated target: self action: #inspect;
+ add: 'Explore (I)' translated target: self action: #explore.
+ (self canBrowseTopicFromParent: parentTopic)
+ ifTrue: [
+ aMenu add: 'Browse (b)' translated
+ target: self
+ selector: #browseTopicFromParent:
+ argumentList: {parentTopic} ].
+
+ ^ aMenu!

Item was added:
+ ----- Method: AbstractHelpTopic>>topicMenuKey:fromParent: (in category 'menus') -----
+ topicMenuKey: aChar fromParent: parentTopic
+
+ aChar
+ caseOf: {
+ [$b] -> [(self canBrowseTopicFromParent: parentTopic)
+ ifTrue: [ self browseTopicFromParent: parentTopic ]].
+ [$i] -> [self inspect].
+ [$I] -> [self explore] }
+ otherwise: [^ false].
+ ^ true!

Item was added:
+ ----- Method: ClassAPIHelpTopic>>canBrowseTopic (in category 'testing') -----
+ canBrowseTopic
+
+ ^ true!

Item was added:
+ ----- Method: ClassBasedHelpTopic>>canBrowseSubtopic (in category 'testing') -----
+ canBrowseSubtopic
+
+ ^ true!

Item was added:
+ ----- Method: ClassBasedHelpTopic>>canBrowseTopic (in category 'testing') -----
+ canBrowseTopic
+
+ ^ true!

Item was added:
+ ----- Method: DirectoryBasedHelpTopic>>canBrowseTopic (in category 'testing') -----
+ canBrowseTopic
+
+ ^ true!

Item was added:
+ ----- Method: FileBasedHelpTopic>>canBrowseTopic (in category 'testing') -----
+ canBrowseTopic
+
+ ^ true!

Item was removed:
- ----- Method: HelpBrowser>>browseTopic (in category 'actions') -----
- browseTopic
-
- ^ (self currentTopic respondsTo: #browseTopic)
- ifTrue: [self currentTopic browseTopic]
- ifFalse: [self currentParentTopic browseSubtopic: self currentTopic]!

Item was removed:
- ----- Method: HelpBrowser>>canBrowseTopic (in category 'testing') -----
- canBrowseTopic
-
- ^ (self currentTopic respondsTo: #browseTopic)
- or: [self currentParentTopic respondsTo: #browseSubtopic:]!

Item was removed:
- ----- Method: HelpBrowser>>exploreTopic (in category 'actions') -----
- exploreTopic
-
- ^ self currentTopic explore!

Item was removed:
- ----- Method: HelpBrowser>>inspectTopic (in category 'actions') -----
- inspectTopic
-
- ^ self currentTopic inspect!

Item was changed:
----- Method: HelpBrowser>>treeKey:from:event: (in category 'menus') -----
treeKey: aChar from: aView event: anEvent

anEvent anyModifierKeyPressed ifFalse: [^ false].
+ ^ (self currentTopic topicMenuKey: aChar fromParent: self currentParentTopic)!
- aChar
- caseOf: {
- [$b] -> [self browseTopic].
- [$i] -> [self inspectTopic].
- [$I] -> [self exploreTopic]. }
- otherwise: [^ false].
- ^ true!

Item was changed:
----- Method: HelpBrowser>>treeListMenu: (in category 'menus') -----
treeListMenu: aMenu


+ ^ self currentTopic
+ ifNil: [aMenu]
+ ifNotNil: [:topic | topic
+ topicMenu: aMenu
+ parentTopic: self currentParentTopic]!
- self currentTopic ifNil: [^ aMenu].
-
- aMenu
- add: 'Inspect (i)' action: #inspectTopic;
- add: 'Explore (I)' action: #exploreTopic.
-
- self canBrowseTopic ifTrue: [
- aMenu
- addLine;
- add: 'Browse (b)' action: #browseTopic].
-
- ^ aMenu!

Item was added:
+ ----- Method: MethodListHelpTopic>>canBrowseTopic (in category 'testing') -----
+ canBrowseTopic
+
+ ^ true!

Item was added:
+ ----- Method: PackageAPIHelpTopic>>canBrowseTopic (in category 'testing') -----
+ canBrowseTopic
+
+ ^ true!






Reply | Threaded
Open this post in threaded view
|

Re: #isNilOr:, #notNilAnd:

Stéphane Rollandin
> The whole point of ProtoObject is to support transparent proxies that
> raise doesNotUnderstand: when sent any message. All the protocol in
> ProtoObject other than doesNotUnderstand: is a colossal mistake and the
> result of serious misunderstanding of the utility and implementation of
> transparent forwarders.

+1

Stef


Reply | Threaded
Open this post in threaded view
|

Re: ProtoObject (was: #isNilOr:, #notNilAnd:)

Levente Uzonyi
In reply to this post by Christoph Thiede
Hi Christoph,

On Sat, 21 Mar 2020, Thiede, Christoph wrote:

>
> (Forking this thread due to difference of concerns ... :-))
>
>
> > I'm also not sure whether these methods belong to ProtoObject or not.
>
> This is an interesting problem, see also the recent thread about cleaning up ProtoObject. I agree with Eliot that a good ProtoObject is as small and stupid as possible in order to be really useful for writing powerful proxies
> or decorators. It should be possible to wrap nil into an ObjectTracer, for example! To put the issue in a nutshell, try out the following:
>
>       newNil := UndefinedObject basicNew.
>
> nilTracer := ObjectTracer on: newNil.
>
> nilTracer isNil. "false"
>
> nilTracer perform: #isNil. "true"
nil is a singleton. You seem to be aware of this because in your code you
had to work around the guard implemented in UndefinedObject >> #new to
create another instance by using #basicNew.
The VM relies on this property, so nil is a special object known by the
VM.
nil's role as a singleton is pronounced by the language itself: it has
its own keyword.
Perhaps we should add that guard to #basicNew. :)

>
>
> Here's a similar issue:
>
>       specialClass := Object newSubclass.
> specialClass compile: 'class ^42'.
> specialObj := specialClass new.
> specialObj class. "Object1"
> specialObj perform: #class. "42"

Yes, #class is a special selector known by the VM. IIRC this one is easier
to get rid of by removing #class from #specialSelectors (or disabling
the use of the special bytecode for #class some other way) and recompiling
all code.

>
>
> (By the way, we are not even consistent with ourself in this concern:
>
>       ([specialObj class] newProcess runUntil: #willReturn) suspendedContext top. "42"
>
>
> ) Pooh, that's both really not nice ... Basically, inlining of #isNil & Co. is an optimization and IMHO it should not affect the basic concept of how messages are dispatched. Too less inlining affects performance, but too
> much inlining introduces conceptional errors.

#isNil is not inlined in Squeak. #ifNil:, #ifNotNil:, #ifNil:ifNotNil:,
#ifNotNil:ifNil: are inlined when their arguments are literal blocks.

Having them inlined is perfectly fine as long as nil is a singleton.

> Would it be a big performance impact if we made inlining optional? Here is a short proposal:

Here's a quick benchmark measuring the slowdown of #ifNotNil: with a non
full block 10 times:

((1 to: 10) collect: [ :run |
  [ :empty :inlined :notInlined | (notInlined - empty) / (inlined - empty) asFloat ]
  valueWithArguments: {
  [ 1 to: 100000000 do: [ :i | ] ] timeToRun.
  [ 1 to: 100000000 do: [ :i | i ifNotNil: [ :j | j ] ] ] timeToRun.
  [ | b | b := [ :j | j ]. 1 to: 100000000 do: [ :i | i ifNotNil: b ] ] timeToRun } ])
  sort
  collect: [ :result | result asScaledDecimal: 2 ]

On my machine it yields values between 12 and 32.

>
> We could tell every bytecode interpreter to check for any receiver if it agrees on being deprived of messages that can be inlined. We could decide this per receiver class and cache these answers in the VM. Hypothetical
> example:
>
>       Object >> #acceptsInlining
>
>     "This message is never sent, but it's existance signalizes the VM that it is okay to inline popular messages such as #isNil or #ifTrue:."
>
>
> If this concept is too expensive or too confusing, we could also add an instance variable to the class object. Anyway, something like this. That way we could make the concept of inlining optional. If an interpreter evaluated
> {nilTracer isNil}, it would have to do the following (I'm following the simulation code in #interpretNextSistaV1InstructionFor:. Disclaimer: Absolutely free of optimizations!)
>
>       ...
> "short sends"
> (div16 = 6 and: [rcvr respondsTo: #acceptsInlining]) ifTrue:
> [^client
> send: (Smalltalk specialSelectorAt: offset + 1)
> super: false
> numArgs: (Smalltalk specialNargsAt: offset + 1)].
> ...
> "otherwise, message will be dispatched regularly"
>
>
> Some similar checks would be required in case of primitives that are generated for inlined methods, such as 257f.:
>
>       ...
> ((primIndex := meth primitive) > 0 and: [
>         (self isInliningPrimitive: primitive) not or: [rcvr respondsTo: #acceptsInlining]])
> ifTrue:
>     [val := self doPrimitive: primIndex method: meth receiver: rcvr args: arguments.
>     (self isPrimFailToken: val) ifFalse:
>         [^val]]
> ...
> "otherwise, message will be dispatched regularly"
>
>
> How would you think of this concept in general? I'm excited to hear your feedback! :-)
My impression is that you want to break the basic concept of nil being a
singleton. It's doable, but then you'll need a separate compiler and
object space where the nil keyword is not allowed.


Levente

>
> Best,
> Christoph
>
>
> _________________________________________________________________________________________________________________________________________________________________________________________________________________________________
> Von: Squeak-dev <[hidden email]> im Auftrag von Levente Uzonyi <[hidden email]>
> Gesendet: Samstag, 21. März 2020 20:25 Uhr
> An: The general-purpose Squeak developers list
> Betreff: Re: [squeak-dev] #isNilOr:, #notNilAnd:  
> Hi Christoph,
>
> They are okay for scripts, but we know that methods like that always end
> up being used out of scripts, and are even misused every now and then.
> They are especially susceptible to misuse when their return value is
> ignored; in which case they are both equivalent to #ifNotNil:.
>
> I'm also not sure whether these methods belong to ProtoObject or not.
> I know the other nil handling methods are there as well, but that doesn't
> mean all such methods belong there.
> E.g. #ifNil:/#ifNotNil: should be there because the current compiler and
> VM will make those available for all objects provided the methods are
> inlined. But #isNil and #notNil have no such properties, so they belong
> more to Object than ProtoObject in my opinion.
>
>
> Levente
>
> On Mon, 16 Mar 2020, Thiede, Christoph wrote:
>
> >
> > Hi all,
> >
> >
> > after the release has been managed successfully, I would like to kindly push this proposal. In the latest months, I found myself to really often desiring #isNilOr: or #notNilAnd:. How would you think about these convenience
> > selectors? :-)
> >
> >
> > Best,
> >
> > Christoph
> >
> >________________________________________________________________________________________________________________________________________________________________________________________________________________________________
> _
> > Von: Thiede, Christoph
> > Gesendet: Mittwoch, 11. Dezember 2019 11:01:44
> > An: Squeak Dev
> > Betreff: #isNilOr:, #notNilAnd:  
> >
> > Hi all, just another idea for a hypothetic extension to ProtoObject:
> >
> >
> > ProtoObject >> #isNilOr: aBlock
> >
> > ^ aBlock cull: self
> >
> > UndefinedObject >> #isNilOr: aBlock
> >
> > ^ true
> >
> > ProtoObject >> #notNilAnd: aBlock
> >
> > ^ aBlock cull: self
> >
> > UndefinedObject >> #notNilAnd: aBlock
> >
> > ^ false
> >
> >
> > Examples:
> >
> > parentObject notNilAnd: #canBrowseSubtopic.
> >
> > helpClass notNilAnd: [:hC| hC usesCodeStyling].
> >
> > self selectionIndex isNilOr: [:index | index > 2].
> >
> > anObject isNilOr: #isZero.
> >
> > foo isNilOr: #isNotEmpty.
> >
> >
> > Just a bit syntactic sugar for idiomatic problems such as below. In particular, I found several possible applications for #notNilAnd: in my image.
> >
> >
> > Go ahead and tell me why it would be a bad idea :)
> >
> >
> > Best,
> >
> > Christoph
> >
> >
> >________________________________________________________________________________________________________________________________________________________________________________________________________________________________
> _
> > Von: Squeak-dev <[hidden email]> im Auftrag von Thiede, Christoph
> > Gesendet: Freitag, 25. Oktober 2019 19:21 Uhr
> > An: gettimothy via Squeak-dev
> > Betreff: Re: [squeak-dev] The Inbox: HelpSystem-Core-ct.123.mcz  
> >
> > If you're needing a decision, I'd vote for option 3.
> >
> >________________________________________________________________________________________________________________________________________________________________________________________________________________________________
> _
> > Von: Squeak-dev <[hidden email]> im Auftrag von Taeumel, Marcel
> > Gesendet: Mittwoch, 16. Oktober 2019 11:14:15
> > An: gettimothy via Squeak-dev
> > Betreff: Re: [squeak-dev] The Inbox: HelpSystem-Core-ct.123.mcz  
> > Quick suggestion on the formatting. This one:
> > ^ self canBrowseTopic or: [
> >    parentTopic ifNotNil: #canBrowseSubtopic ifNil: [false]]
> >
> > could become:
> >
> > ^ self canBrowseTopic or: [
> >    parentTopic
> >    ifNil: [false]
> >    ifNotNil: [:topic | topic canBrowseSubtopic]]
> >
> > or:
> >
> > ^ self canBrowseTopic
> >       or: [parentTopic notNil and: [parentTopic canBrowseSubtopic]]
> >
> > Hmmm... what are other opinions on this? There is no need for #ifNil/ifNotNil in such a boolean expression?
> >
> > Best,
> > Marcel
> >
> >
> >       Am 13.10.2019 21:04:19 schrieb [hidden email] <[hidden email]>:
> >
> >       A new version of HelpSystem-Core was added to project The Inbox:
> >       http://source.squeak.org/inbox/HelpSystem-Core-ct.123.mcz
> >
> >       ==================== Summary ====================
> >
> >       Name: HelpSystem-Core-ct.123
> >       Author: ct
> >       Time: 13 October 2019, 9:04:08.373932 pm
> >       UUID: dec7ceca-320f-d945-8d2a-c2f6a5e49a52
> >       Ancestors: HelpSystem-Core-ct.120
> >
> >       Refactors HelpBrowser menu: Move menu stuff from HelpBrowser into HelpTopic hierarchy in favor of a better object design
> >
> >       Thanks again, Marcel :-)
> >
> >       =============== Diff against HelpSystem-Core-ct.120 ===============
> >
> >       Item was added:
> >       + ----- Method: AbstractHelpTopic>>browseTopicFromParent: (in category 'tools') -----
> >       + browseTopicFromParent: parentTopic
> >       +
> >       + self canBrowseTopic
> >       + ifTrue: [^ self browseTopic].
> >       + parentTopic canBrowseSubtopic
> >       + ifTrue: [^ parentTopic browseSubtopic: self].
> >       + !
> >
> >       Item was added:
> >       + ----- Method: AbstractHelpTopic>>canBrowseSubtopic (in category 'testing') -----
> >       + canBrowseSubtopic
> >       +
> >       + ^ false!
> >
> >       Item was added:
> >       + ----- Method: AbstractHelpTopic>>canBrowseTopic (in category 'testing') -----
> >       + canBrowseTopic
> >       +
> >       + ^ false!
> >
> >       Item was added:
> >       + ----- Method: AbstractHelpTopic>>canBrowseTopicFromParent: (in category 'testing') -----
> >       + canBrowseTopicFromParent: parentTopic
> >       +
> >       + ^ self canBrowseTopic or: [
> >       + parentTopic ifNotNil: #canBrowseSubtopic ifNil: [false]]!
> >
> >       Item was added:
> >       + ----- Method: AbstractHelpTopic>>topicMenu:parentTopic: (in category 'menus') -----
> >       + topicMenu: aMenu parentTopic: parentTopic
> >       +
> >       + aMenu
> >       + add: 'Inspect (i)' translated target: self action: #inspect;
> >       + add: 'Explore (I)' translated target: self action: #explore.
> >       + (self canBrowseTopicFromParent: parentTopic)
> >       + ifTrue: [
> >       + aMenu add: 'Browse (b)' translated
> >       + target: self
> >       + selector: #browseTopicFromParent:
> >       + argumentList: {parentTopic} ].
> >       +
> >       + ^ aMenu!
> >
> >       Item was added:
> >       + ----- Method: AbstractHelpTopic>>topicMenuKey:fromParent: (in category 'menus') -----
> >       + topicMenuKey: aChar fromParent: parentTopic
> >       +
> >       + aChar
> >       + caseOf: {
> >       + [$b] -> [(self canBrowseTopicFromParent: parentTopic)
> >       + ifTrue: [ self browseTopicFromParent: parentTopic ]].
> >       + [$i] -> [self inspect].
> >       + [$I] -> [self explore] }
> >       + otherwise: [^ false].
> >       + ^ true!
> >
> >       Item was added:
> >       + ----- Method: ClassAPIHelpTopic>>canBrowseTopic (in category 'testing') -----
> >       + canBrowseTopic
> >       +
> >       + ^ true!
> >
> >       Item was added:
> >       + ----- Method: ClassBasedHelpTopic>>canBrowseSubtopic (in category 'testing') -----
> >       + canBrowseSubtopic
> >       +
> >       + ^ true!
> >
> >       Item was added:
> >       + ----- Method: ClassBasedHelpTopic>>canBrowseTopic (in category 'testing') -----
> >       + canBrowseTopic
> >       +
> >       + ^ true!
> >
> >       Item was added:
> >       + ----- Method: DirectoryBasedHelpTopic>>canBrowseTopic (in category 'testing') -----
> >       + canBrowseTopic
> >       +
> >       + ^ true!
> >
> >       Item was added:
> >       + ----- Method: FileBasedHelpTopic>>canBrowseTopic (in category 'testing') -----
> >       + canBrowseTopic
> >       +
> >       + ^ true!
> >
> >       Item was removed:
> >       - ----- Method: HelpBrowser>>browseTopic (in category 'actions') -----
> >       - browseTopic
> >       -
> >       - ^ (self currentTopic respondsTo: #browseTopic)
> >       - ifTrue: [self currentTopic browseTopic]
> >       - ifFalse: [self currentParentTopic browseSubtopic: self currentTopic]!
> >
> >       Item was removed:
> >       - ----- Method: HelpBrowser>>canBrowseTopic (in category 'testing') -----
> >       - canBrowseTopic
> >       -
> >       - ^ (self currentTopic respondsTo: #browseTopic)
> >       - or: [self currentParentTopic respondsTo: #browseSubtopic:]!
> >
> >       Item was removed:
> >       - ----- Method: HelpBrowser>>exploreTopic (in category 'actions') -----
> >       - exploreTopic
> >       -
> >       - ^ self currentTopic explore!
> >
> >       Item was removed:
> >       - ----- Method: HelpBrowser>>inspectTopic (in category 'actions') -----
> >       - inspectTopic
> >       -
> >       - ^ self currentTopic inspect!
> >
> >       Item was changed:
> >       ----- Method: HelpBrowser>>treeKey:from:event: (in category 'menus') -----
> >       treeKey: aChar from: aView event: anEvent
> >
> >       anEvent anyModifierKeyPressed ifFalse: [^ false].
> >       + ^ (self currentTopic topicMenuKey: aChar fromParent: self currentParentTopic)!
> >       - aChar
> >       - caseOf: {
> >       - [$b] -> [self browseTopic].
> >       - [$i] -> [self inspectTopic].
> >       - [$I] -> [self exploreTopic]. }
> >       - otherwise: [^ false].
> >       - ^ true!
> >
> >       Item was changed:
> >       ----- Method: HelpBrowser>>treeListMenu: (in category 'menus') -----
> >       treeListMenu: aMenu
> >
> >
> >       + ^ self currentTopic
> >       + ifNil: [aMenu]
> >       + ifNotNil: [:topic | topic
> >       + topicMenu: aMenu
> >       + parentTopic: self currentParentTopic]!
> >       - self currentTopic ifNil: [^ aMenu].
> >       -
> >       - aMenu
> >       - add: 'Inspect (i)' action: #inspectTopic;
> >       - add: 'Explore (I)' action: #exploreTopic.
> >       -
> >       - self canBrowseTopic ifTrue: [
> >       - aMenu
> >       - addLine;
> >       - add: 'Browse (b)' action: #browseTopic].
> >       -
> >       - ^ aMenu!
> >
> >       Item was added:
> >       + ----- Method: MethodListHelpTopic>>canBrowseTopic (in category 'testing') -----
> >       + canBrowseTopic
> >       +
> >       + ^ true!
> >
> >       Item was added:
> >       + ----- Method: PackageAPIHelpTopic>>canBrowseTopic (in category 'testing') -----
> >       + canBrowseTopic
> >       +
> >       + ^ true!
> >
> >
> >
> >
>
>

Reply | Threaded
Open this post in threaded view
|

Re: #isNilOr:, #notNilAnd:

Christoph Thiede
In reply to this post by Stéphane Rollandin

Hi all,


Levente:


> Both #isNilOr: and #notNilAnd: return a boolean value. It is possible to use these methods and ignore their return values. E.g.:
> object ifNotNil: [ collection add: object ].
> can be written as:
> object isNilOr: [ collection add: object ].
> or as:
> object notNilAnd: [ collection add: object ].

I see your point. However, I think this would be a client problem rather than a design problem. :-)

The names tell you when you should send which message. Each selector has a different semantic that is also formed by its return value. If we neglected the return value, we could also critique the following:


aBoolean ifTrue: aBlock

can be written as:

aBoolean ==> aBlock


The return value is significant. Otherwise, you could compare the following:

aCollection do: aBlock

aCollection collect: aBlock

aCollection select: aBlock

...


Eliot:


I have to apologize, you're absolutely right on ProtoObject vs. Object. Back in December, I did not yet understand the actual idea of ProtoObject. I absolutely agree with you that any extension of this kind should go to Object instead.


Best,

Christoph


Von: Squeak-dev <[hidden email]> im Auftrag von Stéphane Rollandin <[hidden email]>
Gesendet: Sonntag, 22. März 2020 09:03:42
An: [hidden email]
Betreff: Re: [squeak-dev] #isNilOr:, #notNilAnd:
 
> The whole point of ProtoObject is to support transparent proxies that
> raise doesNotUnderstand: when sent any message. All the protocol in
> ProtoObject other than doesNotUnderstand: is a colossal mistake and the
> result of serious misunderstanding of the utility and implementation of
> transparent forwarders.

+1

Stef




Reply | Threaded
Open this post in threaded view
|

Re: #isNilOr:, #notNilAnd:

Levente Uzonyi
Hi Christoph,

On Sun, 22 Mar 2020, Thiede, Christoph wrote:

>
> Hi all,
>
>
> Levente:
>
>
> > Both #isNilOr: and #notNilAnd: return a boolean value. It is possible to use these methods and ignore their return values. E.g.:
> > 
> > object ifNotNil: [ collection add: object ].
> > 
> > can be written as:
> > 
> > object isNilOr: [ collection add: object ].
> > 
> > or as:
> > 
> > object notNilAnd: [ collection add: object ].
>
> I see your point. However, I think this would be a client problem rather than a design problem. :-)
I fully agree this is a "client problem". Have a look at the senders of
#in: in a Trunk image (82 senders).
Most uses are entirely artifical (e.g. "I just avoid declaring a temporary
for no reason"), some are outrageous (e.g. I don't want to use any
temporaries, and I don't want to break the cascade chain I'm in, so I use
various methods accepting blocks instead of separate statements just to
write my code as a single expression"), very few are legitimate.
So, yes, we have a client problem. That is why I wrote what I wrote:
I expect adding these would introduce more of those misuses as with #in:.


Levente

>
> The names tell you when you should send which message. Each selector has a different semantic that is also formed by its return value. If we neglected the return value, we could also critique the following:
>
>
> aBoolean ifTrue: aBlock
>
> can be written as:
>
> aBoolean ==> aBlock
>
>
> The return value is significant. Otherwise, you could compare the following:
>
> aCollection do: aBlock
>
> aCollection collect: aBlock
>
> aCollection select: aBlock
>
> ...
>
>
> Eliot:
>
>
> I have to apologize, you're absolutely right on ProtoObject vs. Object. Back in December, I did not yet understand the actual idea of ProtoObject. I absolutely agree with you that any extension of this kind should go to
> Object instead.
>
>
> Best,
>
> Christoph
>
> _________________________________________________________________________________________________________________________________________________________________________________________________________________________________
> Von: Squeak-dev <[hidden email]> im Auftrag von Stéphane Rollandin <[hidden email]>
> Gesendet: Sonntag, 22. März 2020 09:03:42
> An: [hidden email]
> Betreff: Re: [squeak-dev] #isNilOr:, #notNilAnd:  
> > The whole point of ProtoObject is to support transparent proxies that
> > raise doesNotUnderstand: when sent any message. All the protocol in
> > ProtoObject other than doesNotUnderstand: is a colossal mistake and the
> > result of serious misunderstanding of the utility and implementation of
> > transparent forwarders.
>
> +1
>
> Stef
>
>
>
>

Reply | Threaded
Open this post in threaded view
|

Re: #isNilOr:, #notNilAnd:

Eliot Miranda-2
Hi Levente,

> On Mar 22, 2020, at 1:31 PM, Levente Uzonyi <[hidden email]> wrote:
>
> Hi Christoph,
>
>> On Sun, 22 Mar 2020, Thiede, Christoph wrote:
>>
>> Hi all,
>> Levente:
>> > Both #isNilOr: and #notNilAnd: return a boolean value. It is possible to use these methods and ignore their return values. E.g.:
>> >
>> > object ifNotNil: [ collection add: object ].
>> >
>> > can be written as:
>> >
>> > object isNilOr: [ collection add: object ].
>> >
>> > or as:
>> >
>> > object notNilAnd: [ collection add: object ].
>> I see your point. However, I think this would be a client problem rather than a design problem. :-)
>
> I fully agree this is a "client problem". Have a look at the senders of #in: in a Trunk image (82 senders).
> Most uses are entirely artifical (e.g. "I just avoid declaring a temporary for no reason"), some are outrageous (e.g. I don't want to use any temporaries, and I don't want to break the cascade chain I'm in, so I use various methods accepting blocks instead of separate statements just to write my code as a single expression"), very few are legitimate.

I have to say that I disagree that using ifNotNil: to avoid a temporary is outrageous :-). In fact it is part of Vassili’s design rationale for introducing ifNotNil: as an inlineable selector in the first place.  It not only localizes the declaration, changing yo to be a block argument, it makes it read-only ;a definition) and eliminates an assignment.  I’m very fond of using ifNotNil: to bind a value that is nil when it is to be ignored, and far from finding it outrageous, find it elegant.  Tastes differ.  Vice la difference.


> So, yes, we have a client problem. That is why I wrote what I wrote: I expect adding these would introduce more of those misuses as with #in:.
>
>
> Levente
>
>> The names tell you when you should send which message. Each selector has a different semantic that is also formed by its return value. If we neglected the return value, we could also critique the following:
>> aBoolean ifTrue: aBlock
>> can be written as:
>> aBoolean ==> aBlock
>> The return value is significant. Otherwise, you could compare the following:
>> aCollection do: aBlock
>> aCollection collect: aBlock
>> aCollection select: aBlock
>> ...
>> Eliot:
>> I have to apologize, you're absolutely right on ProtoObject vs. Object. Back in December, I did not yet understand the actual idea of ProtoObject. I absolutely agree with you that any extension of this kind should go to
>> Object instead.
>> Best,
>> Christoph
>> _________________________________________________________________________________________________________________________________________________________________________________________________________________________________
>> Von: Squeak-dev <[hidden email]> im Auftrag von Stéphane Rollandin <[hidden email]>
>> Gesendet: Sonntag, 22. März 2020 09:03:42
>> An: [hidden email]
>> Betreff: Re: [squeak-dev] #isNilOr:, #notNilAnd:  
>> > The whole point of ProtoObject is to support transparent proxies that
>> > raise doesNotUnderstand: when sent any message. All the protocol in
>> > ProtoObject other than doesNotUnderstand: is a colossal mistake and the
>> > result of serious misunderstanding of the utility and implementation of
>> > transparent forwarders.
>> +1
>> Stef
>

Reply | Threaded
Open this post in threaded view
|

#in: (was Re: #isNilOr:, #notNilAnd:)

Levente Uzonyi
Hi Eliot,

On Sun, 22 Mar 2020, Eliot Miranda wrote:

> Hi Levente,
>
>> On Mar 22, 2020, at 1:31 PM, Levente Uzonyi <[hidden email]> wrote:
>>
>> Hi Christoph,
>>
>>> On Sun, 22 Mar 2020, Thiede, Christoph wrote:
>>>
>>> Hi all,
>>> Levente:
>>> > Both #isNilOr: and #notNilAnd: return a boolean value. It is possible to use these methods and ignore their return values. E.g.:
>>> >
>>> > object ifNotNil: [ collection add: object ].
>>> >
>>> > can be written as:
>>> >
>>> > object isNilOr: [ collection add: object ].
>>> >
>>> > or as:
>>> >
>>> > object notNilAnd: [ collection add: object ].
>>> I see your point. However, I think this would be a client problem rather than a design problem. :-)
>>
>> I fully agree this is a "client problem". Have a look at the senders of #in: in a Trunk image (82 senders).
>> Most uses are entirely artifical (e.g. "I just avoid declaring a temporary for no reason"), some are outrageous (e.g. I don't want to use any temporaries, and I don't want to break the cascade chain I'm in, so I use various methods accepting blocks instead of separate statements just to write my code as a single expression"), very few are legitimate.
>
> I have to say that I disagree that using ifNotNil: to avoid a temporary is outrageous :-). In fact it is part of Vassili’s design rationale for introducing ifNotNil: as an inlineable selector in the first place.  It not only localizes the declaration, changing yo to be a block argument, it makes it read-only ;a definition) and eliminates an assignment.  I’m very fond of using ifNotNil: to bind a value that is nil when it is to be ignored, and far from finding it outrageous, find it elegant.  Tastes differ.  Vice la difference.
I would disagree with that too, but my problem is with the widespread
misuse of #in:. #ifNotNil: is great.
I suggest you have a look at #in:'s senders in your image.


Levente

>
>
>> So, yes, we have a client problem. That is why I wrote what I wrote: I expect adding these would introduce more of those misuses as with #in:.
>>
>>
>> Levente
>>
>>> The names tell you when you should send which message. Each selector has a different semantic that is also formed by its return value. If we neglected the return value, we could also critique the following:
>>> aBoolean ifTrue: aBlock
>>> can be written as:
>>> aBoolean ==> aBlock
>>> The return value is significant. Otherwise, you could compare the following:
>>> aCollection do: aBlock
>>> aCollection collect: aBlock
>>> aCollection select: aBlock
>>> ...
>>> Eliot:
>>> I have to apologize, you're absolutely right on ProtoObject vs. Object. Back in December, I did not yet understand the actual idea of ProtoObject. I absolutely agree with you that any extension of this kind should go to
>>> Object instead.
>>> Best,
>>> Christoph
>>> _________________________________________________________________________________________________________________________________________________________________________________________________________________________________
>>> Von: Squeak-dev <[hidden email]> im Auftrag von Stéphane Rollandin <[hidden email]>
>>> Gesendet: Sonntag, 22. März 2020 09:03:42
>>> An: [hidden email]
>>> Betreff: Re: [squeak-dev] #isNilOr:, #notNilAnd:
>>> > The whole point of ProtoObject is to support transparent proxies that
>>> > raise doesNotUnderstand: when sent any message. All the protocol in
>>> > ProtoObject other than doesNotUnderstand: is a colossal mistake and the
>>> > result of serious misunderstanding of the utility and implementation of
>>> > transparent forwarders.
>>> +1
>>> Stef
>>

Reply | Threaded
Open this post in threaded view
|

Re: #in: (was Re: #isNilOr:, #notNilAnd:)

timrowledge


> On 2020-03-22, at 6:29 PM, Levente Uzonyi <[hidden email]> wrote:
>
> I would disagree with that too, but my problem is with the widespread misuse of #in:. #ifNotNil: is great.
> I suggest you have a look at #in:'s senders in your image.

Oh. My.

tim
--
tim Rowledge; [hidden email]; http://www.rowledge.org/tim
Strange OpCodes: HAL: Murder Operator



Reply | Threaded
Open this post in threaded view
|

Re: #in: (was Re: #isNilOr:, #notNilAnd:)

marcel.taeumel
Hi Christoph.

-1000 for adding #isNilOr:, #notNilAnd: and similar. :-)

Here is why:

> The names tell you when you should send which message.
> I don't see why you would not like to see #isNilOr: or #notNilAnd: in "production" code? I think their names are self-explaining.

Any kind of nil-check should be carefully questioned. Code readability is impaired for the very reason that the reader stumbles upon the term "nil", which belongs to no actual domain.

Only basic system modules should be allowed to make use of nil-checks. For any higher-level module, there are object-oriented patterns that help express "empty objects" appropriately.

We have what's already there and used. It's tricky to change that. BUT we can avoid adding more such things that would encourage sprinkling nil-checks all over the place -- even in higher-level models and production code.

> However, I think this would be a client problem rather than a design problem.

That's the thing. People will use what's there. They might not always question their decisions. They might not even refactor their programs to make the code look good. They are just happy if a program does what it should. The hassle of debugging and change might only hit other programmers later in the process. That's bad.

We should not open such a Pandora's box. ;-)

> To me, this reads rather ugly. 

Well, I do prefer "notNil and" over "notNilAnd:" just because of the extra space. I, personally, can read that more easily.

Best,
Marcel

P.S.: I think that programmers who like using cascades do also like using #in: from time to time. :-) #in: can make an intention more clear than #ifNotNil: because it avoids the nil-check.

Am 23.03.2020 02:38:38 schrieb tim Rowledge <[hidden email]>:



> On 2020-03-22, at 6:29 PM, Levente Uzonyi wrote:
>
> I would disagree with that too, but my problem is with the widespread misuse of #in:. #ifNotNil: is great.
> I suggest you have a look at #in:'s senders in your image.

Oh. My.

tim
--
tim Rowledge; [hidden email]; http://www.rowledge.org/tim
Strange OpCodes: HAL: Murder Operator





12