Musings

Apr. 9th, 2009 03:38 pm
andrewducker: (Default)
[personal profile] andrewducker
I've been coming to the conclusion that I don't like the way that methods return variables in C-like languages.  Having a single prioritised output parameter seems counterintuitive to me, and leads to two outcomes:
1) It's very easy to ignore/forget the return value.
2) If you have multiple return values then either one of them is the "proper" return value and the other one is passed back as a reference, or you pass back a tuple of some kind, or you have no return value and they both come back as references. 

It seems to me that it would be clearer and simpler if you always specified what parameters were in and out.

Rather than
string Reverse(string inputString)
you'd have
Reverse (string inputString, out string outputString)

This doesn't make a lot of difference when it comes to a simple method like that.  But if we take one with two outputs:

Do we use:
1) int GetMonthAndDay(DateTime inputDate, out int day)
2) MonthDayPair GetMonthAndDay(DateTime inputDate)
3) void GetMonthAndDay(DateTime inputDate, out int month, out int day)

The first one is clearly awful, the second one requires me to create an extra class, and the third one seems entirely clear.

Also, the use of different parameters means that you have smart method selection going on.
Convert(DateTime inputDate, out long seconds)
Convert(DateTime inputDate, out string nicelyFormatted)
makes it obvious from the parameters exactly what you're expecting to get back from it.

Of course, it does complicate very simple things:
DateTime tomorrow = today.AddDay();
would become
DateTime tomorrow;
today.AddDay(out tomorrow);

I suspect I'm just working on stuff where there are always multiple ins and outs, and also using generics and type inference, which doesn't work on return values, just parameters, and this is leading me to odd conclusions.

Date: 2009-04-09 02:51 pm (UTC)
From: [identity profile] davidcook.livejournal.com
So you want to program in Ada, then ? A bold move :-)

procedure GetMonthAndDay(inputDate: in DateTime; month: out Integer; day: out Integer) is
begin
-- do stuff here
end GetMonthAndDay;

Date: 2009-04-09 07:53 pm (UTC)
From: [identity profile] call-waiting.livejournal.com
Ada does also have functions with return values, however, so it's just as easy to write the Horible Version in Ada as it is to write it in C#:

function getMonthAndDay(inputDate: in DateTime; day: out Integer) return Integer is
   ...


Just as with the C'ish languages, you need to have an enforced coding standard.

Date: 2009-04-09 03:18 pm (UTC)
From: [identity profile] figg.livejournal.com
have fun with the following common operations:

f(g(x))

there is a reason we use that notaton as it's easy to combine functions.

p.s. in prolog does something very similar, and it's annoying as hell sometimes.

Date: 2009-04-09 04:19 pm (UTC)
From: [identity profile] figg.livejournal.com
Um, no, It works quite well for prolog.

The problem is that function composition is a bitch
instead of doing


y = f(g(x)), you have to do f(x,x1), g(x1,y)

this gets very clumsy and annoying after a while.

Functions are useful for the common case of a single return, and in more mature languages you can return multiple values as pairs.

Date: 2009-04-09 05:25 pm (UTC)
From: [identity profile] figg.livejournal.com
Prolog isn't object orientated, and abstract datastructures are a piece of piss.


To be frank, I haven't encounted such a problem with python, named parameters and easy tuples make this sort of problem a breeze.

Oh, and you're confusing strict and strong typing. Strict typing is type definition at compile time, and dynamic is at runtime.

Strong typing is the lack of implicit casts, and weak typing has implicit casting.


Oh, and saying 'hey intellisense makes this easy' is akin to 'hey this crutch makes it easy to walk with a broken leg'.

Date: 2009-04-10 10:33 am (UTC)
From: [identity profile] martling.livejournal.com
Tuples are part of the core type system in Python. You don't have to declare a class to hold your tuple, you can just return "foo", 3. The same is true of most functional languages, e.g. (string, int) is a valid type in ML.

It's a shame this is missing from Java, it seems it was planned originally: http://gbracha.blogspot.com/2007/02/tuples.html

I gather that adding this to C# would be difficult now because essentially the CLR type system doesn't have the underlying support. F# has tuples in the language but the underlying implementation is a hack.

Incidentally, has [livejournal.com profile] figg told you about the gathering he is organising for people interested in programming languages?

Date: 2009-04-10 11:50 am (UTC)
From: [identity profile] martling.livejournal.com
Oh, sure, it's still Python and therefore a non-confidence-inspiring dynamically typed mess. But the basic principle of tuples as a core part of the type system could be added to to any language - it's just sadly pretty rare outside of functional languages.

If you had them in a statically typed C-like language you could do this:

(int month, int day) GetMonthAndDay(DateTime inputDate) {
    return (inputDate.getMonth(), inputDate.getDay());
}


And then call like this:
(int month, int day) = GetMonthAndDay(date);


Note that as first-class types, tuples would also be usable for input parameters, variables, etc.

(int month, int day) month_and_day;
month_and_day = GetMonthAndDay(date);
int day = month_and_day[1]; // address like arrays.
int month = month_and_day.month; // or like structs, if contents named
 birthdays = GetBirthdays(month_and_day);

Date: 2009-04-10 12:28 pm (UTC)
From: [identity profile] martling.livejournal.com
I don't see any reason why the tuple members couldn't be accessed by name, if names were given for them where the tuple was declared. I suggested this in the the penultimate line above.

The function in that style:
(int month, int day) GetMonthAndDay(DateTime inputDate) {
    return (month = inputDate.getMonth(), day = inputDate.getDay());
}


I'd think of them rather as anonymous C-style structures than as classes - there's nothing specifically object-oriented here.

Date: 2009-04-09 03:19 pm (UTC)
From: [identity profile] meihua.livejournal.com
I guess I always lean towards the idea of working with nice classes.

DateTime MyDateTime = New DateTime(inputDate)
MyMonth = MyDateTime.Month
MyDay = MyDateTime.Day

Date: 2009-04-09 03:31 pm (UTC)
From: [identity profile] bracknellexile.livejournal.com
That's the way I'd do it too.

I'll take the flexibility of being able to add properties/methods to a class (and not break existing calling-code when the class is expanded) any day, despite the extra work required to create the class.

Date: 2009-04-09 03:35 pm (UTC)
From: [identity profile] call-waiting.livejournal.com
I haven't paid much attention to C# etc, but in C itself it's possible to disambiguate input-only from output parameters in the language with the use of const qualifiers for read-only parameters. Perhaps the Javaish languages make this sort of thing harder.

It's a class of problem, though, that can be easily dealt with as a coding standards issue. So long as there's some clear standard for how input/output parameters are indicated, and that standard is followed (with the help of linting tools).

It's also possible to partially solve the "I need another class for a single return type" problem in C with a macro hack to give the equivalent of anonymous tuple types, which is sometimes handy.

Date: 2009-04-09 04:07 pm (UTC)
From: [identity profile] call-waiting.livejournal.com
Ah, excellent! Then what you need is a coding standard which states something like: a function with more than one output will have type void and will pass back those outputs by output parameters. I'd still make the argument that for single outputs, a return value is far clearer than an out parameter, so it's a worthwhile language feature. It's just a case of having the authority to enforce good practice ;)

Date: 2009-04-09 03:46 pm (UTC)
From: [identity profile] drainboy.livejournal.com
I hate long parameter lists, they end up as a jumble of variable names, where you have to constantly look up ordering to get the functionality you want.

I'm almost tempted to say have one input parameter and one output parameter, where both of those parameters are a list of named variables. You could still deal with default variable values (if your flavour of C has them) in the header file (if your flavour of C has them) and you could still call f(g()) because the output named parameter list of g would contain the input parameter list of f.

Though I'm sure this would get messy as you'd have to then remember the actual names of all the named parameters you wanted to pass.

This might also be more difficult to optimise in terms of putting parameters into registers, though I'm sure compiler writers would find a way (maybe by having JIT recompilation based on code use like C# etc.)

And I'm sure you can do this in Lua already.

Date: 2009-04-09 03:59 pm (UTC)
From: [identity profile] drainboy.livejournal.com
I'm not so convinced they would. You could act like it's a bag of variables, whilst still checking at compile time that all the variables were of the correct type.

Obviously you're right about intellisense, though it's not as good in C++ as in C# (or maybe MSDev 2005 isn't as hot as 2008).

A bag of input parameters would mean you'd no longer have to put all the default variables at the end and fill them in explicitly if you merely wanted the last variable to be a non-default value.

Date: 2009-04-09 04:02 pm (UTC)
From: [identity profile] drainboy.livejournal.com
Actually, thinking about it, whilst you could guarantee the types of the input variables, you might not be sure what you were getting out of the function, so g(f()) might be difficult to strongly type, unless you had default named outputs.

Which starts to get overly complex and doesn't sound worth it :)

Date: 2009-04-09 04:23 pm (UTC)
From: [identity profile] figg.livejournal.com
Named paramaters are kinda neat, and a little better than passing a list.

Date: 2009-04-09 06:07 pm (UTC)
From: [identity profile] channelpenguin.livejournal.com
I tend to solve this (for more than, say 2 or 3 out params) by making the return value specific type (object, struct).

Date: 2009-04-10 10:34 am (UTC)
From: [identity profile] call-waiting.livejournal.com
To be honest, that sounds like exactly the sort of problem that generics should be able to solve trivially, and I'm rather puzzled.

It should surely be possible to do something like

class pair<T,U> {
  T fst; U snd; }
pair<int,int> getMonthAndDay(DateTime...);
...
pair<int,int> res = getMonthAndDay(dt);


If something like this isn't possible, then there's something deeply wrong with their generics implementation.

September 2025

S M T W T F S
  12 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24252627
282930    

Most Popular Tags

Style Credit

Expand Cut Tags

No cut tags
Page generated Sep. 25th, 2025 10:46 am
Powered by Dreamwidth Studios