Value types in the .NET framework tend to have a static TryParse method for converting a string to a typed value. Well, most of them do, the Guid type doesn't for some reason. The method takes a string and an output parameter for the result.
Output parameters suck and require a local variable to be passed in, resulting in a ton of useless supporting code:
int val = 0; string raw = "1234"; bool success = int.TryParse(raw, out val); // success is true // val is 1234
As I was working through the Expert F# book, I came across a neat little F# trick for dealing with .NET output parameters. When you leave the output parameter off of the method call, the function returns a tuple. Here's some overly explicit F# to demonstrate:
let raw = "1234" let result = Int32.TryParse(raw) (* result is (true, 1234) *) let success, value = result (* success is true *) (* value is 1234 *)
The result value is particularly interesting to me, since I've not really used a language with the concept of tuples before. A tuple is basically a grouping of several values into a single value. The "let success, value..." line above is decomposes the result tuple into two separate values. You can read more about tuples in .NET if you'd like.
If we wanted to do it all as one assignment:
let success, value = Int32.TryParse("1234") (* success is true *) (* value is 1234 *)
This feature is really very small, but I've been annoyed enough about having to initialize extra variables for out parameters in the past that I really got a kick out of it.
Update: Just to clarify what I mean by "tons of useless supporting code", here's a slightly longer (and equally contrived!) example to illustrate the type of bloat I've run across:
If I were to build a User object from post data, the code might look something like this in C#:
public class User { public int Age { get; set; } public DateTime SignupDate { get; set; } public Double Weight { get; set; } static string Post(string key) { return key; } static User Build() { var age = 0; var signupDate = DateTime.MinValue; var weight = 0.0; int.TryParse(Post("age"), out age); double.TryParse(Post("weight"), out weight); DateTime.TryParse(Post("signupDate"), out signupDate); return new User { Age = age, SignupDate = signupDate, Weight = weight }; } }
The alternative F#, however, is quite a bit more concise. Even better, it's much more declarative:
type User = { Age : int; SignupDate : DateTime; Weight : Double; } let post key = key let u = { Age = Int32.TryParse(post "age") |> snd; SignupDate = DateTime.TryParse(post "signupDate") |> snd; Weight = Double.TryParse(post "weight") |> snd; }
Now, there are a lot of other niceties in the F# version as well. But the important bit is that I can take the resulting tuples from the TryParse operations, pipe them into a the snd function, and suddenly I have valid values for my fields. snd does just what it's short for: it returns the second value in a two value tuple.
Comments (16)
The fact is that you have to initialize a variable for the resulting int anyhow.
The TryParse pattern is best used within an if statement, as you typically need to do something in the event that the parsing failed.
int foo = int.MinValue;
if(!int.TryParse(myString,out foo))
return false;
// continue on with computation
Well sure, but I don’t get to do it all in one step.
Alternatively, I could ignore the success/fail altogether and use the default value for that type:
let _,value = Int32.TryParse("1234")Do you disagree that output parameters are an abortion? :p
Honestly, there is no way of looking at TryParse and knowing that it would return a tuple, so I find that more disturbing than using an output parameter.
Well, it’s more of a language feature. If you leave off out-parameters in a method call, you get a tuple.
It would be nice if the F# intellisense showed it as an additional overload, though, so I filed a bug requesting as much.
That said, there’s really no way of knowing that you can only use a local variable for an out parameter in C# either.
“That said, there’s really no way of knowing that you can only use a local variable for an out parameter in C# either.”
Sure there is, try compiling it with a property instead
Wouldn’t this be the same as writing a TryParse that returned a nullable type:
Nullable<Guid> result = TryParseGuid(stringValue);
if (result.HasValue)
{
… result.Value;
}
er, blog stripped my Nullable<Guid>
I fixed it.
For value types, you could do something like that. Then again, nulls suck almost as much as output parameters.
There are other BCL methods that use output parameters and not value types, though. TryGetValue on dictionary is one I use quite often. In theory, you TryGetValue could succeed and still return a null value, so the null check wouldn’t exactly work in those cases.
Maybe I miss your point, but where do you see “a ton of useless supporting code” in this :
string raw = “1234″;
int val; /* No need to initialize an out parameter */
bool success = int.TryParse(raw, out val);
compared to that :
let raw = “1234″
let success, val = Int32.TryParse(raw)
?
Let me even rewrite first snippet of code in C#3 :
var raw = “1234″;
int val; /* Too bad I cannot write declare val as var */
var success = int.TryParse(raw, out val);
As far as I can count, I have as many variable declarations.
Only differences are : semicolons, typed declaration(s) and order of declarations (val is declared before use in C#).
Maybe you’ll find this disturbing, maybe you’ll find that tuples rock, maybe you’ll find that TryParse design sucks, but don’t say this is because of verbosity.
Jurgen: Have a look at the additional examples I provided. I’m being a bit hyperbolic, but useless variable declarations qualify as bloat as far as I’m concerned.
Kurt,
It may be better to say what I am thinking. I was not aware of tuple’s either and F# does seem to have an elegant way of handling multiple return values.
Tuples rock!
However, it seems, in this case, that it would make more sense to have a bool Int32.CanParse(string value) method.
And, move the TryParse methods to a special System.ICryAboutPerformance namespace.
Just a thought.
I was just using Nullable as an example of returning a class instead of using an out parameter.
My point is you _can_ have “tupley” =) code in C#, it’s just called a “return type”. In the case of the dictionary example:
var result = myDictionary.TryGetValue(key);
if (result.IsValidKey)
{
…result.Value
}
Where “TryGetValue” is an extension method that returns a custom type.
Granted, you don’t get the language-baked feature to automatically do this, but with a few good extension methods, you wouldn’t miss it =)
I have made som very basic stuff for retreiving nullable types from TryParse:
http://www.khebbie.dk/post/2008/04/Nullable-TryParse.aspx
let first, foo = Int32.TryParse(xxx);
let second, bar = Int32.TryParse(yyy);
if first && second then (* dothomething with first and second *) else (* default behaviour *)
Pretty cool. Even cooler would be some kind of active pattern.
match (xxx, yyy) with
| (parse(first), parse(second)) -> (* dothomething with first and second *)
| _ -> (* default behaviour *)
helium> Pretty cool. Even cooler would be some kind of active pattern.
Don’t know if I misunderstand you, but you can simply do this in F#:
match Int32.TryParse(xxx), Int32.TryParse(yyy) with
| (true, foo), (true, bar) -> (* something *)
| _ -> (* otherwise *)
Anyone still not agreeing with Kurt?
Btw, a real F#-ish TryParse would return an option value, with which it could look like this:
match Int32.TryParse xxx, Int32.TryParse yyy with
| Some foo, Some bar -> (* something *)
| _ -> (* otherwise *)
Trackbacks/Pingbacks (2)
[...] shortly after adding the more in depth examples in my last post, I started playing around with the TryParse method in C# to see how “nice” I could make the example [...]
[...] The SchemaField type needs two basic pieces of information, a label and a set of options. I don’t know about you, but when I read “two pieces of information”, I think tuple: [...]