In my quest for ultimate one-liners, I was hoping to find the most concise way to call method with a guard clause conditional that executes only one statement if the guard clause isn’t triggered, all in one line.
Take for example this method:
Vector3 Move(Vector3 direction) => direction != Vector3.zero ? transform.position += direction : Vector3.zero;
It is pretty dumb, but bear with me. It returns a vector3.zero if direction is equal to, well zero… which is precisely the dumb part of it I don’t need. The important part is the transform manipulation doesn’t occur in that case. How would I do this using a lambda without returning a value? Null coalescing operator doesn’t seem to work in this situation being that Vector3 is not nullable. I know I can do this with regular brackets and a traditional if() statement, however, I’m more interested in finding out why I can’t do it with a lambda statement. Omnisharp is vague, and frankly, I think I’m doing what its saying I should be doing, ie, incrementing a value.
The error I get from omnisharp is that only assignment, call, increment, decrement, await etc are valid operations in a statement. And I keep yelling at the screen “i want to increment by the value of direction!!” but its no use.
Vector3 Move(Vector3 direction) => direction != Vector3.zero ? transform.position += direction : Vector3.zero;
If I came across this “concise” one-line monstrosity from my team I would immediately locate the author and demand that it be rewritten with no lines longer than 72 characters.
I highly recommend against writing “concise” code and instead write explicit code.
I recommend just coding out the steps one after the other in precisely the same way you would tell your Grandmother how you are doing the steps.
The generated code will be identical and as a benefit, everybody can actually understand what you are doing.
As to whether what you contemplate can be done or not, that’s gonna be a question for the C# language weenies and the parsing details of the ternary operator vs the closure operator(s), none of which I bother to clutter my brain with.
@Kurt-Dekker I love learning whacky weird language weenie techniques, edge cases, and other seemingly useless things. This is not necessarily because I want to use them… --its more or less because if I don’t know the boundaries of possibility, I will waste time on trying to do the impossible. You don’t always know what you don’t know. Perfect example, yesterday I made a class for a component shared by both the enemy and the player objects, and the component unified similar functionality between the two, but its implementation relied on the idea of using an overloaded method in a property setter to convert a given type into the appropriate type for the property and return it to the private variable. Turns out, overloading methods in property setters flat-out isn’t supported at all in C#.
I get what you’re saying about being explicit though and the link you provided is good stuff. Its a thing I keep hearing again and again, but the reality is how explicit people think you should be seems to vary widely as far I can tell. Its usually very assumptive that you are working in a group environment and/or are planning to distribute the code and it irks me in the same way that linux assumes your laptop is gonna be used in a public library by dozens of people a day, mac assumes you are rich, and windows assumes you are a toddler.
That sir, was funny. I agree wholeheartedly. I would only amend the Windows part but I would add something that I will not publicly post.
I will grant you that an engineer should KNOW about all those constructs, if only because those things will exist in the wild, despite all my tilting-at-windmills, so it behooves a professional to be able to read it.
Your original construct is probably coming into conflict with some part of the new lambda-on-the-fly syntax. I reason this because the actual construct is completely valid as a regular old function:
Actually, I was a bit surprised by this since a lambda with a return type can hold a conditional expression. I can point you at the docs that explain this is the case, but you’d have to dig further if you wanted to know why.
One guess about why is that it might be hard to guess the type for the conditional_expression without the extra context of the lambda’s return type, though I think that’s pretty unlikely.
I’m more inclined to think this was an intentional decision based on the principle of “the pit of success”.
While this code is of pedantic interest, I have to agree with @Kurt-Dekker . As a general rule, I stay away from the conditional operator.
This sort of post is what makes the internet so great. There’s always somebody who knows a lot more about what you’re talking about! Perfect opportunity for me to learn.
Thanks a lot for the notes above @eisenpony … I think the same “pit of success” reason might explain why IEnumerators can’t be anonymous functions. I did see a blog post about it from the language designers some time ago but no idea where.
I think I overcomplicated my op as I often do. The real question at hand is this: is there a shorthand way to do:
void MyMethod(){
if(nonNullableVariable != whatever) return;
//execute a single statement here;
}
I feel like this is some kind of guard clause-ish use-case that is so common it should exist, but I wouldn’t know how to search for whether or not it does because I don’t know what to call it, assuming it did lol
I’ve googled around for some context on this, only finding a vague discussion of overengineered codebases and how OOP pushes novices like me to premature optimization. Where exactly does that term originate from (in the context of software dev)? kinda sparked my curiosity
The “pit of success” is a design philosophy which basically says “design the system so that it’s easier to make decisions that follow best practices (and harder to do unusual things that lead to problems down the road)”.
I think this is the seminal post about how the pit of success applies to the CLR
Eric Lippert mentions it when talking about his experience with C and the design of C#
I’m not sure what you mean by this … This seems to be fine:
Func<int, IEnumerator> test = i => Enumerable.Range(0, i).GetEnumerator();
Not really. Weenie really is the correct euphemism here; as in “teeny weenie”; as in, I only know a tiny bit.
But … I am endlessly fascinated by the stuff.
And … have endless respect for the people who do get this stuff well enough to have created anything resembling the C# language.
It’s amazing how deep and complex this stuff gets as soon as you peel off a few layers.
When I try that now I get complaints that the compiler is considering my anonymous delegate to be a string, so that’s NEVER going to work, but for completely different reasons.
Taking it one silly step further (silly in the context that I am not a langween), these don’t work either:
Okay I see. You’re talking specifically about the yield statement.
Since I don’t work in Unity much, I’ve never wanted to put a yield statement in an anonymous method, though I can see why that would be useful in the context of coroutines.
You’re right, this could be a result of the pit of success principle, but it could also have been a feature that was cut because, i) it didn’t seem particularly useful (outside of Unity), ii) there is a good workaround (make a non-anonymous method), and iii) there was probably extra complexity in generating the enumerator state machine when dealing with closures.
The reason I stressed could is because this is apparently possible to do with local functions. So, assuming you are on C# 7, you can do something like:
Getting back to the idea of a one-liner, I’ve seen code like this before:
public void Foo(Bar b)
{
Helpers.AssertNotNull(b);
// .. use b however
}
I suppose you could extend that idea out to a helper that executes based on a bool, but I can’t think of a way to do it without creating a delegate … so that would generate garbage.
Frankly, unless you are doing something considerably more complex, I would just go with the idiomatic, and loosen any requirements for wrapping parenthesis.
public void Move(Vector3 d)
{
if (d != Vector3.zero)
transform.position += d;
}
Though … I have to wonder, what is the point of making this check?
In this case it looks like you are trading an “always add” for an "always compare, sometimes add, AND a branch".
I wasn’t aware of this. As for what I was doing, well nothing practical in this particular case… it was just something I happened to stumble upon when trying to use a wrapper function to keep a guard clause from interfering with a continuous update loop where say, you want to execute a series of statements conditionally but without breaking the loop if !whatever. It just so happened I only had one statement at the time and it was dealing with a non-nullable when I discovered the “gotcha” that I didn’t understand as I tried to lambda it. The reality is the actual functionality desired can be achieved with a simple if statement. I’m a newbie that learns in reverse gear so I often find the most convoluted ways of justifying the need for super basic stuff and it usually takes a forum post or a discord hazing session for me to remember to KISS
Found another gotcha, this time with the ternary operator that returns an interface. Weird thing about this one is that omnisharp (vscode) doesn’t mind it at all, however, in unity console it won’t compile because “Type of conditional expression cannot be determined because there is no implicit conversion”. Is it common to have syntax/code errors or rules in unity that doesn’t apply to the language itself? (not talking about logic errors)
IController _controller;
// No omnisharp errors but an error in unity console
_controller = isHuman ? _humanControl : _artificialControl;
// same result
IController SetController() => isHuman ? _humanControl : _artificialControl;
_controller = SetController();
// No errors at all
IController SetController()
{
if (isHuman) return _humanControl;
else return _artificialControl;
}
_controller = SetController();
Basically I’m trying to call an overloaded method that returns void based on a conditional within OnShoot using a discard but I’m getting told that you can’t return void here by Omnisharp. Skimming over the msdocs I can’t really see a reason why it wouldn’t work as the docs mention nothing about not being able to return void to a discard, yet, here we are. Can’t do it.
Can you do anything cool in C# or are you pretty much stuck with brackets and whitespace?
This sounds like it could be a bug in Unity.
Are you compiling with il2cpp?
Can you share the declarations for _humanControl and _artificalControl?
I’d like to say no, but these things are very complex and I don’t have enough experience with Unity to say one way or another how many bugs are hiding within.
I do know that il2cpp is still a pretty new technology, so if you’re out on the edges of what’s possible …
You’re bound to run into some rough patches.
As I said earlier, I tend to avoid the conditional operator. It’s not that it’s wrong to use it – there are some very legitimate use cases – just that it isn’t self explaining and, as a result, isn’t used a lot in the C# I have seen. I suspect it’s rarity might have led to less attention from implementers of new compilers.
Are you saying that Rotate is a void method?
In that case, even if this was a correct syntax, the assignment – even to a discarded variable – doesn’t make sense in C#. You can’t assign void to a variable.
More over, I’m seeing a pattern in your questions here regarding the conditional operator. In C#, this operator is an expression. This means it needs to have a “value” … aka, it should resolve to a result. Since void is not a result, you can’t use the conditional operator to choose between two void methods.
I tend to think so, but if your idea of cool is the conditional operator and other obscure language syntax, then I guess you might be disappointed with the clarity that C# encourages.
I’m reading between the lines here a bit so you can correct me if I’m off base… but it looks like you might be switching your method calls to a dependency (the controller) based on the underlying type of the dependency. It’s a bit obscured here because you are using a flag instead of explicitly using the typeof or is operator, but the result is the same.
This is a violation of the Liskov substitution principle – the L in the SOLID principles popularized by Robert Martin. The rule is basically that your code should agnostic about the underlying types of its dependencies and treat them as a generic implementation of the interface.
On the other side of this coin, I’m guessing you have created both a Rotate() and Rotate(Vector3) method on your base type or interface. This is making your interface larger than it needs to be, which is a violation of the interface segregation principle – the I in SOLID.
It looks like you’ve got the first idea about interfaces, but I think you went the wrong direction when dealing with this particular issue. It’s considered bad for your client code to know about the underlying type of the dependency and to have extra methods in your interface to support various clients. However, it’s perfectly okay to create implementations of an interface that ignore parameters passed to its methods. So instead of creating two versions of Rotate, just create the one that takes a Vector3 and ignore it when the controller is the human version.