|
Munkeymon, I think we're arguing two different things right now. The main point I'm trying to make is that method signatures that are very long are almost always indicative of some form of God Function that does way too much. The solution to this problem is not just to take those very long method signatures and put them into a class, then pass an instance of the class, that would be silly. The signature is just the symptom, the God Function is the problem. Fix the problem. Again, without a specific situation it's hard to give concrete examples of the best way to fix a God Function, but there is almost always a better way to architect your program that will improve test-ability, readability, maintainability, and usability.
|
# ? Jan 16, 2014 21:01 |
|
|
# ? Jun 4, 2024 11:36 |
|
That's all well and good but I think it's a little bit simplistic to say any code that is easier to read with named parameters is wrong. If that were really the case why would they have added such a feature at all?
|
# ? Jan 17, 2014 00:21 |
|
To ease interoperability with with APIs that aren't able to leverage overloading (e.g. COM).
|
# ? Jan 17, 2014 00:35 |
|
Well, fair enough, I suppose. I like to use them even when there aren't a lot of arguments. Particularly with bools because it's not obvious what you were specifying when you put "false" in something.
|
# ? Jan 17, 2014 00:46 |
|
Munkeymon posted:Look at Bognar's post about not passing models around. Yeah, I was definitely just referring to data models there. If you have a parameter object that's specific to your function, then you don't run into the problems I was bitching about (e.g. modifying a data model can cause unexpected behavior in your methods that include it in its signature). I'm not really a fan of parameter objects in general, though, mainly for the reason that if I change a method and add or remove a parameter then my code could still compile while being incorrect and defer the errors to runtime. If my method signature instead lists all of its requirements explicitly, I can't compile the code until I fix everything that the change broke. Obviously you should still have the runtime checks for invalid data being passed in, but I like to lean on the compiler wherever possible. This isn't exactly relevant in the situation of optional/named parameters, but I would still prefer those to parameter objects.
|
# ? Jan 17, 2014 02:36 |
|
I can't say I'm a huge fan of parameter objects either; for all the reasons you've discussed. And in any case it's hardly getting at the root of the problem (assuming it is one and you don't have a legitimate need for lots of parameters).
|
# ? Jan 17, 2014 02:59 |
|
I don't frequently use "parameter objects", but when I do, they do a lot more than just being a struct to pass data around.
|
# ? Jan 17, 2014 14:29 |
|
Someone help me wrap my head around this loving thing I'm looking at. Some background on what the control does (I can't really speak about in in depth for confidentiality reasons) is that you can enter a keyword like "payment" in this textbox, and there's a dropdown right next to the combobox that populates with all of these database columns whose metadata contains a description of the word "payment." But the code behind is literally just this for these two controls. code:
code:
Can someone explain to me likely how this works? I want to move this filter out of the usercontrol and have a dedicated lookup elsewhere in the UI. e: poo poo, I found it. It's not quite some magic anymore. I thought the DependencyProperty was doing something magical. Macichne Leainig fucked around with this message at 16:51 on Jan 17, 2014 |
# ? Jan 17, 2014 16:32 |
|
Dietrich posted:Munkeymon, I think we're arguing two different things right now. I guess we were, but I'd like to point out that this is the entirety of what I was disagreeing with: quote:it could benefit from passing in an object that encapsulates the necessary arguments Which I think you just called silly? I really don't like parameter objects, is all
|
# ? Jan 17, 2014 16:32 |
|
Munkeymon posted:I guess we were, but I'd like to point out that this is the entirety of what I was disagreeing with: Let me clarify. I think outsourcing argument validation from the body of an overly-long method would be a beneficial change. I think we then got into a discussion about compiler "you missed an argument" errors versus my point that argument validation needs to be about more than just missing arguments. But that was leading us away from my primary point, which was that I think an even more beneficial change would be getting rid of the overly-long method.
|
# ? Jan 17, 2014 16:44 |
|
Dietrich posted:The main point I'm trying to make is that method signatures that are very long are almost always indicative of some form of God Function that does way too much. I'd agree with that for object-oriented design. But for functional programming? where everything really is a function? and the essence of the problem is that you have something that takes in a bunch of stuff and gives out an answer? then I think that lots of parameters are a true (and therefore good) way to represent the problem. One other thing to note is that parameter objects and named arguments can be pretty similar. For instance, code:
|
# ? Jan 17, 2014 17:55 |
|
All this named parameter talk has obscured the fact that among all those nice C# 6 features the multiple return values example is a big ugly lump. wtf is that syntax.
|
# ? Jan 18, 2014 01:56 |
|
Monkeyseesaw posted:All this named parameter talk has obscured the fact that among all those nice C# 6 features the multiple return values example is a big ugly lump. wtf is that syntax. And the null coalesce syntax? Hooray, an untyped maybe monad that throws NREs.
|
# ? Jan 18, 2014 03:10 |
|
Sedro posted:Really, all the language needs is some syntactic sugar for tuples, and that would be useful for more than just multiple return values. I shouldn't ever have to write Tuple.Create or tuple.Item1, tuple.Item2. They probably wanted to leverage the multiple return capabilities of the CLR. We were talking about this at work today and my co-worker convinced me that being able to return anonymous types would be a better solution. It's a bit quirky, but it allows you to have named properties instead of just relying on the position in the tuple. It might look like: C# code:
Sedro posted:And the null coalesce syntax? Hooray, an untyped maybe monad that throws NREs. I'm not sure I follow here. Isn't the point that it stops evaluating the expression once it encounters a null and thus doesn't throw NREs? Bognar fucked around with this message at 03:29 on Jan 18, 2014 |
# ? Jan 18, 2014 03:21 |
|
Bognar posted:We were talking about this at work today and my co-worker convinced me that being able to return anonymous types would be a better solution. It's a bit quirky, but it allows you to have named properties instead of just relying on the position in the tuple. It might look like: So return a dynamic then. I mean I know people are afraid of them but they're useful for any number of things.
|
# ? Jan 18, 2014 03:55 |
|
Bognar posted:
Bognar posted:I'm not sure I follow here. Isn't the point that it stops evaluating the expression once it encounters a null and thus doesn't throw NREs? Obviously they can't fix null at this point, but advocating its use through language features seems like a bad idea.
|
# ? Jan 18, 2014 04:06 |
|
Sedro posted:Obviously they can't fix null at this point, but advocating its use through language features seems like a bad idea. I honestly didn't expect to see prohibitionism brought up regarding programming language features, but I suppose it makes sense in retrospect. Letting people deal with nulls more cleanly isn't going to cause panic in the streets.
|
# ? Jan 18, 2014 05:20 |
|
RICHUNCLEPENNYBAGS posted:So return a dynamic then. I honestly wish dynamics had never been added. I get that they're useful for making COM interop code not look terrible, but I feel like the solution is to build a sane wrapper API around common COM interaction rather than adding duck typing to a statically typed language. I don't have a problem with duck typing in and of itself, I just don't want it littered throughout my statically typed code. The compiler will check my errors in all my statically typed code, but the compiler only looks like it's checking my errors in dynamically typed code. Also, just by virtue of having it as a language feature people will try to use it in places where it shouldn't be. This is one of those places. Sedro posted:They made some mention of structural types. In your example the return type would be something like { string Letter, int Count }. I don't remember seeing this in the stuff I read, but I would be very pleased if that was added. Sedro posted:Well, in a language where any reference can be null, everything is a maybe, and you're not going to change every dereference into a maybe-dereference. So without the help of the compiler you have to read through API docs to see if things return null. And if you mess one up, NRE. I don't think that this new feature encourages people to use null any more than they already do. It's already everywhere and strictly unavoidable due to the language design, so this is just syntactic sugar to make working around it suck less. Bognar fucked around with this message at 05:40 on Jan 18, 2014 |
# ? Jan 18, 2014 05:33 |
|
Munkeymon posted:It's not just you. Their pidgin SQL just gives me a headache because it's not SQL but looks similar enough that it requires extra mental effort to reason about what it's doing differently. At least that's what I think is going on. At any rate, I always write LINQ in object.Linq(lambda or function call) style because I find it a lot easier to both read and write. I can never remember how to do left joins in LINQ, among other things, for the same reason. However when you have a query like this the query comprehension syntax is a heck of a lot better than manually doing the key selection and whatnot for grouping: code:
Simulated fucked around with this message at 18:13 on Jan 18, 2014 |
# ? Jan 18, 2014 18:11 |
|
ljw1004 posted:Oh yes, it's getting incorporated into VB (I'm the VB design lead). I blogged about a few bits and pieces... Does the current C# design lead have a blog? Eric Lippert used to be my goto for that sort of thing. Please, for the love of StackOveflow, make Edit & Continue support Lambdas, anonymous types, etc. The inability to use it with any method containing those things combined with LINQ has effectively killed that feature for me and it was so drat handy for debugging (when it takes 15 minutes for your process to load several gigabytes from the database having to stop, build, and restart is a massive waste of time). Otherwise I am terribly excited about language support for immutability and I pray it happens. We are doing a sort of copy-on-write with immutable objects to support highly concurrent access to these multi-gigabyte bits of data and it is a billion times better than locking, even though we have to handle all the immutability by hand.
|
# ? Jan 18, 2014 19:01 |
|
Ender.uNF posted:(when it takes 15 minutes for your process to load several gigabytes from the database Maybe you should be debugging against a smaller data set? Or writing some unit testing your logic so you don't have to deal with databases at all?
|
# ? Jan 18, 2014 20:09 |
|
And here I thought dynamic was about working with JSON easier or when you don't feel the need to create an interface for some shared properties or whatever... I guess I should be glad I don't have to deal with COM stuff...
|
# ? Jan 18, 2014 20:39 |
|
Ender.uNF posted:I can never remember how to do left joins in LINQ, among other things, for the same reason. However when you have a query like this the query comprehension syntax is a heck of a lot better than manually doing the key selection and whatnot for grouping: I think I would have handled that with a view or stored procedure. Good lord.
|
# ? Jan 18, 2014 20:47 |
|
Ithaqua posted:Maybe you should be debugging against a smaller data set? Or writing some unit testing your logic so you don't have to deal with databases at all? For normal day-to-day debugging, that is what we do. But customers run with large data sets in the real world and the only way to be sure you don't fall down under that kind of load is to use it the same way. Plus we are heavily metadata-driven, which then acts off the user data so sometimes bugs can only be replicated by running a copy of the customer's database. Just to give some scope: We have an embedded JavaScript engine that fires customer-written validation scripts in response to realtime user actions. We have an in-memory transaction manager that knows how to roll-back changes to objects in memory (which we are dumping for a copy-on-write system). We do diffs and merges between these data sets... Imagine your entire file system is one git repo and now you want to do a diff and merge between a million+ files and each file's thousand bits of metadata, including tracking moves, deletes, renames, etc. Oh and the metadata has changed since the older snapshot was made. And some of the metadata is actually a piece of JavaScript that calculates the value dynamically because it is NP-hard to track dependencies closely enough to know when to invalidate cached values on writes, not to mention the space/lookup tradeoffs it would require. I don't think I will ever work on a piece of software this complicated again in my lifetime.
|
# ? Jan 18, 2014 22:17 |
|
Bognar posted:I honestly wish dynamics had never been added. I get that they're useful for making COM interop code not look terrible, but I feel like the solution is to build a sane wrapper API around common COM interaction rather than adding duck typing to a statically typed language. I don't have a problem with duck typing in and of itself, I just don't want it littered throughout my statically typed code. The compiler will check my errors in all my statically typed code, but the compiler only looks like it's checking my errors in dynamically typed code. Dynamic dispatch is a really useful idiom, anyway. I don't think anonymous types are really all that much "better" than dynamics in any of the aspects that bother you about dynamics.
|
# ? Jan 18, 2014 23:55 |
|
RICHUNCLEPENNYBAGS posted:Dynamic dispatch is a really useful idiom, anyway. I don't think anonymous types are really all that much "better" than dynamics in any of the aspects that bother you about dynamics. Except that anonymous types offer compile time type safety.
|
# ? Jan 19, 2014 07:15 |
|
RICHUNCLEPENNYBAGS posted:Dynamic dispatch is a really useful idiom, anyway. I don't think anonymous types are really all that much "better" than dynamics in any of the aspects that bother you about dynamics. Well anonymous types are just syntactical sugar, right? The compilers just autogenerating a class for me.
|
# ? Jan 19, 2014 09:10 |
|
Bognar posted:Except that anonymous types offer compile time type safety. I just don't see what kind of sense it makes returning them. If you think you're going to need the object in multiple places you should actually define it. Otherwise it seems to me you're getting an inappropriate level of coupling between methods. As for type safety, if you want you can cast a dynamic to get Intellisense or whatever. But to be honest I think the chances of this kind of error aren't super-high and also they're going to be really obvious when you run across them. RICHUNCLEPENNYBAGS fucked around with this message at 17:54 on Jan 19, 2014 |
# ? Jan 19, 2014 17:51 |
|
RICHUNCLEPENNYBAGS posted:I just don't see what kind of sense it makes returning them. If you think you're going to need the object in multiple places you should actually define it. Otherwise it seems to me you're getting an inappropriate level of coupling between methods. You are coupled to the method by definition. If I change the return type from byte[] to int, bool to float, or Frobber to Gobulator you're just as tightly coupled as if I were returning { x, y }. If I change the parameter list, other than adding new optional parameters or some limited type changes, all my callers must be updated. If I add, remove, or change out/ref parameters I just broke your poo poo yo. Same would apply to returning an anonymous type: anything other than adding new properties is a breaking change. Yes, a class must be defined... But let the compiler worry about it. You can certainly create iterators without yield return but why the hell would you? Let the compiler create that class for you. "you should define it" automatically smells like bullshit to me because you can apply that argument to everything besides pure assembly. No, actually, I shouldn't have to define it when the compiler can figure it out automatically.
|
# ? Jan 19, 2014 18:48 |
|
basically a Tuple<int,string> is not actually more 'defined' than a hypothetical {.NamedInt,.NamedString} structural type. it's just less convenient.
|
# ? Jan 19, 2014 20:57 |
|
RICHUNCLEPENNYBAGS posted:I just don't see what kind of sense it makes returning them. If you think you're going to need the object in multiple places you should actually define it. It's just syntactic sugar. Defining a class seems silly when it does nothing more than hold a few properties of data, but it's the best option if you want to have multiple return types. Tuples are the go-to method of handling this in many other languages because those languages have terse syntax for creating them. C# is getting better about Tuple creation in the next iteration, but ultimately it seems like it's inferior to defining an anonymous type. Anonymous types have a terse syntax and named properties, I can already use them locally in my code to great effect, so why not be able to return them as well? RICHUNCLEPENNYBAGS posted:As for type safety, if you want you can cast a dynamic to get Intellisense or whatever. Casting still isn't safe. You're telling the compiler "Yeah, this is what I think that variable will be" rather than the compiler knowing "this is exactly what that variable is". Also, if you're returning a dynamic because you didn't want to define a small class, what exactly are you going to cast the dynamic to? RICHUNCLEPENNYBAGS posted:But to be honest I think the chances of this kind of error aren't super-high and also they're going to be really obvious when you run across them. Sure, when you run across them at runtime as opposed to never having the problem at all because the compiler handled it all for you. I don't understand why you would willingly throw away the compiler's error checking when it's basically free. EDIT: Here's some example code: C# code:
Bognar fucked around with this message at 22:11 on Jan 19, 2014 |
# ? Jan 19, 2014 22:00 |
|
Ender.uNF posted:Same would apply to returning an anonymous type Allowing anonymous type returns is something that's been discussed in C# and VB language design meetings, but rejected. * It's clear that just returning tuples isn't enough, because when developers write "var (item1,item2,item3,item4) = foo()" they always forget which item is which, and they long for self-describing structures. * The types of anonymous types can't be expressed as types in C# or VB. Therefore, if we were to allow methods to return anonymous types, we'd have to open the door to implicit typing for any members. code:
* Implicit return types would also open the door to circularity! Even aside from circularity, C#/VB would need whole new sets of rules about the order in which members' types are resolved. (including with partial types). code:
Bognar posted:C# is getting better about Tuple creation in the next iteration No! Mads mentioned one possibility that has been raised but it's far from settled and not obvious that it's better or will be adopted. Mads described the idea that * When you write a constructor call "new Foo(args)" then, if there doesn't exist a nongeneric "class Foo" but there does exist one or more generic types like "class Foo<T>" or "class Foo<T,U>" then it will perform overload resolution on the constructors of all of those types to infer T. This would let you write "new Tuple(x,y)" in every place that you currently write "Tuple.Create(x,y)" and that's all the help it would provide for tuples. It would save you exactly four keystrokes every time you wanted construct a tuple. That's not much help. It wouldn't help the more common cases, e.g. code:
|
# ? Jan 20, 2014 02:38 |
|
ljw1004 posted:Allowing anonymous type returns is something that's been discussed in C# and VB language design meetings, but rejected. OK, I agree about nameless values but I disagree... I am not asking for var to end up in the ultimate public signature of an API. What I am asking for is to have the compiler treat return new { x=5, y=farts } as a public class with auto-properties x,y, each having the type inferred from the values if known at compile type, or ultimately object if it can't be deduced (or maybe you require the type be specified if it can't be inferred). So from the point of view of a consumer of this method, it has a type, just not one manually defined. Treating any instance of a similar type but with an additional member as inheriting from that type so you can add new members without breaking existing users would just be icing on the cake. People are doing this stuff now, they just use brittle stuff like dictionaries and lists to transport the values. quote:* Implicit return types would also open the door to circularity! Even aside from circularity, C#/VB would need whole new sets of rules about the order in which members' types are resolved. (including with partial types). So what happens if I create classes A : B and B: A today? That's easily identified as an error. I agree that something different from an anonymous class or an automatically defined class is probably ideal because the semantics are more like prototype-based languages, except the vast majority of the time the type of all the values will be known with reasonable specificity at compile time.
|
# ? Jan 20, 2014 03:34 |
|
Ender.uNF posted:You are coupled to the method by definition. If I change the return type from byte[] to int, bool to float, or Frobber to Gobulator you're just as tightly coupled as if I were returning { x, y }. If I change the parameter list, other than adding new optional parameters or some limited type changes, all my callers must be updated. If I add, remove, or change out/ref parameters I just broke your poo poo yo. Except now instead of reading a class definition to find out the properties of an object, you're going and reading through a method. This isn't really good design.
|
# ? Jan 20, 2014 04:27 |
|
Bognar posted:Sure, when you run across them at runtime as opposed to never having the problem at all because the compiler handled it all for you. I don't understand why you would willingly throw away the compiler's error checking when it's basically free. Yeah, great, but this is so trivial that I don't think it's worth worrying about. Using dynamics can save you from writing a lot of extraneous code.
|
# ? Jan 20, 2014 04:29 |
|
RICHUNCLEPENNYBAGS posted:Yeah, great, but this is so trivial that I don't think it's worth worrying about. Using dynamics can save you from writing a lot of extraneous code. I echo this, somewhat. Look, work with Javascript a while and you'll realize that it isn't that big of a deal. That being said, with proper tooling, it takes all of three seconds to create a type with the properties that you want to return when one doesn't exist. alt-enter-enter to freedom with resharper dudes.
|
# ? Jan 20, 2014 04:40 |
|
ljw1004 posted:Allowing anonymous type returns is something that's been discussed in C# and VB language design meetings, but rejected. All my hopes and dreams, dashed away. RICHUNCLEPENNYBAGS posted:Yeah, great, but this is so trivial that I don't think it's worth worrying about. Using dynamics can save you from writing a lot of extraneous code. I don't disagree that dynamics have their place, I just don't think that place is to deal with multiple return values when you could use tuples, anonymous types , or concrete classes instead. Dietrich posted:Look, work with Javascript a while and you'll realize that it isn't that big of a deal. As I said earlier, I don't mind dynamic typing. I just don't like it mixed in with static typing. My mindset in Javascript is expecting a lack of type safety, so I end up coding more cautiously (checking for existence of methods/properties before invoking them, etc.). In statically typed compiled languages, though, my mindset is expecting the compiler to enforce type rules. Dynamic typing throws those rules out the window at compile time. Bognar fucked around with this message at 07:09 on Jan 20, 2014 |
# ? Jan 20, 2014 06:59 |
|
ljw1004 posted:* The end result is that being able to return tuples or anonymous types is an interesting idea but not quite right. What's needed is a strongly-typed way to return a collection of named values. We haven't yet seen a good enough syntax for this. F# lets you do it but in a kind of mysterious way.
|
# ? Jan 20, 2014 08:12 |
|
Returning anonymous types is a really bad idea and breaks type safety. Each return from a method would need to be checked against every other to ensure they all match, even through deep hierarchies, reflection would be chaos as well. You would at least need { int:Number, string:Name } as a return type, which isn't so anonymous. What does Func<int,var> do? Funking Giblet fucked around with this message at 11:16 on Jan 20, 2014 |
# ? Jan 20, 2014 10:53 |
|
|
# ? Jun 4, 2024 11:36 |
|
In my world, you can't define Func<int,var> outside a method anymore than you can declare public property var Fizzulator {get;set;}. Var is just a placeholder that the compiler fills in at compile time. If the type can't be deduced then that's a compilation error. Circular dependencies are a compilation error. So when you see the VS tooltip for that var-returning method (or disassemble the IL), it is public { int x, string y } MethodName(). By definition, the compiler must be able to determine the shape of var or it won't compile. Having some slight CLR support for this (call it anonymous tuple or whatever) would make it easier for tooling to know what's up and to make it non-brittle by allowing addition of new members in the future, but isn't strictly required. What holes are there in this scheme? You guys seem to see this as some kind of major problem that I just can't see, so I'm genuinely curious where the idea falls down, subject to the stated limits. Edit: obviously multiple return points can't return different types in the same method... I think that may be the key unstated assumption missing here. The ultimate shape of the return type could either be required to be identical at all return points, or could be the union of all return points with unspecified members getting default(T). Simulated fucked around with this message at 20:41 on Jan 20, 2014 |
# ? Jan 20, 2014 20:38 |