views:

76

answers:

3

Does Smalltalk(especially Squeak/Pharo) have some form of variadic functions?

I was just reading about the power of designing your own control statments in smalltalk and while I'm a big fan of ifTrue: ifFalse: I was having a hard time coming up with a good way to implement arbitrary if,if else, if else,...,else statements thinking how useful Variadic functions those would be for implementing case statements. Something like

false class

ifTrue: aBlock (... elseIf: aBoolean then: aSecondBlock ...) else: aLastBlock

vArgList pairsDo: [:x :y| x ifTrue:[^ (y value)] ].
^ aLastBlock
+3  A: 

Smalltalk's keyword-like syntax for method calls inherently defines the arity of the method. There's no &rest pattern like in Common Lisp.

You can of course have a method take a list, like BlockClosure>>valueWithArguments: but that's hardly the same thing.

You might modify Compiler to support variadic method calls. Maybe the call would simply have with: between each of the variables:

(condition) ifTrue: aBlock elseIf: aBoolean with: aSecondBlock with: anotherBoolean with: aThirdBlock

Frank Shearar
You can't modify only the compiler for that. You also need to modify the VM. Because the number of argument is inside the BlockClosure that tell the size (that include the number of args) of the context to be created.
mathk
+1  A: 

You can use the #caseOf:otherwise: message. Look for some sender of this message for some example.

But anyway if you want to use case statement you are not following the smalltalk-way-of-doing-things. Tell us what you want to achieve with your case statement so that we can show you some cleaner way to do it.

mathk
As a general principle, I agree. It's hard for me to see how one could improve, readability-wise, on Andreas Raab's use of #caseOf:otherwise: in DnsClient (on SqueakSource).
Frank Shearar
A: 

One of the things I've learned in programming is not that you don't need case statements, you don't want case statements.

Case statements are the way to bloat an object. When you use it, you are degrading maintenance. You'll have all the possibilities you want at that time, but the day you want to add something you'll have to review nasty code when you don't remember anymore the subtleties in the responsibility of that object, so you will bloat it even more.

In the very short term, they look friendly but case statements aren't your friends. They will bite you in the long run.

Also they make your code less flexible. For example, you can't add a case confidently about the previous code. You are forced to review old code when you don't remember it anymore why it was coded like that.

Case statements are enemies of good code.

If you have something that goes beyond ifTrue:ifFalse: then the right thing to do is to make states for that. So what you do is to implement three classes that are very simple and they all understand some verb.

Say, #doTheNextThing. So when the object receives the message it delegates to the state (whichever is the one of those 3 (or 30, so note this scales complexity nicely)) and the state knows how to make the original receiver to react correctly.

That will leave your code light, obvious and highly maintainable. You'll be able to forget about that because you know that when you look at it again, everything is so obvious you don't need to think in the past of your code as much as in the future of your code.

That's important because your memory is the most expensive memory you have and AFAIK is not upgradable. So this technique allows you to do more powerful stuff.

Beside, making 3 classes may sound like more work for some but is not. Any noob can add 3 empty classes in a blink but only the right expert will remember how a case statement in old code was made the way it was made. And if you're wildly optimistic, it will take minutes to recall that so you all know what to do next. Think about that.

Sebastian Sastre