andrewducker: (Default)
[personal profile] andrewducker
I want to be able to retrieve a value and then branch on its value, all in
one step. For some reason it irks me to have to say:
var blah = GetBlah();
if(blah != null)
{
//Do Stuff With blah
}

I want to say

if((var blah = GetBlah) != null)
{
//Do stuff with blah
}

I'm not sure why the extra line bothers me - possibly because it's a
pattern I keep having to repeat in my code, and any pattern I repeat irks
me after a while. That's why computers are there, to stop you having to do
the same thing over and over again.

Date: 2011-06-20 12:44 pm (UTC)
simont: A picture of me in 2016 (Default)
From: [personal profile] simont
If nothing else, the latter might have the useful effect of limiting the scope of 'blah', ensuring you didn't use it in an accidental or unclear way further down the function...

Date: 2011-06-20 12:52 pm (UTC)
From: [identity profile] call-waiting.livejournal.com
Can you at least do the C'ish

var blah;
if ((var = getBlah()) != null)
{
  //Do stuff with blah
}


?

Date: 2011-06-21 09:17 am (UTC)
From: [identity profile] channelpenguin.livejournal.com
what I was going to say. I use ? all the time...

Date: 2011-06-20 01:13 pm (UTC)
From: [identity profile] strawberryfrog.livejournal.com
In general yes, since (blah = getBlah()) returns the value assigned to blah.
You'd have to declare blah without using "var" though, unless there's an assignment on the same line the type inference has nothing to work on.

so this works

string blah;
if ((blah = getBlah()) != null)
{
//Do stuff with blah
}

private string getBlah()
{ ... }

Date: 2011-06-20 01:34 pm (UTC)
From: [identity profile] call-waiting.livejournal.com
You'd have to declare blah without using "var" though

Ah yes, I'd forgotten that C# has type inference; I'd assumed that Andy's 'var' was pseudocode, heh.

(no subject)

From: [identity profile] strawberryfrog.livejournal.com - Date: 2011-06-20 01:43 pm (UTC) - Expand

(no subject)

From: [identity profile] strawberryfrog.livejournal.com - Date: 2011-06-20 02:11 pm (UTC) - Expand

(no subject)

From: [identity profile] call-waiting.livejournal.com - Date: 2011-06-20 06:33 pm (UTC) - Expand

Date: 2011-06-20 07:18 pm (UTC)
From: [identity profile] cartesiandaemon.livejournal.com
Hm. I was thinking that C and C++ can do that, but didn't follow the thought through to the conclusion. I can't remmeber if C or C++ allow variable declaration in any statements other than for loops?

However, I don't think I _want_ to write what Andy wanted to write. I think the assignment and test together are two ugly. I would accept a syntax like:

if (var blah=getBlah()) // if implicit test for false/null makes sense


or:

if (var blah=getBlah(); blah!=null)


or possibly some other convenient shorthand.

In C++ it might be possible to make an awful preprocessor hack, something like

#define TRY(var, value) for(auto var = value;var!=null;var=null)

TRY(blah, GetBlah()) {
 // do stuff
}


You mgiht be able to improve the semantics, I'm not sure. I doubt it's worth using, though. You may be able to do something fancier with templates, but it's getting it to act like an "if" statement that's hard.

Date: 2011-06-20 08:46 pm (UTC)
From: [identity profile] call-waiting.livejournal.com
Yes, in C++ you can say:

if (blahType blah = GetBlah()) { ... }

(no subject)

From: [identity profile] cartesiandaemon.livejournal.com - Date: 2011-06-20 09:16 pm (UTC) - Expand

Date: 2011-06-20 12:53 pm (UTC)
From: [identity profile] strawberryfrog.livejournal.com
Hm, some sort of macro, hashdefine or metaprogramming sytem is what's needed to do this right.

The closest you could get in C# would be an extension method using some generics and lambdas:


public static T DoIfNotNull<T>(this T value, Action<T> action) where T: class
{
if (value != null)
{
action(value);
}

// return the value for further chaining
return value;
}


Then you could code:

GetBlah().DoIfNotNull(blah => DoStuffWith(blah));

of course there are variations on this like

public static U DoIfNotNull<T, U>(this T value, Func<T, U> func) where T : class
{
U result = default(U);

if (value != null)
{
result = func(value);
}

return result;
}


.. so then your action can change the type and you can still chain methods.
Edited Date: 2011-06-20 12:56 pm (UTC)

(no subject)

From: [identity profile] strawberryfrog.livejournal.com - Date: 2011-06-20 01:17 pm (UTC) - Expand

(no subject)

From: [identity profile] channelpenguin.livejournal.com - Date: 2011-06-21 09:20 am (UTC) - Expand

Date: 2011-06-20 01:17 pm (UTC)
From: [identity profile] steer.livejournal.com
Related but not quite what you're after is Java 7's "null-safe" handling:

http://metoojava.wordpress.com/2010/11/15/java-7-awesome-features/

It's an interesting language feature I've not seen elsewhere that I hope spreads.

Date: 2011-06-20 05:12 pm (UTC)
From: [identity profile] skington.livejournal.com
Perl lets you do that, but not quite as much as you'd want.

You can say

if (my $blah = get_blah()) {
...
}

but not

if (my $blah = get_blah() and $blah->can('do_stuff')) {
$blah->do_stuff();
}

because $blah doesn't exist when the second part of the conditional is run. You need to say

if (my $blah = get_blah()) {
if ($blah->can('do_stuff')) {
$blah->do_stuff();
}
}

instead.

Date: 2011-06-20 09:05 pm (UTC)
From: [identity profile] call-waiting.livejournal.com
If you really wanted to support that sort of idiom, you could define a 'failure' class whose AUTOLOAD method returned the object ref; then any method failing in such a way could return a (constant?) instance of this class:

package fail;
sub AUTOLOAD { return $_[0]; }
use constant FAIL => bless [], fail;
package blah;
sub doStuffWithBlah { ...; return $self; }
sub doMoreStuffWithBlah { ...; return $self; }
package main;
sub getBlah { ... else { return FAIL; } }

getBlah()->doStuffWithBlah()->doMoreStuffWithBlah();


If getBlah returns FAIL, then 'doStuffWithBlah' resolves to fail::AUTOLOAD, which does nothing but return FAIL, allowing 'doMoreStuffWithBlah' to resolve to fail::AUTOLOAD also, so the failing behaviour cascades.


...and I'm just going to stop right there, because, frankly, it's quite silly, isn't it?

Date: 2011-06-20 06:49 pm (UTC)
From: [identity profile] call-waiting.livejournal.com
I have a vague recollection that some language I used to know had a similar feature. It might be Verisity 'E', but I don't remember it having exactly this. It did have some very neat functional list operators anyway, and I think it might have been possible to easily construct an idiom that did that, but i can't remember enough of the details to work out if it's possible.

It also strongly reminds me of Icon's goal-directed evaluation, but since Icon doesn't have any OO features and very very limited structures, I'm pretty sure it doesn't have anything quite like this; but if it *did*, it would support this sort of thing straight out of the box.

(I recommend giving Icon a once-over for curiosity's sake, by the way; it's an interesting little language, primarily because of the goal-oriented evaluation and generation. )

Date: 2011-06-20 07:56 pm (UTC)
From: [identity profile] momentsmusicaux.livejournal.com
But the second version is so ugly! You can't as easily see what's going.

(no subject)

From: [identity profile] momentsmusicaux.livejournal.com - Date: 2011-06-20 08:15 pm (UTC) - Expand

(no subject)

From: [identity profile] momentsmusicaux.livejournal.com - Date: 2011-06-20 08:55 pm (UTC) - Expand

(no subject)

From: [identity profile] call-waiting.livejournal.com - Date: 2011-06-20 08:53 pm (UTC) - Expand

(no subject)

From: [identity profile] call-waiting.livejournal.com - Date: 2011-06-20 08:54 pm (UTC) - Expand

(no subject)

From: [identity profile] strawberryfrog.livejournal.com - Date: 2011-06-21 08:53 am (UTC) - Expand

Date: 2011-06-20 10:44 pm (UTC)
From: [identity profile] lpetrazickis.livejournal.com
I haven't done C#, but in Java my pattern is to avoid multiple levels of indentation by reversing the logic:

var blah = GetBlah();
if (blah == null) return;
// Do Stuff With blah

Or:

var blah = GetBlah();
if (blah == null) continue;
// Do Stuff With blah

Date: 2011-06-21 08:56 am (UTC)
From: [identity profile] strawberryfrog.livejournal.com
I agree. Some people think that multiple returns is bad. They are wrong. (Link to my own blog).

(no subject)

From: [identity profile] channelpenguin.livejournal.com - Date: 2011-06-21 09:22 am (UTC) - Expand

Date: 2011-06-21 11:41 am (UTC)
From: [identity profile] red-phil.livejournal.com
Personally the first option wins on clarity.

The second approach saves one line of source code, but if the compiler is in any way decent, it will be no better when run.

Date: 2011-06-21 11:42 am (UTC)
From: [identity profile] strawberryfrog.livejournal.com
I had some free time, so I decided to learn by trying to do this in F#.

F# has a pipeline operator "|>" which is just a syntactic re-arrangement, so that aList |> filter |> sort is equivalent to sort(filter(aList). It's for readability more than anything else. "the list is filtered then sorted" is clearly expressed in the pipeline version, even though it's equivalent to the other version. What's interesting is that |> can be defined inside F#, so your operator couldn't be too hard....

I found out that, at least for the novice, F# is a language where typing the brief code is the least of your worries. Understanding what's going on takes far longer. This took a while.

F# does nulls with the option / Some / None constructs. anything without these cannot be null.

Herewith some code and commentary from the F# interactive console:


Microsoft (R) F# 2.0 Interactive build 4.0.40219.1
Copyright (c) Microsoft Corporation. All Rights Reserved.

For help type #help;;

> let increment a = a + 1;;

val increment : int -> int

I have defined a function called 'increment'. F# has deduced that the type of it is 'int -> int'

> 42 |> increment;;
val it : int = 43

I can use the pipeline operator to pipeline 42 into increment and get 43

> let (|+) a fn = fn a ;;

val ( |+ ) : 'a -> ('a -> 'b) -> 'b

I can define my own operator that works just like the pipeline. It also has a type, and the second input is a function a -> b.


> 42 |+ increment;;
val it : int = 43

.. and it works the same


> let (|++) a fn =
  if Option.isSome(a)
    then Some(fn(Option.get(a))) 
    else None;;


val ( |++ ) : 'a option -> ('a -> 'b) -> 'b option

Here is the 'DoIfNull' operator. If it was called 'DoIfNull' it wouldn't be an infix operator, 
apprently operators can't be named in the alphabet. 
Also now the types are those that can be null, and I need to wrap values in Some() to make them nullable again. 
Getting this to work took a while. 

> Some(1) |++ increment;;
val it : int option = Some 2
> Some(42) |++ increment;;
val it : int option = Some 43

But I can use it, it will increment a nullable int

> None |++ increment;;
val it : int option = None

And it will leave a null int alone

> let (|++) a  fn =
  match a with
     | Some(x) -> Some(fn(x)) 
     | None -> None;;


val ( |++ ) : 'a option -> ('a -> 'b) -> 'b option

> Some(42) |++ increment;;
val it : int option = Some 43

A slightly different but equivalent defintion of the same operator
 

With more complex types:

let numbers = [1..10];;

val numbers : int list = [1; 2; 3; 4; 5; 6; 7; 8; 9; 10]

> let isEven i = i % 2 = 0;;

val isEven : int -> bool

> numbers |> List.filter isEven;;
val it : int list = [2; 4; 6; 8; 10]

> Some(numbers) |++ List.filter isEven;;
val it : int list option = Some [2; 4; 6; 8; 10]
> 

(no subject)

From: [identity profile] strawberryfrog.livejournal.com - Date: 2011-06-21 12:43 pm (UTC) - Expand

(no subject)

From: [identity profile] strawberryfrog.livejournal.com - Date: 2011-06-21 01:00 pm (UTC) - Expand

(no subject)

From: [identity profile] martling.livejournal.com - Date: 2011-06-24 02:37 pm (UTC) - Expand

(no subject)

From: [identity profile] strawberryfrog.livejournal.com - Date: 2011-06-24 02:49 pm (UTC) - Expand

(no subject)

From: [identity profile] martling.livejournal.com - Date: 2011-06-28 07:58 am (UTC) - Expand

(no subject)

From: [identity profile] martling.livejournal.com - Date: 2011-06-24 05:06 pm (UTC) - Expand

(no subject)

From: [identity profile] martling.livejournal.com - Date: 2011-06-24 05:48 pm (UTC) - Expand

(no subject)

From: [identity profile] martling.livejournal.com - Date: 2011-06-27 01:10 pm (UTC) - Expand

(no subject)

From: [identity profile] martling.livejournal.com - Date: 2011-06-28 01:51 pm (UTC) - Expand

(no subject)

From: [identity profile] martling.livejournal.com - Date: 2011-06-28 03:03 pm (UTC) - Expand

April 2026

S M T W T F S
    1 2 34
567 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 232425
2627282930  

Most Popular Tags

Style Credit

Expand Cut Tags

No cut tags
Page generated Apr. 24th, 2026 03:20 am
Powered by Dreamwidth Studios