[ANN] LambdaMessageSend on SqueakMap
Stéphane Rollandin
lecteur at zogotounga.net
Sun May 14 20:01:33 UTC 2006
stupid me :)
please ignore the attached file from my previous post. here is the good
one !
cheers,
Stef
-------------- next part --------------
SystemOrganization addCategory: #FunctionalTalk!
SystemOrganization addCategory: #'FunctionalTalk-Tests'!
TestCase subclass: #FunctionalTalkTest
instanceVariableNames: ''
classVariableNames: ''
poolDictionaries: ''
category: 'FunctionalTalk-Tests'!
Object subclass: #Lambda
instanceVariableNames: ''
classVariableNames: ''
poolDictionaries: ''
category: 'FunctionalTalk'!
MessageSend subclass: #LambdaMessageSend
instanceVariableNames: 'compiledForm'
classVariableNames: ''
poolDictionaries: ''
category: 'FunctionalTalk'!
LambdaMessageSend subclass: #AtomicLambdaMessageSend
instanceVariableNames: ''
classVariableNames: ''
poolDictionaries: ''
category: 'FunctionalTalk'!
Object subclass: #LambdaSlot
instanceVariableNames: 'id'
classVariableNames: ''
poolDictionaries: ''
category: 'FunctionalTalk'!
FunctionalTalkTest class
instanceVariableNames: ''!
Lambda class
instanceVariableNames: ''!
LambdaMessageSend class
instanceVariableNames: ''!
AtomicLambdaMessageSend class
instanceVariableNames: ''!
LambdaSlot class
instanceVariableNames: ''!
!AtomicLambdaMessageSend commentStamp: 'spfa 5/14/2006 13:22' prior: 0!
Wrapper for a LambdaSlot, e.g. function identity!
]style[(48)f1!
!FunctionalTalkTest commentStamp: 'spfa 5/10/2006 16:26' prior: 0!
Unit tests for the package FunctionalTalk.
See the tests classified "documentation" for an overview of the package.!
!Lambda commentStamp: 'spfa 5/14/2006 14:32' prior: 0!
This class simply provides syntactic sugar for lambda expressions.
| f |
"1) compact syntax for AtomicLambdaMessageSend creation"
f _ Lambda x + (Lambda y * 5) sqrt. "printIt"
"2) compact syntax for 'section' (Haskell vocabulary)"
f <~~ {Lambda . 1} "printIt"!
!LambdaMessageSend commentStamp: 'spfa 5/11/2006 18:13' prior: 0!
a MessageSend suitable for functional programming:
its receiver, selector and arguments can themselves be LambdaMessageSends.
see the 'documentation' test cases in FunctionalTalkTest for a comprehensive overview of this topic
!
!LambdaSlot commentStamp: 'spfa 5/10/2006 16:23' prior: 0!
A slot in a lambda construct.
see LambdaMessageSend for details!
!LambdaMessageSend methodsFor: 'easy arithmetic' stamp: 'spfa 5/14/2006 11:09'!
adaptToNumber: rcvr andSend: sel
"allows things like (1 + Lambda x)"
^ (rcvr lambda: sel) substitute: {self}! !
!LambdaMessageSend methodsFor: 'evaluating' stamp: 'spfa 5/10/2006 15:36'!
argument: anObject
super arguments: {anObject}! !
!LambdaMessageSend methodsFor: 'conversion' stamp: 'spfa 5/11/2006 22:03'!
asBlock
| slots |
^ Compiler evaluate: (String streamContents: [:str |
str nextPut: $[.
(slots _ self rootSlots) do: [:arg |
str nextPut: $:.
arg printAsCodeOn: str.
str space].
slots size isZero ifFalse: [str nextPut: $|; space].
self printAsCodeOn: str.
str nextPut: $]])
! !
!LambdaSlot methodsFor: 'printing' stamp: 'spfa 5/10/2006 13:52'!
asVariableName
^ String streamContents: [:str | self printAsCodeOn: str]
! !
!LambdaMessageSend methodsFor: 'compilation' stamp: 'spfa 5/13/2006 18:16'!
beFast
"allow internal compilation"
compiledForm _ nil.
self compileAsBlock.! !
!LambdaMessageSend methodsFor: 'compilation' stamp: 'spfa 5/13/2006 00:09'!
bePure
"do not allow internal compilation"
compiledForm _ #not
! !
!LambdaMessageSend methodsFor: 'reducting' stamp: 'spfa 5/11/2006 08:41'!
collectArgumentsForAll: anArray from: aStream
^ anArray collect: [:each | self collectArgumentsFor: each from: aStream ]! !
!LambdaMessageSend methodsFor: 'reducting' stamp: 'spfa 5/11/2006 15:35'!
collectArgumentsFor: anObject from: aStream
^ anObject isLambda
ifFalse: [anObject]
ifTrue: [anObject reduceWithAll: (aStream next: anObject slots size)]! !
!LambdaMessageSend methodsFor: 'compilation' stamp: 'spfa 5/12/2006 14:25'!
compileAsBlock
compiledForm == #not ifTrue: [^ self].
compiledForm _ self asBlock.
! !
!LambdaMessageSend methodsFor: 'compilation' stamp: 'spfa 5/11/2006 15:40'!
compileAs: aSelector in: aClass classified: heading
aClass class compile:
(String streamContents: [:code |
self rootSlots with: aSelector keywords do: [:arg :word |
code nextPutAll: word.
code space.
arg printAsCodeOn: code.
code space].
code cr; cr; tab; nextPutAll: '^ '.
self printAsCodeOn: code.])
classified: heading.
! !
!LambdaMessageSend methodsFor: 'compilation' stamp: 'spfa 5/13/2006 18:26'!
compiled
self isPure ifTrue: [^ self asBlock].
compiledForm ifNil: [self compileAsBlock].
^ compiledForm
! !
!LambdaMessageSend methodsFor: 'compilation' stamp: 'spfa 5/11/2006 09:23'!
compiledForm
^ compiledForm ! !
!LambdaMessageSend methodsFor: 'compilation' stamp: 'spfa 5/11/2006 15:40'!
compileIn: aClass classified: heading
self compileAs: (String streamContents: [:sel |
self rootSlots do: [:slot |
slot printAsCodeOn: sel.
sel nextPut: $:]])
in: aClass classified: heading
! !
!AtomicLambdaMessageSend methodsFor: 'reducting' stamp: 'spfa 5/14/2006 13:49'!
composeWith: aMessageSend
^ aMessageSend substitute: {receiver}! !
!LambdaMessageSend methodsFor: 'composition' stamp: 'spfa 5/11/2006 19:23'!
composeWith: aMessageSend
^ aMessageSend substitute: {self}! !
!LambdaMessageSend methodsFor: 'printing' stamp: 'spfa 5/13/2006 20:55'!
constructor
^ String streamContents: [:stream | self writeConstructorOn: stream]! !
!LambdaMessageSend methodsFor: 'reducting' stamp: 'spfa 5/10/2006 23:14'!
copyKeepSlots: slots
^ self shallowCopy postCopyKeepSlots: slots
! !
!LambdaSlot methodsFor: 'reduction' stamp: 'spfa 5/10/2006 23:15'!
copyKeepSlots: slots
(slots identityIncludes: self)
ifFalse: [^ self copy]! !
!LambdaSlot methodsFor: 'printing' stamp: 'spfa 5/13/2006 20:57'!
declareOn: stream
"specific usage. see #declareOn: "
self printAsCodeOn: stream .
stream nextPutAll: ' _ Lambda '.
self printAsCodeOn: stream .
stream nextPut: $.; space
! !
!LambdaMessageSend methodsFor: 'compilation' stamp: 'spfa 5/12/2006 14:28'!
deepCopy
self eraseCompiledForm.
^super deepCopy
! !
!AtomicLambdaMessageSend methodsFor: 'printing' stamp: 'spfa 5/14/2006 13:45'!
doesNotUnderstand: aMessage
^ LambdaMessageSend
receiver: receiver
selector: aMessage selector
arguments: aMessage arguments! !
!Lambda class methodsFor: 'as yet unclassified' stamp: 'spfa 5/14/2006 12:43'!
doesNotUnderstand: aMessage
aMessage selector numArgs isZero
ifTrue: [^ AtomicLambdaMessageSend receiver:
(LambdaSlot new id: aMessage selector)].
^ super doesNotUnderstand: aMessage
! !
!LambdaMessageSend methodsFor: 'composition' stamp: 'spfa 5/13/2006 20:23'!
doesNotUnderstand: aMessage
^ self class
receiver: (self copyKeepSlots: self rootSlots)
selector: aMessage selector
arguments: aMessage arguments! !
!LambdaSlot class methodsFor: 'API' stamp: 'spfa 5/9/2006 22:39'!
doesNotUnderstand: aMessage
^ self selector: aMessage selector arguments: aMessage arguments! !
!LambdaMessageSend methodsFor: 'compilation' stamp: 'spfa 5/11/2006 19:49'!
ensureSlotsArityFromIds
self slots do: [:s1 |
self slots do: [:s2 |
((s1 == s2) not and: [s1 id = s2 id])
ifTrue: [s1 becomeForward: s2]]].! !
!LambdaMessageSend methodsFor: 'compilation' stamp: 'spfa 5/11/2006 19:50'!
ensureUnambigousSlotIds
self slots do: [:s1 |
self slots do: [:s2 |
((s1 == s2) not and: [s1 id = s2 id])
ifTrue: [s1 getNewId]]].! !
!LambdaMessageSend methodsFor: 'compilation' stamp: 'spfa 5/12/2006 14:27'!
eraseCompiledForm
compiledForm isBlock ifTrue: [compiledForm _ nil]
! !
!LambdaSlot methodsFor: 'accessing' stamp: 'spfa 5/10/2006 22:26'!
getNewId
self id: (id isNumber ifTrue: [id +1]
ifFalse: [id isString ifTrue: [id, 'z'] ifFalse: [1]])
! !
!LambdaMessageSend methodsFor: 'compilation' stamp: 'spfa 5/12/2006 14:26'!
hasCompiledForm
^ compiledForm notNil and: [compiledForm isBlock]
! !
!LambdaSlot methodsFor: 'accessing' stamp: 'spfa 5/9/2006 22:34'!
id
^ id ! !
!LambdaSlot methodsFor: 'accessing' stamp: 'spfa 5/9/2006 14:43'!
id: anObject
id _ anObject! !
!LambdaSlot class methodsFor: 'instance creation' stamp: 'spfa 5/9/2006 14:45'!
id: anObject
^ self new id: anObject! !
!LambdaMessageSend methodsFor: 'compilation' stamp: 'spfa 5/13/2006 18:13'!
initialize
super initialize.
self bePure
! !
!AtomicLambdaMessageSend methodsFor: 'testing' stamp: 'spfa 5/14/2006 13:54'!
isAtomicLambda
^ true! !
!LambdaMessageSend methodsFor: 'testing' stamp: 'spfa 5/14/2006 13:55'!
isAtomicLambda
^ false! !
!LambdaSlot methodsFor: 'testing' stamp: 'spfa 5/14/2006 13:58'!
isAtomicLambda
^ true! !
!AtomicLambdaMessageSend methodsFor: 'testing' stamp: 'spfa 5/14/2006 13:56'!
isLambda
^ true! !
!LambdaMessageSend methodsFor: 'testing' stamp: 'spfa 5/9/2006 23:46'!
isLambda
^ receiver isLambda
or: [selector isLambda]
or: [arguments isLambda]
or: [arguments anySatisfy: [:arg | arg isLambda]]! !
!LambdaSlot methodsFor: 'testing' stamp: 'spfa 5/14/2006 14:14'!
isLambda
^ true
! !
!Object methodsFor: '*FunctionalTalk' stamp: 'spfa 5/9/2006 10:23'!
isLambda
^ false! !
!LambdaSlot methodsFor: 'testing' stamp: 'spfa 5/11/2006 14:02'!
isLambdaSlot
^ true
! !
!Object methodsFor: '*FunctionalTalk' stamp: 'spfa 5/11/2006 14:03'!
isLambdaSlot
^ false! !
!LambdaMessageSend methodsFor: 'compilation' stamp: 'spfa 5/12/2006 23:51'!
isPure
^ compiledForm == #not
! !
!LambdaMessageSend methodsFor: 'easy arithmetic' stamp: 'spfa 5/13/2006 22:01'!
isZero
"allows things like (1/(Lambda x + 1))"
^ false
! !
!Object methodsFor: '*FunctionalTalk-API' stamp: 'spfa 5/9/2006 20:52'!
lambda
^ LambdaMessageSend
receiver: self
selector: nil
arguments: #()
! !
!AtomicLambdaMessageSend methodsFor: 'reducting' stamp: 'spfa 5/14/2006 14:05'!
lambdaArity
^ 1! !
!LambdaMessageSend methodsFor: 'accessing' stamp: 'spfa 5/11/2006 21:25'!
lambdaArity
^ self slots asIdentitySet size! !
!Object methodsFor: '*FunctionalTalk' stamp: 'spfa 5/9/2006 13:13'!
lambdaArity
^ 0 ! !
!LambdaSlot class methodsFor: 'API' stamp: 'spfa 5/10/2006 10:00'!
lambdaMessage
^ (self id: 'receiver') lambdaMessage! !
!Object methodsFor: '*FunctionalTalk-API' stamp: 'spfa 5/10/2006 09:59'!
lambdaMessage
^ LambdaMessageSend
receiver: self
selector: (LambdaSlot id: 'selector')
arguments: (LambdaSlot id: 'arguments')
! !
!LambdaMessageSend methodsFor: 'evaluating' stamp: 'spfa 5/9/2006 11:46'!
lambdaValue
^ self value! !
!Object methodsFor: '*FunctionalTalk' stamp: 'spfa 5/9/2006 11:46'!
lambdaValue
^ self ! !
!LambdaSlot class methodsFor: 'API' stamp: 'spfa 5/10/2006 09:53'!
lambda: anObjectOrArray
^ (self id: 'receiver') lambda: anObjectOrArray! !
!Object methodsFor: '*FunctionalTalk-API' stamp: 'spfa 5/10/2006 09:53'!
lambda: aSymbolOrArray
aSymbolOrArray isSymbol
ifTrue:
[
^ LambdaMessageSend
receiver: self
selector: aSymbolOrArray
arguments: ((1 to: aSymbolOrArray numArgs)
collect: [:n | LambdaSlot id: n])
]
ifFalse: "Array, then"
[
^ LambdaMessageSend
receiver: self
selector: (LambdaSlot id: 'selector')
arguments: aSymbolOrArray
]
! !
!Object methodsFor: '*FunctionalTalk-API' stamp: 'spfa 5/9/2006 14:48'!
lambda: aSymbol slotIds: anArray
^ LambdaMessageSend
receiver: self
selector: aSymbol
arguments: (anArray collect: [:n | LambdaSlot id: n])
! !
!Object methodsFor: '*FunctionalTalk-API' stamp: 'spfa 5/9/2006 16:01'!
lambda: aSymbol slotId: anObject
aSymbol numArgs ~= 1 ifTrue: [^ self error: 'more than one slot defined'].
^ LambdaMessageSend
receiver: self
selector: aSymbol
arguments: {LambdaSlot id: anObject}
! !
!Object methodsFor: '*FunctionalTalk-API' stamp: 'spfa 5/9/2006 21:01'!
lambda: aSymbol with: anObject
^ LambdaMessageSend
receiver: self
selector: aSymbol
arguments: {anObject}
! !
!Object methodsFor: '*FunctionalTalk-API' stamp: 'spfa 5/9/2006 21:03'!
lambda: aSymbol with: anObject with: bObject
^ LambdaMessageSend
receiver: self
selector: aSymbol
arguments: {anObject . bObject}
! !
!Object methodsFor: '*FunctionalTalk-API' stamp: 'spfa 5/9/2006 21:03'!
lambda: aSymbol with: anObject with: bObject with: cObject
^ LambdaMessageSend
receiver: self
selector: aSymbol
arguments: {anObject . bObject . cObject}
! !
!LambdaMessageSend methodsFor: 'short API' stamp: 'spfa 5/11/2006 19:23'!
o: aMessageSend
^ self composeWith: aMessageSend
! !
!LambdaMessageSend methodsFor: 'compilation' stamp: 'spfa 5/12/2006 14:28'!
postCopy
self eraseCompiledForm.
receiver isLambda ifTrue: [receiver _ receiver copy].
selector isLambda ifTrue: [selector _ selector copy].
arguments _ arguments isLambda ifTrue: [arguments copy]
ifFalse: [arguments collect:
[:arg | arg isLambda ifTrue: [arg copy] ifFalse: [arg]]].
self ensureSlotsArityFromIds
! !
!LambdaMessageSend methodsFor: 'reducting' stamp: 'spfa 5/12/2006 14:28'!
postCopyKeepSlots: slots
self eraseCompiledForm.
receiver isLambda
ifTrue: [receiver _ receiver copyKeepSlots: slots].
selector isLambda
ifTrue: [selector _ selector copyKeepSlots: slots].
arguments _ arguments isLambda
ifTrue: [arguments copyKeepSlots: slots]
ifFalse: [arguments collect:
[:arg | arg isLambda
ifTrue: [arg copyKeepSlots: slots]
ifFalse: [arg]]].
self ensureSlotsArityFromIds! !
!LambdaMessageSend methodsFor: 'printing' stamp: 'spfa 5/10/2006 13:54'!
printAsCodeOn: stream
stream nextPut: $(.
receiver printAsCodeOn: stream.
selector ifNotNil: [
stream space.
(arguments isLambda not and: [arguments isEmpty]) ifTrue: [
selector isLambda ifTrue:[
stream nextPutAll: 'perform: '.
selector printAsCodeOn: stream]
ifFalse: [stream nextPutAll: selector]]
ifFalse: [
(selector isLambda or: [arguments isLambda]) ifTrue:[
stream nextPutAll: 'perform:'; space.
selector printAsCodeOn: stream.
stream space; nextPutAll: 'withArguments:'; space.
arguments isLambda
ifTrue:[arguments printAsCodeOn: stream]
ifFalse: [
stream nextPut: ${.
arguments do: [:arg |
arg printAsCodeOn: stream.
stream nextPutAll: ' . '].
stream skip: -3; nextPut: $}]]
ifFalse: [
arguments with: selector keywords do: [:arg :word |
stream nextPutAll: word.
stream space.
arg printAsCodeOn: stream.
stream space].
stream skip: -1]]].
stream nextPut: $)! !
!LambdaSlot methodsFor: 'printing' stamp: 'spfa 5/10/2006 13:52'!
printAsCodeOn: stream
| varname |
varname _ id asString.
(varname allSatisfy: [:char | char isAlphaNumeric])
ifFalse: [varname _ varname asIdentifier: false]
ifTrue: [varname startsWithDigit ifTrue: [stream nextPutAll: 'arg']].
stream nextPutAll: varname
! !
!Object methodsFor: '*FunctionalTalk' stamp: 'spfa 5/11/2006 23:31'!
printAsCodeOn: aStream
self storeOn: aStream! !
!AtomicLambdaMessageSend methodsFor: 'printing' stamp: 'spfa 5/14/2006 12:44'!
printOn: aStream
^ receiver printOn: aStream! !
!LambdaMessageSend methodsFor: 'printing' stamp: 'spfa 5/10/2006 10:15'!
printOn: stream
stream nextPut: $(.
receiver printOn: stream.
selector ifNotNil: [
stream space.
(arguments isLambda not and: [arguments isEmpty]) ifTrue: [
selector isLambda ifTrue:[
stream nextPutAll: 'perform: '.
selector printOn: stream]
ifFalse: [stream nextPutAll: selector]]
ifFalse: [
(selector isLambda or: [arguments isLambda]) ifTrue:[
stream nextPutAll: 'perform:'; space.
selector printOn: stream.
stream space; nextPutAll: 'withArguments:'; space.
arguments printOn: stream.]
ifFalse: [
arguments with: selector keywords do: [:arg :word |
stream nextPutAll: word.
stream space.
arg printOn: stream.
stream space].
stream skip: -1]]].
stream nextPut: $)
! !
!LambdaSlot methodsFor: 'printing' stamp: 'spfa 5/10/2006 16:21'!
printOn: stream
stream nextPut: $<.
stream nextPutAll: id asString.
stream nextPut: $>.
! !
!AtomicLambdaMessageSend class methodsFor: 'as yet unclassified' stamp: 'spfa 5/14/2006 12:42'!
receiver: aLambdaSlot
^ self receiver: aLambdaSlot selector: #yourself arguments: #()! !
!LambdaMessageSend class methodsFor: 'as yet unclassified' stamp: 'spfa 5/11/2006 19:50'!
receiver: anObject selector: aSymbol arguments: anArray
^ (super receiver: anObject selector: aSymbol arguments: anArray)
ensureUnambigousSlotIds compileAsBlock! !
!LambdaMessageSend methodsFor: 'reducting' stamp: 'spfa 5/11/2006 22:19'!
reduceWithAllNonLambda: args
self hasCompiledForm ifFalse: [self compileAsBlock].
^ compiledForm valueWithArguments: args
! !
!AtomicLambdaMessageSend methodsFor: 'reducting' stamp: 'spfa 5/14/2006 13:47'!
reduceWithAll: args
| arg |
arg _ args first.
arg == Lambda ifTrue: [^ self].
arg isLambdaSlot ifTrue: [^ self class receiver: arg].
^ arg ! !
!LambdaMessageSend methodsFor: 'reducting' stamp: 'spfa 5/14/2006 13:41'!
reduceWithAll: args
| val |
self isPure ifFalse:
[
self hasCompiledForm ifFalse: [self compileAsBlock].
((args size = self lambdaArity)
and: [args noneSatisfy: [:arg | arg isLambda or: [arg == Lambda]]])
ifTrue: [^ compiledForm valueWithArguments: args]
].
val _ self substitute: args.
val isLambda ifTrue: [^ val compileAsBlock] ifFalse: [^ val value]
! !
!LambdaSlot methodsFor: 'reduction' stamp: 'spfa 5/14/2006 14:18'!
reduceWithAll: args
| x |
args size ~= 1 ifTrue: [self error: 'Bad number of arguments'].
((x _ args first) == Lambda
or: [x isLambdaSlot and: [x id isNil]]) ifTrue: [^ self].
^ x
! !
!LambdaMessageSend methodsFor: 'reducting' stamp: 'spfa 5/11/2006 10:17'!
reduceWithNonLambda: arg
^ self reduceWithAllNonLambda: {arg}
! !
!LambdaMessageSend methodsFor: 'reducting' stamp: 'spfa 5/12/2006 10:43'!
reduceWithSlots: anArrayOfAssociations
^ self reduceWithAll:
(self rootSlots collect: [:lambda |
lambda reduceWithSlots: anArrayOfAssociations])
! !
!LambdaSlot methodsFor: 'reduction' stamp: 'spfa 5/12/2006 10:43'!
reduceWithSlots: anArrayOfAssociation
^ (anArrayOfAssociation
detect: [:assoc | (id = assoc key)] ifNone: [^ self]) value
! !
!LambdaMessageSend methodsFor: 'reducting' stamp: 'spfa 5/12/2006 10:43'!
reduceWithSlot: anAssociation
^ self reduceWithAll:
(self rootSlots collect: [:lambda |
lambda reduceWithSlot: anAssociation])
! !
!LambdaSlot methodsFor: 'reduction' stamp: 'spfa 5/12/2006 10:42'!
reduceWithSlot: anAssociation
(id = anAssociation key) ifTrue: [^ anAssociation value]
! !
!LambdaMessageSend methodsFor: 'reducting' stamp: 'spfa 5/9/2006 14:18'!
reduceWith: arg
^ self reduceWithAll: {arg}
! !
!LambdaMessageSend methodsFor: 'accessing' stamp: 'spfa 5/11/2006 15:39'!
rootSlots
^ self slots inject: #() into: [:uslots :s |
(uslots identityIncludes: s) ifTrue: [uslots] ifFalse: [uslots, {s}]]! !
!LambdaSlot class methodsFor: 'API' stamp: 'spfa 5/9/2006 14:39'!
selector: aSymbol
^ self selector: aSymbol arguments: #()! !
!LambdaSlot class methodsFor: 'API' stamp: 'spfa 5/10/2006 09:53'!
selector: aSymbol arguments: anArray
^ self selector: aSymbol arguments: anArray slotId: 'receiver'! !
!LambdaSlot class methodsFor: 'API' stamp: 'spfa 5/9/2006 22:39'!
selector: aSymbol arguments: anArray slotId: anObject
^ LambdaMessageSend receiver: (LambdaSlot id: anObject) selector: aSymbol arguments: anArray! !
!LambdaSlot class methodsFor: 'API' stamp: 'spfa 5/9/2006 14:39'!
selector: aSymbol argument: anObject
^ self selector: aSymbol arguments: {anObject}! !
!LambdaSlot class methodsFor: 'API' stamp: 'spfa 5/9/2006 22:40'!
selector: aSymbol argument: anObject slotId: bObject
^ self selector: aSymbol arguments: {anObject} slotId: bObject! !
!LambdaSlot class methodsFor: 'API' stamp: 'spfa 5/9/2006 22:41'!
selector: aSymbol slotId: bObject
^ self selector: aSymbol arguments: #() slotId: bObject! !
!LambdaMessageSend methodsFor: 'compilation' stamp: 'spfa 5/11/2006 15:40'!
slotIds
^ self rootSlots collect: [:slot | slot id].! !
!LambdaMessageSend methodsFor: 'compilation' stamp: 'spfa 5/11/2006 15:40'!
slotIds: anArray
self rootSlots with: anArray do: [:slot :id | slot id: id].! !
!LambdaMessageSend methodsFor: 'compilation' stamp: 'spfa 5/11/2006 22:21'!
slotId: anObject newId: bObject
(self rootSlots detect: [:slot | slot id = anObject] ifNone: [^ self])
id: bObject! !
!LambdaMessageSend methodsFor: 'accessing' stamp: 'spfa 5/11/2006 15:34'!
slots
^ Array streamContents: [:str |
receiver isLambda ifTrue: [str nextPutAll: receiver slots].
selector isLambda ifTrue: [str nextPutAll: selector slots].
arguments isLambda ifTrue: [str nextPutAll: arguments slots]
ifFalse: [arguments do: [:a |
a isLambda ifTrue: [str nextPutAll: a slots]]]]
! !
!LambdaSlot methodsFor: 'accessing' stamp: 'spfa 5/11/2006 15:35'!
slots
^ {self}! !
!LambdaMessageSend methodsFor: 'compilation' stamp: 'spfa 5/12/2006 14:28'!
storeDataOn: aDataStream
self eraseCompiledForm.
super storeDataOn: aDataStream.
self compileAsBlock
! !
!AtomicLambdaMessageSend methodsFor: 'printing' stamp: 'spfa 5/14/2006 12:44'!
storeOn: aStream
^ receiver storeOn: aStream! !
!LambdaMessageSend methodsFor: 'printing' stamp: 'spfa 5/13/2006 20:59'!
storeOn: stream
"specific usage - should be renamed I guess"
stream nextPut: $(.
receiver storeOn: stream.
selector ifNotNil: [
stream space.
(arguments isLambda not and: [arguments isEmpty]) ifTrue: [
selector isLambda ifTrue:[
stream nextPutAll: 'perform: '.
selector storeOn: stream]
ifFalse: [stream nextPutAll: selector]]
ifFalse: [
(selector isLambda or: [arguments isLambda]) ifTrue:[
stream nextPutAll: 'perform:'; space.
selector storeOn: stream.
stream space; nextPutAll: 'withArguments:'; space.
arguments isLambda
ifTrue:[arguments storeOn: stream]
ifFalse: [
stream nextPut: ${.
arguments do: [:arg |
arg storeOn: stream.
stream nextPutAll: ' . '].
stream skip: -3; nextPut: $}]]
ifFalse: [
arguments with: selector keywords do: [:arg :word |
stream nextPutAll: word.
stream space.
arg storeOn: stream.
stream space].
stream skip: -1]]].
stream nextPut: $)! !
!LambdaSlot methodsFor: 'printing' stamp: 'spfa 5/13/2006 20:46'!
storeOn: stream
"specific usage. see #declareOn: "
self printAsCodeOn: stream
! !
!Rectangle methodsFor: '*FunctionalTalk-unrelated-fix' stamp: 'spfa 5/12/2006 14:16'!
storeOn: aStream
aStream nextPut: $(.
self printOn: aStream.
aStream nextPut: $)! !
!AtomicLambdaMessageSend methodsFor: 'reducting' stamp: 'spfa 5/14/2006 13:46'!
substitute: args
^ self reduceWithAll: args! !
!LambdaMessageSend methodsFor: 'reducting' stamp: 'spfa 5/14/2006 13:52'!
substitute: args
| argsSlots fullArgs mySlots stream |
mySlots _ self slots.
args size = mySlots size
ifTrue: [
"this is NOT a public API ; used internally by #collectArgumentsFor:from:"
fullArgs _ args]
ifFalse: [
| size argsStream z |
args size > (size _ mySlots asIdentitySet size) "self lambdaArity"
ifTrue: [self error: 'Too many arguments'].
argsStream _ ReadStream on:
(args forceTo: size paddingWith: Lambda).
z _ 'zzzzdoing'.
fullArgs _ Array new: mySlots size withAll: z.
mySlots doWithIndex: [:s :i |
| a |
(fullArgs at: i) == z ifTrue: [
fullArgs at: i put: (a _ argsStream next).
mySlots doWithIndex: [:ss :j |
(ss == s and: [j > i])
ifTrue: [fullArgs at: j put: a]]]]].
stream _ ReadStream on: fullArgs.
argsSlots _ Array streamContents: [:str |
(fullArgs select: [:arg | arg isLambda])
do: [:arg | str nextPutAll: arg slots]].
^ ((self copyKeepSlots: argsSlots)
receiver: (self collectArgumentsFor: receiver from: stream);
selector: (self collectArgumentsFor: selector from: stream);
arguments: (arguments isLambda
ifTrue: [arguments reduceWithAll: stream upToEnd]
ifFalse: [self collectArgumentsForAll: arguments from: stream]))
ensureUnambigousSlotIds; compileAsBlock
! !
!FunctionalTalkTest methodsFor: 'tests' stamp: 'spfa 5/13/2006 22:17'!
testArithmetic
self assert: (2 + Lambda x) isLambda.
self assert: (1/(Lambda x + 1)) isLambda.
self assert: (1/Lambda x) isLambda! !
!FunctionalTalkTest methodsFor: 'tests' stamp: 'spfa 5/11/2006 18:38'!
testArity1
| f g f2 ff sumf |
f _ 56 lambda: #+ .
g _ f sqrt.
f2 _ f * 2.
ff _ f * f.
sumf _ ff + f2 + g.
self assert: (sumf <~ 8) = 4232.
self assert: (f <~ 4) = 60.
self assert: (g <~ 8) = 8.
self assert: (f2 <~ 4) = 120.
self assert: (ff <~ 4) = 3600.
! !
!FunctionalTalkTest methodsFor: 'tests' stamp: 'spfa 5/11/2006 18:35'!
testArity2
| f t |
f _ 56 lambda: #+.
t _ (f sqrt) lambda: #+.
self assert: t lambdaArity = 2.
self assert: (t <~~ {8 . 2}) = 10.
self assert: (t+f) lambdaArity = 2.
self assert: ((t + f) <~~ {8 . 2}) = 74.
! !
!FunctionalTalkTest methodsFor: 'tests' stamp: 'spfa 5/11/2006 18:50'!
testArity3
| f g t h |
f _ 56 lambda: #+.
g _ 10 lambda: #-.
t _ f + g.
self assert: t lambdaArity = 2.
self assert: (t <~~ {8 . 8}) = 66.
h _ Point lambda: #x:y:.
t _ h r.
self assert: (t <~ 0 <~ 1) = 1.
self assert: ((f + t) <~ 4 <~ 10 <~ 0) = 70
! !
!FunctionalTalkTest methodsFor: 'tests' stamp: 'spfa 5/11/2006 18:37'!
testArityPreservation
| f t g |
f _ LambdaSlot selector: #+ arguments: {56}.
t _ (f sqrt) + 2.
g _ ((f sqrt) + 2) * f.
self assert: ((t <~ 8) closeTo: 10).
self assert: ((g <~ 8) closeTo: 640).
self assert: ((t <~ -56) closeTo: 2).
self assert: ((g <~ -55) closeTo: 3).
! !
!FunctionalTalkTest methodsFor: 'documentation' stamp: 'spfa 5/11/2006 18:54'!
testBasics
"defining a message send from an arbitrary object:"
self assert: 5 lambda isMessageSend.
self assert: 5 lambda value = 5.
"defining a message send from an object and message:"
self assert: (4 lambda + 1) isMessageSend.
self assert: (4 lambda + 1) value = 5.
"defining a lambda expression from an object and selector only"
self assert: (4 lambda: #+) isLambda.
"this can not be reduced: it is not a plain message send"
self assert: (4 lambda: #+) value isLambda.
"an argument can be provided:"
self assert: ((4 lambda: #+) argument: 5) isMessageSend.
"this can now be reduced:"
self assert: ((4 lambda: #+) argument: 5) value = 9 .
"note that the argument can itself be a lambda expression:"
self assert: ((4 lambda: #+) argument: (2 lambda: #+)) isLambda.
"giving an argument and getting the corresponding value is called reduction. this can be done in one step"
self assert: ((4 lambda: #+) reduceWith: 5) = 9 .
"shorter API :"
self assert: ((4 lambda: #+) <~ 5) = 9 .
"giving an argument without evaluating is called substitution"
self assert: ((4 lambda: #+) substitute: {5}) printString = '(4 + 5)' .
"there is also a shorter API :"
self assert: ((4 lambda: #+) <@ 5) printString = '(4 + 5)' .! !
!FunctionalTalkTest methodsFor: 'documentation' stamp: 'spfa 5/11/2006 19:04'!
testBasics2
| f g r |
f _ 4 lambda: #+.
"a lambda expression can be expanded by sending it any messages"
g _ (f + 10) * 2.
self assert: g isLambda.
self assert: (g <~ 1) = 30.
"it can be composed with another lambda expression"
self assert: (f ~> g) isLambda.
self assert: ((f ~> g) <~ 1) = 38.
"actually ~> is just <~ the other way round"
self assert: (g <~ f <~ 1) = 38.
self assert: (1 ~> f ~> g) = 38.
self assert: (f ~> g <~ 1) = 38.
"to see what (f ~> g) really is, print it"
self assert: (f ~> g) printString = '(((4 + (4 + <1>)) + 10) * 2)'.
self assert: f printString = '(4 + <1>)'.
self assert: g printString = '(((4 + <1>) + 10) * 2)'.
self assert: (g ~> f) printString = '(4 + (((4 + <1>) + 10) * 2))'.
"<1>, <2>, .. are the default labels (id) for lambda slots
it is possible to define arbitrary ids"
f _ 4 lambda: #+ slotId: 'étonnant, non ?'.
self assert: f printString = '(4 + <étonnant, non ?>)'.
f _ 4 lambda: #+ slotId: Smalltalk.
self assert: f printString = '(4 + <a SystemDictionary(lots of globals)>)'.
"this is especially useful for expressions with several arguments"
r _ Rectangle lambda: #left:right:top:bottom:
slotIds: #('left' 'right' 'top' 'bottom').
self assert: r printString =
'(Rectangle left: <left> right: <right> top: <top> bottom: <bottom>)'.
"we can reduce the number of arguments by providing values for specific slots, using slot ids"
r _ r reduceWithSlots: {'left' -> 50 . 'right' -> 60}.
self assert:
r <~~ {0 . 100} = (Rectangle left: 50 right: 60 top: 0 bottom: 100).
self assert:
r printString = '(Rectangle left: 50 right: 60 top: <top> bottom: <bottom>)' .
"we can substitute a lambda expression in a slot"
r _ r reduceWithSlot: ('bottom' -> (100 lambda: #- slotId: 'bottom offset')).
self assert:
r slotIds = #('top' 'bottom offset').
self assert:
r <~~ {0 . 0} = (Rectangle left: 50 right: 60 top: 0 bottom: 100).
self assert: r printString =
'(Rectangle left: 50 right: 60 top: <top> bottom: (100 - <bottom offset>))' .
"<<~ and <<~~ are shortcuts for reduceWithSlot: and reduceWithSlots:
you can also use the backward versions ~>> and ~~>>"
r _ Rectangle lambda: #left:right:top:bottom:
slotIds: #('left' 'right' 'top' 'bottom').
r _ r <<~ ('right' -> 60).
r _ {'left' -> 50 . 'bottom' -> (100 lambda: #-)} ~~>> r.
self assert:
{0 . 0} ~~> r = (Rectangle left: 50 right: 60 top: 0 bottom: 100).
! !
!FunctionalTalkTest methodsFor: 'documentation' stamp: 'spfa 5/10/2006 16:38'!
testBasics3
| f g h k j m |
"lambda abstraction can be based on the receiver of an expression"
f _ LambdaSlot + 5.
self assert: (f <~ 1) = 6.
self assert: f printString = '(<receiver> + 5)'.
"we can also abstract both receiver and arguments at the same time"
g _ (LambdaSlot lambda: #+) .
self assert: (g <~~ {2 . 3}) = 5.
self assert: g printString = '(<receiver> + <1>)'.
"this is equivalent to"
g _ LambdaMessageSend
receiver: (LambdaSlot id: 'receiver')
selector: #+
arguments: {LambdaSlot id: 'argument'}.
self assert: (g <~~ {2 . 3}) = 5.
"the selector can be abstracted too..."
h _ 5 lambda: {5}.
self assert: (h <~ #+) = 10.
self assert: (h <~ #*) = 25.
self assert: h printString = '(5 perform: <selector> withArguments: #(5))'.
"abstracting both receiver and selector"
j _ (LambdaSlot lambda: {5}) .
self assert: (j <~~ {2 . #+}) = 7.
self assert: (j <~~ {1 . #-}) = -4.
self assert: j printString =
'(<receiver> perform: <selector> withArguments: #(5))'.
"abstracting both selector and arguments"
m _ 5 lambdaMessage.
self assert: (m <~~ {#+ . {3}}) = 8.
self assert: (m <~~ {#raisedTo: . {2}}) = 25.
self assert: (m <~~ {#adaptToFraction:andSend: . {1/2 . #+}}) = (11/2).
self assert: m printString =
'(5 perform: <selector> withArguments: <arguments>)'.
"here is the full monty"
k _ LambdaSlot lambdaMessage.
self assert: (k <~~ {2 . #+ . {3}}) = 5.
self assert: (k <~~ {'hello' . #padded:to:with: . {#right . 10 . $!!}}) = 'hello!!!!!!!!!!'.
self assert: k printString =
'(<receiver> perform: <selector> withArguments: <arguments>)'.
"this is equivalent to"
k _ LambdaMessageSend
receiver: (LambdaSlot id: 'receiver')
selector: (LambdaSlot id: 'selector')
arguments: (LambdaSlot id: 'arguments').
self assert: (k <~~ {2 . #+ . {3}}) = 5.
! !
!FunctionalTalkTest methodsFor: 'documentation' stamp: 'spfa 5/11/2006 19:08'!
testBasics4
| x f g h z p |
"slots are shared by default when combining functions"
x _ Lambda x.
f _ x * (1+x).
self assert: f lambdaArity = 1. "this means the two x above will take the same one argument"
self assert: f slots printString= '#(<x> <x>)'.
self assert: f rootSlots printString= '#(<x>)'.
self assert: 10 ~> f = 110.
"slots are not shared when copying"
g _ x * (1+ x copy).
self assert: g lambdaArity = 2. "theres is now TWO arguments"
self assert: g slots printString = '#(<xz> <x>)'.
self assert: g rootSlots printString= '#(<xz> <x>)'.
self assert: {10 . 2} ~~> g = 30.
"slots built independently stay independent"
h _ Lambda y * (1+ Lambda y).
self assert: h lambdaArity = 2. "the two Lambda y define different slots"
self assert: h slots printString= '#(<yz> <y>)'.
self assert: h rootSlots printString= '#(<yz> <y>)'.
self assert: {10 . 2} ~~> h = 30.
"note that it is always possible to redefine the slots and change the arity"
z _ Lambda z.
p _ {z . z} ~~> h.
self assert: p lambdaArity = 1.
self assert: p slots printString = '#(<z> <z>)'.
self assert: p rootSlots printString = '#(<z>)'.
self assert: 10 ~> p = 110.
! !
!FunctionalTalkTest methodsFor: 'documentation' stamp: 'spfa 5/13/2006 19:56'!
testBenchmarks
| bk x y f tblock fassert t |
x _ Lambda x.
y _ Lambda y.
f _ ((x*x) + (y*y)) sqrt.
f beFast. "we allow internal compilation"
bk _ [:a :b | ((a*a) + (b*b)) sqrt].
"tblock is the duration taken by bk to evaluate 100000 times:"
tblock _ [100000 timesRepeat: [bk value: 3 value: 4]] durationToRun.
"fassert is a lambda function which compare a duration t with tblock
it succeeds if t is very roughly the same as tblock "
t _ Lambda t.
fassert _ self lambda: #assert: with: ((t * 1.4) > tblock) & ((t * 0.7) < tblock).
"
NOTE:
in the following we replace all the usual 'self assert: ...' with invocation of our specialized fassert. this will provide an example of functional programming along with our benchmarks :)
now let's proceed:
"
"the default way to reduce a lambda expression is VERY slow:
here it is about 5000 times slower than its equivalent block
(although this does not make much sense as both do not perform the same operation at all)"
fassert <~ [20 timesRepeat: [f <~~ {3 . Lambda}]] durationToRun.
"now if we are not actually substituting but simply evaluating the lambda expression, it gets better.
here we perform exactly the same operation as the block: it is roughly 50 times slower"
fassert <~ [2000 timesRepeat: [f <~~ {3 . 4}]] durationToRun.
"if the function is to be evaluated with plain, non-lambda arguments, as in our example, then it is much better to use //> instead of ~~> because some expensive checks gets disabled.
we are now only 2 times slower than the block"
fassert <~ [50000 timesRepeat: [f <// {3 . 4}]] durationToRun.
"even faster is the direct invocation of the compiled form of the function (which is a block itself): we eventually catch up with the block"
fassert <~ [100000 timesRepeat: [f compiled value: 3 value: 4]] durationToRun. ! !
!FunctionalTalkTest methodsFor: 'documentation' stamp: 'spfa 5/11/2006 15:25'!
testBeware
"lambda expressions expand by receiving message.
they do not expand when sent to other objects"
self assert: ('abc' lambda, 'def') value = 'abcdef'.
self should: [('abc', 'def' lambda) value] raise: Error.
self assert: ('abc' lambda, 'def' lambda) value = 'abcdef'.
"as an exception, arithmetic operations do allow more freedom"
self assert: (77 + 5 lambda) isMessageSend.
self assert: (77 + 5 lambda) value = 82.
! !
!FunctionalTalkTest methodsFor: 'documentation' stamp: 'spfa 5/11/2006 19:10'!
testBeware2
| f g h |
"slots ids are changed silently if a name clash happen
this allows further compilation of the function as a method or a block, where different arguments with the same name would not be accepted"
f _ 5 lambda: #+ slotId: 'x'.
g _ 1 lambda: #- slotId: 'x'.
h _ f + g.
self assert: h lambdaArity = 2.
self assert: (h <~~ {5 . 1}) = 10.
self assert: h slotIds = {'xz' . 'x'}. "a 'z' was appended"
! !
!FunctionalTalkTest methodsFor: 'tests' stamp: 'spfa 5/11/2006 11:02'!
testCompilation
| cat |
cat _ '--test leftover (remove it all)--'.
((Rectangle lambda: #left:right:top:bottom:)
<<~~ {3 -> 500 . 4 -> 600})
slotIds: {'gauche' . 'droite' };
compileAs: #gauche:droite: in: Rectangle classified: cat.
self assert: (Rectangle gauche: 45 droite: 177) = (45 at 500 corner: 177 at 600).
Rectangle class removeCategory: cat! !
!FunctionalTalkTest methodsFor: 'tests' stamp: 'spfa 5/11/2006 11:03'!
testCompilation2
| cat |
cat _ '--test leftover (remove it all)--'.
((Rectangle lambda: #left:right:top:bottom:)
<<~~ {3 -> 500 . 4 -> 600})
slotIds: {'gauche' . 'droite' };
compileIn: Rectangle classified: cat.
self assert: (Rectangle gauche: 45 droite: 177) = (45 at 500 corner: 177 at 600).
Rectangle class removeCategory: cat! !
!FunctionalTalkTest methodsFor: 'tests' stamp: 'spfa 5/10/2006 15:06'!
testCompilation3
| g cat |
cat _ '--compiled example (remove it if you want)--'.
g _ LambdaMessageSend
receiver: (LambdaSlot id: 'receiver')
selector: (LambdaSlot id: 'selector')
arguments: {LambdaSlot id: 'argument'}.
g compileIn: LambdaSlot classified: cat.
self assert: (LambdaSlot receiver: 10 selector: #/ argument: 2) = 5.
LambdaSlot class removeCategory: cat! !
!FunctionalTalkTest methodsFor: 'tests' stamp: 'spfa 5/10/2006 15:06'!
testCompilation4
| cat |
cat _ '--compiled example (remove it if you want)--'.
LambdaSlot lambdaMessage compileIn: LambdaSlot classified: cat.
self assert: (LambdaSlot receiver: 10 selector: #/ arguments: {2}) = 5.
LambdaSlot class removeCategory: cat! !
!FunctionalTalkTest methodsFor: 'tests' stamp: 'spfa 5/13/2006 18:17'!
testCompiledForm
| f g morph |
f _ ((Lambda x raisedTo: 2) + (Lambda y raisedTo: 2)) sqrt.
f beFast.
self assert: f hasCompiledForm.
self assert: f copy hasCompiledForm not.
g _ f copy <<~ ('y' -> 0).
self assert: g hasCompiledForm.
self assert: (g <~ 2) = 2.
self assert: (f <// {3 . 4}) = 5.
"watch out --- there is a problem with compiled forms: ---"
morph _ Morph new.
f _ morph lambda: #position:.
f beFast.
self assert: f hasCompiledForm.
"trouble is:"
self deny: (f <~ (0 at 0)) == (f <~ (0 at 0)) .
f <~ (55 at 12). "we actually touch a temporary copy of morph"
self deny: morph position = (55 at 12).
"fix:"
f bePure. "do not use internal compilation - this is the default BTW"
self deny: f hasCompiledForm.
f <~ (55 at 12).
self assert: morph position = (55 at 12).
"deeper fix:
pure functional programming does not use mutable variables and does not expect side-effects from the evaluation of a function.
so the example above is just an example of bad practice :)"
! !
!FunctionalTalkTest methodsFor: 'tests' stamp: 'spfa 5/11/2006 17:03'!
testComposition
| f g gf ggf gggf fgggf |
f _ 56 lambda: #+ .
g _ 10 lambda: #* .
gf _ f ~> g.
ggf _ gf ~> g.
gggf _ f ~> g ~> g ~> g.
fgggf _ f <~ g <~ ggf.
self assert: ((4 ~> gf ) = 600).
self assert: ((4 ~> ggf ) = 6000).
self assert: ((4 ~> gggf ) = 60000).
self assert: ((4 ~> fgggf ) = 60056)! !
!FunctionalTalkTest methodsFor: 'tests' stamp: 'spfa 5/11/2006 17:05'!
testComposition2
| p mp |
p _ Point lambda: #x:y: .
p _ p <~~ {1 lambda: #+ slotIds: {1} . 2 lambda: #* slotIds: {2}}.
self assert: p printString = '(Point x: (1 + <1>) y: (2 * <2>))'.
self assert: (p <~~ {5 . 15}) = (6 @ 30).
mp _ p <~~ {5 . (Point lambda: #x:y:) r}.
self assert: mp printString = '(Point x: 6 y: (2 * ((Point x: <1> y: <2>) r)))'.
self assert: (mp <~~ {0 . 1}) = (6 @ 2)
! !
!FunctionalTalkTest methodsFor: 'tests' stamp: 'spfa 5/10/2006 16:16'!
testComposition3
| f t y |
f _ LambdaSlot selector: #+ arguments: {56}.
t _ f <~~ {3 lambda: #-}.
y _ t <~~ {LambdaSlot selector: #+ arguments: {10}}.
self assert: t printString = '((3 - <1>) + 56)'.
self assert: (t <~ 2) = 57.
self assert: y printString = '((3 - (<receiver> + 10)) + 56)'.
self assert: (y <~ 2) = 47.
! !
!FunctionalTalkTest methodsFor: 'tests' stamp: 'spfa 5/13/2006 21:27'!
testConstructor
| x f g |
x _ Lambda x.
f _ 2+x.
g _ x * x.
self assert: (Compiler evaluate: (f + g) constructor) = (f + g)
! !
!FunctionalTalkTest methodsFor: 'tests' stamp: 'spfa 5/12/2006 09:49'!
testConversionAsMessageSend
| f fx fy g h |
fx _ Lambda x.
fy _ Lambda y.
f _ ((fx*fx) + (fy*fy)) sqrt.
g _ f as: MessageSend.
self assert: g value = f.
self assert: g value <~~ {3. 4} = 5.
h _ (f <~~ {3 . Lambda}) as: MessageSend.
self assert: h value = (f <<~ ('x' -> 3)).
self assert: h value <~ 4 = 5! !
!FunctionalTalkTest methodsFor: 'tests' stamp: 'spfa 5/12/2006 13:53'!
testCopy
| f g |
f _ ((Lambda x raisedTo: 2) + (Lambda y raisedTo: 2)) sqrt.
g _ f copy.
self deny: (f slots identityIncludes: g slots first).
self deny: (f slots identityIncludes: g slots second).
f _ BlobMorph new lambda yourself yourself yourself.
self assert: f value == f value.
g _ f copy.
self assert: g value == f value.
g _ f veryDeepCopy.
self deny: g value == f value.
! !
!FunctionalTalkTest methodsFor: 'tests' stamp: 'spfa 5/14/2006 14:25'!
testEquality
| f x y x2 xy g o oo s |
x _ Lambda x.
y _ Lambda y.
self assert: x = LambdaSlot new.
"lambda slots are functionaly lambda atoms. All atoms are equivalent"
self assert: x = y.
f _ ((x*x) + (y*y)) sqrt.
x2 _ x*x.
xy _ x*y.
self deny: (x2+x2) sqrt = f.
self deny: (xy+xy copy) sqrt = f.
self assert: (x2+x2 copy) sqrt = f.
self assert: (x2+(y*y)) sqrt = f.
g _ {f . xy} ~~> (Point lambda: #x:y:).
self assert: {3 . 4} ~~> g = (5 at 12).
o _ (LambdaSlot lambda: #x:y:) <~~ {Lambda . (x2 + Lambda y2) sqrt . x*y}.
s _ Lambda s.
oo _ ('y2' -> (s * s)) ~>> (Point ~> o).
self assert: (('s' ->y) ~>> oo) = g.
self deny: (('s' ->Lambda z) ~>> oo) = g. ! !
!FunctionalTalkTest methodsFor: 'documentation' stamp: 'spfa 5/13/2006 14:33'!
testExample
| x y f g j |
x _ Lambda x.
y _ Lambda y.
f _ ((x*x) + (y*y)) sqrt.
self assert: f printString = '(((<x> * <x>) + (<y> * <y>)) sqrt)'.
self assert: {3 . 4} ~~> f = 5.
g := ('y' -> 0) ~>> f.
self assert: g printString = '(((<x> * <x>) + 0) sqrt)' .
self assert: 3 ~> g = 3.
j := ('y' -> (Lambda z1 + Lambda z2)) ~>> f.
self assert: j printString = '(((<x> * <x>) + ((<z1> + <z2>) * (<z1> + <z2>))) sqrt)' .
self assert: {3 . 2 . 2} ~~> j = 5.
! !
!FunctionalTalkTest methodsFor: 'tests' stamp: 'spfa 5/14/2006 14:26'!
testLambda
"Lambda acts as a shortcut for the creation of an AtomicLambdaMessageSend which a function wrapper for a LambdaSlot"
| f |
self assert: Lambda x <~ 4 = 4.
self assert: {4} ~~> Lambda z = 4.
self assert: Lambda x <@ 4 = 4.
self assert: (Lambda x <@ Lambda y) printString = '<y>'.
self assert: ({Lambda} ~~> Lambda z) printString = '<z>'.
self assert: ({Lambda x} ~~> Lambda z) printString = '<x>'.
self assert: ('x' -> 45) ~>> Lambda x = 45.
self assert: Lambda x <<~ ('x' -> 45) = 45.
f _ Lambda x * Lambda y.
self assert: (f o: Lambda x) = f.
self assert: (Lambda x o: f) = f.! !
!FunctionalTalkTest methodsFor: 'documentation' stamp: 'spfa 5/13/2006 18:25'!
testLambdaVsBlock
| b f fx fy c g |
"let's compare two ways to define a function of two variables"
"1) with a block"
b _ [:x :y | ((x*x) + (y*y)) sqrt].
"2) with a lambda expression"
fx _ Lambda x. "shortcut for LambdaSlot id: 'x'"
fy _ Lambda y.
f _ ((fx*fx) + (fy*fy)) sqrt.
"they allow the same calculation"
self assert: (b value: 3 value: 4) = (f <~~ {3 . 4}).
"reducing the number of arguments"
c _ [:x | b value: x value: 0].
g _ f <<~ ('y' -> 0).
self assert: (c value: 4) = (g <~ 4).
"lambda function printing is completely transparent"
self assert: c printString =
'[] in FunctionalTalkTest>>testLambdaVsBlock {[:x | b value: x value: 0]}'.
self assert: g printString = '(((<x> * <x>) + 0) sqrt)'.
"you can not add blocks"
self should: [b+c] raise: Error.
self assert: (f copy+g) isLambda.
self assert: (f <~~ {3 . 4}) + (g <~ 4) = ((f copy+g) <~~ {3 . 4 . 4}).
"a lambda function can be converted into a block"
self assert: f compiled isBlock.
self assert: (f compiled value: 3 value: 4) = 5.
! !
!FunctionalTalkTest methodsFor: 'documentation' stamp: 'spfa 5/12/2006 09:07'!
testLambdaVsBlock2
"as far as local variables are concerned, lambda expressions are analogous to a block with #fixTemps sent"
| b c f fx fy a |
a _ 10.
b _ [:x :y | a + ((x*x) + (y*y)) sqrt].
c _ [:x :y | a + ((x*x) + (y*y)) sqrt] fixTemps.
fx _ Lambda x.
fy _ Lambda y.
f _ a + ((fx*fx) + (fy*fy)) sqrt.
self assert: (b value: 3 value: 4) = (f <~~ {3 . 4}).
self assert: (c value: 3 value: 4) = (f <~~ {3 . 4}).
a _ 0.
self deny: (b value: 3 value: 4) = (f <~~ {3 . 4}).
self assert: (c value: 3 value: 4) = (f <~~ {3 . 4}).! !
!FunctionalTalkTest methodsFor: 'documentation' stamp: 'spfa 5/13/2006 18:28'!
testLambdaVsBlock3
| b f |
"lambda expressions capture objects, not only code"
b _ [BlobMorph new].
f _ BlobMorph new lambda.
self assert: f value == f value.
self deny: b value == b value.
"tricky consequence: the compiled form, which is a block, may not have the same semantic as the lambda expression"
f beFast.
self deny: f value == f compiled value.
self deny: f compiled value == f compiled value.
! !
!FunctionalTalkTest methodsFor: 'tests' stamp: 'spfa 5/11/2006 18:47'!
testObfuscated
| p mp |
p _ Point lambda: #x:y: .
p _ p <~~ {1 lambda: #+ slotIds: {1} . 2 lambda: #* slotIds: {2}}.
self assert: p printString = '(Point x: (1 + <1>) y: (2 * <2>))'.
self assert: {5 . 15} ~~> p = (6 @ 30).
mp _ p <~~ {5 . (Point lambda: #x:y: slotIds: {'x2' . 'y2'}) r}.
self assert: mp printString = '(Point x: 6 y: (2 * ((Point x: <x2> y: <y2>) r)))'.
self assert: {0 . 1} ~~> mp = (6 @ 2)
! !
!FunctionalTalkTest methodsFor: 'tests' stamp: 'spfa 5/10/2006 16:13'!
testPrinting1
| f g |
f _ LambdaSlot selector: #+ arguments: {56}.
g _ ((f sqrt) + 2) * f.
self assert: g printString = '((((<receiver> + 56) sqrt) + 2) * (<receiver> + 56))'
! !
!FunctionalTalkTest methodsFor: 'tests' stamp: 'spfa 5/10/2006 16:13'!
testPrinting2
| f g f2 ff |
f _ 56 lambda: #+ .
g _ f sqrt.
f2 _ f * 2.
ff _ f * f.
self assert: f printString = '(56 + <1>)'.
self assert: g printString = '((56 + <1>) sqrt)'.
self assert: f2 printString = '((56 + <1>) * 2)'.
self assert: ff printString = '((56 + <1>) * (56 + <1>))'.
! !
!FunctionalTalkTest methodsFor: 'tests' stamp: 'spfa 5/10/2006 16:14'!
testPrinting3
| p sp |
p _ Point lambda: #x:y: .
sp _ p <~~ {0 . LambdaSlot id: 1}.
self assert: p printString = '(Point x: <1> y: <2>)'.
self assert: sp printString = '(Point x: 0 y: <1>)'.
p <~~ {1 . 2}.
self assert: p printString = '(Point x: <1> y: <2>)'.
sp <~ 10.
self assert: sp printString = '(Point x: 0 y: <1>)'.
! !
!FunctionalTalkTest methodsFor: 'tests' stamp: 'spfa 5/11/2006 15:37'!
testRenameSlots
| f |
f _ Behavior lambda: #read:go:buy:cook:inviteForDinner:.
f slotIds: #('recipe' 'shop' 'food' 'dish' 'girlfriend').
self assert: f slots printString =
'#(<recipe> <shop> <food> <dish> <girlfriend>)'.
self assert: (f <<~ ('girlfriend' -> #bob)) slots printString =
'#(<recipe> <shop> <food> <dish>)'.! !
!FunctionalTalkTest methodsFor: 'tests' stamp: 'spfa 5/11/2006 18:41'!
testSection
| p sp a sa ssa sssa |
p _ Point lambda: #x:y:.
sp _ p <~~ {5 . Lambda}.
self assert: (sp <~ 3) = (5 at 3).
a _ Array lambda: #with:with:with:with:with:with:.
sa _ a <~~ {Lambda . Lambda. Lambda . #hello . Lambda . Lambda}.
ssa _ sa <~~ {Lambda . #two . Lambda . #five . Lambda}.
sssa _ ssa <~~ {#a . #b . Lambda}.
self assert: (sa lambdaArity = 5).
self assert: (sa <~~ {1 . 2 . 3 . 4 . 5}) = {1 . 2 . 3 . #hello . 4 . 5}.
self assert: (ssa lambdaArity = 3).
self assert: (ssa <~~ {1 . 2 . 3}) = {1 . #two . 2 . #hello . #five . 3}.
self assert: (sssa lambdaArity = 1).
self assert: (sssa <~ 1) = {#a . #two . #b . #hello . #five . 1}.
! !
!FunctionalTalkTest methodsFor: 'tests' stamp: 'spfa 5/13/2006 18:20'!
testSerialization
| f rs g |
f _ Lambda x + Time lambda: #primSecondsClock.
f beFast.
rs _ SmartRefStream streamedRepresentationOf: f.
g _ SmartRefStream objectFromStreamedRepresentation: rs.
self assert: f hasCompiledForm.
self deny: g hasCompiledForm.
self assert: f = g.
f _ BlobMorph new lambda: #openInWorld.
f beFast.
rs _ SmartRefStream streamedRepresentationOf: f.
g _ SmartRefStream objectFromStreamedRepresentation: rs.
self assert: f hasCompiledForm.
self deny: g hasCompiledForm.
self deny: f = g.! !
!FunctionalTalkTest methodsFor: 'tests' stamp: 'spfa 5/13/2006 21:25'!
testSerialization2
| x f g rs a |
x _ Lambda x.
f _ 2+x.
g _ x * x.
rs _ SmartRefStream streamedRepresentationOf: {f . g}.
a _ SmartRefStream objectFromStreamedRepresentation: rs.
self assert: (a first + a second) lambdaArity = 1.
self assert: (3 ~> (a first + a second)) = 14
! !
!FunctionalTalkTest methodsFor: 'documentation' stamp: 'spfa 5/13/2006 18:21'!
testShortAPI
| f |
"all arrow notations work in both direction, although parenthesis are needed in a chain from left to right when pointing to a non-lambda object"
f _ Lambda x - Lambda y.
self assert: (f <~~ {3 . 4}) = -1.
self assert: ({3 . 4} ~~> f) = -1.
self assert: (f <~ 3) printString = '(3 - <y>)'.
self assert: (3 ~> f) printString = '(3 - <y>)'.
self assert: (f <~ 3 <~ 4) = -1.
self assert: (4 ~> (3 ~> f)) = -1.
self should: [4 ~> 3 ~> f] raise: Error.
self assert: (f <~~ {Lambda . 4}) printString = '(<x> - 4)'.
self assert: ({Lambda . 4} ~~> f) printString = '(<x> - 4)'.
self assert: (f <~~ {Lambda . 4} <~ 3) = -1.
self assert: (3~> ({Lambda . 4} ~~> f)) = -1.
self assert: (f <<~ ('y' -> 4)) printString = '(<x> - 4)'.
self assert: (('y' -> 4) ~>> f) printString = '(<x> - 4)'.
self assert: (f <<~~ {'y' -> 4 . 'x' -> 3}) = -1.
self assert: ({'y' -> 4 . 'x' -> 3} ~~>> f) = -1.
self assert: (f <@@ {3 . 4}) printString = '(3 - 4)'.
self assert: ({3 . 4} @@> f) printString = '(3 - 4)'.
self assert: (f <@ 3) printString = '(3 - <y>)'.
self assert: (3 @> f) printString = '(3 - <y>)'.
self assert: (f <@ 3 <@ 4) printString = '(3 - 4)'.
self assert: (4 @> (3 @> f)) printString = '(3 - 4)'.
self should: [4 @> 3 @> f] raise: Error.
f beFast.
self assert: (f <// {3 . 4}) = -1.
self assert: ({3 . 4} //> f) = -1.
self should: [f </ 3] raise: Error.! !
!FunctionalTalkTest methodsFor: 'tests' stamp: 'spfa 5/14/2006 13:50'!
testSlotVariableName
self assert: (LambdaSlot id: 1) asVariableName = 'arg1'.
self assert: (LambdaSlot id: Float pi) asVariableName = 'a3141592653589793'.
self assert: (LambdaSlot id: 'le schtroumpf qui fait schtroumpf') asVariableName = 'leschtroumpfquifaitschtroumpf'.
self assert: ((LambdaSlot id: BlobMorph new) asVariableName beginsWith: 'aBlobMorph').! !
!FunctionalTalkTest methodsFor: 'tests' stamp: 'spfa 5/9/2006 15:53'!
testSubstituteSlot
| p sp a sa ssa sssa |
p _ Point lambda: #x:y: slotIds: {'x' . 'y'}.
sp _ p <<~ ('x' -> 5).
self assert: (sp <~ 3) = (5 at 3).
a _ Array lambda: #with:with:with:with:with:with:.
sa _ a <<~ (4 -> #hello).
ssa _ sa <<~~ {2 -> #two . 5 -> #five}.
sssa _ ssa <<~~ {1 -> #a . 3 -> #b}.
self assert: (sa lambdaArity = 5).
self assert: (sa <~~ {1 . 2 . 3 . 4 . 5}) = {1 . 2 . 3 . #hello . 4 . 5}.
self assert: (ssa lambdaArity = 3).
self assert: (ssa <~~ {1 . 2 . 3}) = {1 . #two . 2 . #hello . #five . 3}.
self assert: (sssa lambdaArity = 1).
self assert: (sssa <~ 1) = {#a . #two . #b . #hello . #five . 1}.
! !
!FunctionalTalkTest methodsFor: 'tests' stamp: 'spfa 5/11/2006 18:44'!
testSubstituteSlot2
| f sf ssf sssf |
f _ Behavior lambda: #read:go:buy:cook:inviteForDinner:
slotIds: #('recipe' 'shop' 'food' 'dish' 'girlfriend').
sf _ ('recipe' -> (Preferences lambda: #cookbook: slotId: 'book')) ~>> f.
ssf _ {
'shop' -> (Utilities lambda: #store:inCity: slotIds: {'store' . 'city'}).
'girlfriend' -> (Preferences lambda: #girlNamed: slotId: 'lovedOne')
}~~>> sf.
sssf _ {
'dish' -> 'couscous' .
'food' -> (Utilities lambda: #allYouNeedForAGoodCouscous)
}~~>> ssf.
self assert:
sf slots printString = '#(<book> <shop> <food> <dish> <girlfriend>)'.
self assert:
ssf slots printString = '#(<book> <store> <city> <food> <dish> <lovedOne>)' .
self assert:
sssf slots printString = '#(<book> <store> <city> <lovedOne>)' .
self assert:
sssf printString = '(Behavior read: (Preferences cookbook: <book>) go: (Utilities store: <store> inCity: <city>) buy: (Utilities allYouNeedForAGoodCouscous) cook: ''couscous'' inviteForDinner: (Preferences girlNamed: <lovedOne>))'! !
!LambdaMessageSend methodsFor: 'evaluating' stamp: 'spfa 5/9/2006 23:56'!
value
arguments isLambda ifTrue: [^ self].
^ self valueWithArguments: arguments
! !
!LambdaMessageSend methodsFor: 'evaluating' stamp: 'spfa 5/9/2006 22:56'!
valueWithArguments: anArray
selector ifNil: [^ receiver lambdaValue].
(receiver isLambda
or: [selector isLambda]
or: [anArray anySatisfy: [:arg | arg isLambda]])
ifTrue: [^ self].
^ receiver lambdaValue
perform: selector lambdaValue
withArguments: (anArray collect: [:each | each lambdaValue])! !
!LambdaMessageSend methodsFor: 'printing' stamp: 'spfa 5/13/2006 21:03'!
writeConstructorOn: stream
stream nextPutAll: '([| '.
self rootSlots do: [:s | s storeOn: stream. stream space].
stream nextPutAll: '| '.
self rootSlots do: [:s | s declareOn: stream].
self storeOn: stream.
stream nextPutAll: '] value)'! !
!SequenceableCollection methodsFor: '*FunctionalTalk-API' stamp: 'spfa 5/11/2006 13:28'!
//> aLambdaMessageSend
^ aLambdaMessageSend reduceWithAllNonLambda: self! !
!Object methodsFor: '*FunctionalTalk-API' stamp: 'spfa 5/11/2006 13:28'!
/> aLambdaMessageSend
^ aLambdaMessageSend reduceWithNonLambda: self! !
!LambdaMessageSend methodsFor: 'short API' stamp: 'spfa 5/11/2006 10:18'!
</ arg
^ self reduceWithNonLambda: arg
! !
!LambdaMessageSend methodsFor: 'short API' stamp: 'spfa 5/11/2006 10:18'!
<// arg
^ self reduceWithAllNonLambda: arg
! !
!LambdaMessageSend methodsFor: 'short API' stamp: 'spfa 5/10/2006 15:33'!
<<~ anAssociation
^ self reduceWithSlot: anAssociation
! !
!LambdaMessageSend methodsFor: 'short API' stamp: 'spfa 5/10/2006 15:33'!
<<~~ anAssociation
^ self reduceWithSlots: anAssociation
! !
!LambdaMessageSend methodsFor: 'short API' stamp: 'spfa 5/10/2006 15:32'!
<@ anObject
^ self substitute: {anObject}
! !
!LambdaMessageSend methodsFor: 'short API' stamp: 'spfa 5/10/2006 15:32'!
<@@ anArray
^ self substitute: anArray
! !
!LambdaMessageSend methodsFor: 'short API' stamp: 'spfa 5/9/2006 14:19'!
<~ arg
^ self reduceWith: arg
! !
!LambdaMessageSend methodsFor: 'short API' stamp: 'spfa 5/9/2006 14:18'!
<~~ args
^ self reduceWithAll: args
! !
!AtomicLambdaMessageSend methodsFor: 'testing' stamp: 'spfa 5/14/2006 13:54'!
= anObject
^ anObject isLambda and: [anObject isAtomicLambda]! !
!LambdaMessageSend methodsFor: 'testing' stamp: 'spfa 5/12/2006 09:57'!
= anObject
^ anObject species == self species
and: [receiver = anObject receiver
and: [selector = anObject selector
and: [arguments = anObject arguments]]]
and: [anObject lambdaArity = self lambdaArity]! !
!LambdaSlot methodsFor: 'testing' stamp: 'spfa 5/14/2006 14:04'!
= anObject
^ anObject isLambdaSlot! !
!Object methodsFor: '*FunctionalTalk-API' stamp: 'spfa 5/11/2006 13:26'!
@> aLambdaMessageSend
^ aLambdaMessageSend substitute: {self}! !
!SequenceableCollection methodsFor: '*FunctionalTalk-API' stamp: 'spfa 5/11/2006 13:26'!
@@> aLambdaMessageSend
^ aLambdaMessageSend substitute: self! !
!Object methodsFor: '*FunctionalTalk-API' stamp: 'spfa 5/11/2006 13:20'!
~> aLambdaMessageSend
^ aLambdaMessageSend reduceWith: self! !
!Association methodsFor: '*FunctionalTalk-API' stamp: 'spfa 5/11/2006 13:31'!
~>> aLambdaMessageSend
^ aLambdaMessageSend reduceWithSlot: self! !
!SequenceableCollection methodsFor: '*FunctionalTalk-API' stamp: 'spfa 5/11/2006 13:26'!
~~> aLambdaMessageSend
^ aLambdaMessageSend reduceWithAll: self! !
!SequenceableCollection methodsFor: '*FunctionalTalk-API' stamp: 'spfa 5/11/2006 13:30'!
~~>> aLambdaMessageSend
^ aLambdaMessageSend reduceWithSlots: self! !
More information about the Squeak-dev
mailing list
|