Reputation: 346
How can I adapt the method described in http://yieldprolog.sourceforge.net to Pharo Smalltalk ?
Are there analogous to the yield and generator functions exist in Smalltalk ?
For start, how should I rewrite code from Tutorial1 with message passing:
class UnifyingVariable:
def __init__(self):
self._isBound = False
def unify(self, arg):
if not self._isBound:
self._value = arg
self._isBound = True
yield False
# Remove the binding.
self._isBound = False
elif self._value == arg:
yield False
def personWithUnify(Person):
for l1 in Person.unify("Chelsea"):
yield False
for l1 in Person.unify("Hillary"):
yield False
for l1 in Person.unify("Bill"):
yield False
def main():
print("Names using UnifyingVariable:")
Person = UnifyingVariable()
for l1 in personWithUnify(Person):
print(Person._value)
Is it possible use single thread realisation, avoiding multithreading causes lot of complexity ?
Upvotes: 1
Views: 321
Reputation: 4633
Squeak Smalltalk has a Generator
class with a yield:
method (and if Pharo didn't remove it then it should still be there).
In Smalltalk, yield
is not a keyword as in other languages, but implemented in Smalltalk itself. It uses a single thread with co-routines (manipulating the execution context).
Here's the first example from YieldProlog:
personWithReturnValue := Generator on: [ :g |
g yield: 'Chelsea'.
g yield: 'Hillary'.
g yield: 'Bill' ].
Transcript cr; show: 'Names using a return value:'.
personWithReturnValue do: [ :p |
Transcript cr; show: p ].
As long as the Generator is side-effect free, it can be used exactly as in other languages.
However, YieldProlog relies on side-effects when unifying its variables. The difference is that in Squeak the code up to the next yield:
is executed immediately. That is, when creating the Generator, all the code up to the first yield:
gets executed. On the first next
call it executes all the code up to the second yield:
, but answers the value of the first yield. And so on. This is so the Generator can detect when to stop executing (because it is a subclass of Stream
and needs to support its atEnd
interface).
This means that you would have to put the side-effects after the yield. E.g. for the second YieldProlog example this works:
Object subclass: #SimpleVariable
instanceVariableNames: '_value'
classVariableNames: ''
poolDictionaries: ''
category: 'YieldProlog'.
SimpleVariable createInstVarAccessors.
personWithSimpleVariable := [ :person |
Generator on: [ :g |
g yield: false.
person _value: 'Chelsea'.
g yield: false.
person _value: 'Hillary'.
g yield: false.
person _value: 'Bill' ] ].
Transcript cr; show: 'Names using a SimpleVariable:'.
p := SimpleVariable new.
(personWithSimpleVariable value: p) do: [ :l1 |
Transcript cr; show: p _value ].
but in general this makes it hard to implement YieldProlog properly. Now, since Generator
is implemented in Smalltalk, it's relatively easy to fix this, too. See the changeset posted at http://forum.world.st/Generators-td4941886.html
With that change, the UnifyingVariable example works:
Object subclass: #UnifyingVariable
instanceVariableNames: '_value _isBound'
classVariableNames: ''
poolDictionaries: ''
category: 'YieldProlog'.
UnifyingVariable createInstVarAccessors.
UnifyingVariable compile: 'unify: arg
^Generator on: [ :g |
_isBound = true
ifFalse: [
_value := arg.
_isBound := true.
g yield: false.
"Remove the binding".
_isBound := false ]
ifTrue: [
_value = arg
ifTrue: [ g yield: false ] ] ]'.
personWithUnify := [ :person |
Generator on: [:g |
(person unify: 'Chelsea') do: [ :l1 |
g yield: false ].
(person unify: 'Hillary') do: [ :l1 |
g yield: false ].
(person unify: 'Bill') do: [ :l1 |
g yield: false ] ] ].
Transcript cr; show: 'Names using a UnifyingVariable:'.
person := UnifyingVariable new.
(personWithUnify value: person) do: [ :l1 |
Transcript cr; show: person _value ].
Transcript cr; show: 'Use unify to check a person:'.
person := UnifyingVariable new.
(person unify: 'Hillary') do: [ :l1 |
(personWithUnify value: person) do: [ :l2 |
Transcript cr; show: 'Hillary is a person.' ] ].
(person unify: 'Buddy') do: [ :l1 |
(personWithUnify value: person) do: [ :l2 |
"This won't print."
Transcript cr; show: 'Buddy is a person.' ] ].
All this being said, I doubt that this YieldProlog implementation is as efficient as the actual Prolog for Squeak. You should have a look at that: http://www.zogotounga.net/comp/squeak/prolog.htm
Upvotes: 4