RBNode theory¶
sendso predicate¶
Consider the message
"RBProgramNodeSendsoVisitor, protocol visiting"
visitMessageNode: aMessageNode
super visitMessageNode: aMessageNode.
goals add: (selectorVar unifyo value: aMessageNode selectorNode)
We list the selectors that it sends with
"RBNodePredicatesTest, protocol tests"
testSenderoForCompiledMethod
self
assert: [ :selector |
(RBProgramNodeSendsoVisitor >> #visitMessageNode:) sendso value:
selector ] asGoal solutions
equals: {
(RBSelectorNode value: #visitMessageNode:).
(RBSelectorNode value: #unifyo).
(RBSelectorNode value: #selectorNode).
(RBSelectorNode value: #value:).
(RBSelectorNode value: #add:) }
modulo: #asOrderedCollection
where
"CompiledMethod, protocol *MicroKanren-RB"
sendso
^ self sourceNode body sendso
delegates to
"RBNode, protocol *MicroKanren-RB"
sendso
^ [ :anObject |
RBProgramNodeSendsoVisitor new
selectorVar: anObject;
visitNode: self;
asGoal ]
BlockClosure objects also allow us to retrieve their own representation in
terms of RBNode objects, therefore they respond to
"BlockClosure, protocol *MicroKanren-RB"
sendso
^ self sourceNode body sendso
as CompiledMethod objects do, so the initial test can be rephrased for them as
"RBNodePredicatesTest, protocol tests"
testSenderoForBlockClosure
| aBlock |
aBlock := [ :aMessageNode :goals :selectorVar |
super visitMessageNode: aMessageNode.
goals add:
(selectorVar unifyo value: aMessageNode selectorNode) ].
self
assert:
[ :selector | aBlock sendso value: selector ] asGoal solutions
equals: {
(RBSelectorNode value: #visitMessageNode:).
(RBSelectorNode value: #unifyo).
(RBSelectorNode value: #selectorNode).
(RBSelectorNode value: #value:).
(RBSelectorNode value: #add:) }
modulo: #asOrderedCollection.
self deny: (super respondsTo: #visitMessageNode:)
Observe that aBlock sends #visitMessageNode: to super even though
RBNodePredicatesTest superclass doesn’t implement #visitMessageNode:,
as the last #deny: checks. This shows that we use the block aBlock just
for its own AST discarding its computation.
As corner cases, both for the empty block
"RBNodePredicatesTest, protocol tests"
testSenderoForEmptyBlockClosure
self
assert: [ :selector | [ ] sendso value: selector ] asGoal solutions
equals: #( )
modulo: #asOrderedCollection
and for the identity block
"RBNodePredicatesTest, protocol tests"
testSenderoForIdentityBlockClosure
self
assert:
[ :selector | [ :o | o ] sendso value: selector ] asGoal solutions
equals: #( )
modulo: #asOrderedCollection
sendso yields no solutions at all.
Because we are training on a logic engine, it makes sense to use #sendso
backwards, as the following test shows
"RBNodePredicatesTest, protocol tests"
testSenderoBackwards
self arguments_testSenderoBackwards bind: [ :receiverNode :messageNodes |
self
assert: ([ :aSend |
[ :o :ast | ast sendso value: aSend ] asGoalWithASTof: [ :o | o ] ]
asGoal solutions next: messageNodes size)
equals: messageNodes
modulo: #asOrderedCollection ]