# Good Procedure or Function Design

This is in reference to the question posted here. As I would judge it, the question there should be closed simply because it seems to ask why the Borland developers made one thing a function and another a procedure (function that returns a void result).

Now the example was given there of some string manipulation functions that exist in Borland Pascal or Delphi:

``````S2 := Copy(S1,3,2);
``````

Copies into S2 from S1 the 2 characters starting from the 3rd position.

``````Delete(S,3,2);
``````

Deletes from S the 2 characters starting from the 3rd position.

Now I thought it might be a good discussion on what would be good design decisions regarding devising these things. I picked up a few rules of good design on what a function prototype should look like beyond the obvious (name reflects function, function result should be atomic, no extraneous variables), but are there any others that should govern a situation such as this one or others? I really didn't see a good case in Atwood's code book where it was discussed.

Anyway, are there some good rules on how functions should be designed to work? Barring the specious naming, would it be better to have

``````S2 := Delete(S1,3,2);
``````

as opposed to

``````Delete(S,3,2);
``````

? Are there any other useful considerations or examples to discuss of good and bad function design?

• I see Pieter B put a bounty on this question (I didn't know you could bounty other people's questions). In accepting an answer, I'll look to his guidance (if he indicates here), since it was his question to begin with on SO. Otherwise, I'll use my judgment. Commented Sep 29, 2013 at 0:36
• One tip is to be consistent about how bounds are specified. Common options are inclusive bounds, half-open bounds and (as in that `delete`) first-and-size. Half-open bounds (inclusive at the start, exclusive at the end) are popular for reasons that Dijkstra explained fairly well. Most important, though, is consistency. Confusion about bounds is a common source of errors.
– user8709
Commented Oct 2, 2013 at 22:20
• I was looking for a why to use, rather then an explanation of the difference between the two methods. I'll reward the highest voted answer. Commented Oct 3, 2013 at 9:19

It seems to me that both styles have a place because they don't do the same thing from a semantics standpoint:

``````S2 := Delete(S1,3,2);
``````

Assignment implies that there is a subroutine that returns something that can in-fact be assigned to a variable.

``````Delete(S,3,2);
``````

Implies an in-place mutation by the subroutine without any kind of return value, thus making it a procedure, as procedures lack return values.

Generally speaking, functions should be designed so that they can be completely curried and so that they are conceivably pure. Since the second style of "function" design relies entirely on a side effect to generate its effect, I'd argue that you should only ever use the first style.

• To me, there's a name issue with the first case. A procedure/subroutine/action/whatever that mutates a value in place should be named using a verb that describe what it does. A function that returns the value you want without mutating its arguments should be named using a noun (or adjective) that describes the returned result. For arrays/strings, I've always had a soft spot for the old BASIC string function names `left`, `right` and `mid` for extracting substrings. For the remaining part after a deletion, I'm not sure there's a good short name - best I can think of is `delete_result`.
– user8709
Commented Oct 2, 2013 at 22:13
• Actually, thinking about it, I mostly apply that rule in imperative languages - not Haskell.
– user8709
Commented Oct 2, 2013 at 22:31

As long as some basic language rules hold, either notation can be translated to the other.

``````S2 := Delete(S1,3,2);
``````

can be rewritten as

``````Delete(S2,S1,3,2);
``````

if S2 is declared to be pass-by-reference.

And

``````Delete(S,3,2);
``````

can be rewritten as

``````S := Delete(S,3,2);
``````

In most languages I can think of, this is the case. If it's not the case, the argument for notation is clear: use the one that works.

But assuming one can use either, you asked for useful considerations. To me, the two guiding ones should be clarity and efficiency.

On clarity, take the first example above. Personally, from a function, I expect one of two behaviors. Either it returns all of the work it does in S2 (with no pass-by-reference arguments at all), or it returns all of its work in pass-by-reference args and then returns only a code indicating success or failure. To do otherwise -- to manipulate some args as work product and then return some other work product -- makes the code impossible to understand. Now, if there is no question about the success or failure of the function, and if pass-by-reference is more appropriate for the work being done, then procedural syntax is fine. But in my experience, 90+% of the time, I either want a success/failure indication, or don't want to pass by reference, or both.

On efficiency, take the second example. Obviously if S or the operations on it are large and complex, pass-by-reference can be more efficient than working on a copy of data and returning only to assign to the same original variable. And as above, if pass-by-reference is appropriate, and if there is no question about the function/procedure succeeding or failing, then no return is necessary.

I'm not sure about other languages, but in Ruby the approach they take is to usually implement both types of method, so that the user can choose which one is best for the situation, but the type that modifies the data (rather than leaving the original data unchanged and returning the changed version) is always suffixed with a '!', so it is clear that the function performs a destructive operation.