TTTTTESTING RAW T-SQL QUERIES
-
@pie_flavor said in TTTTTESTING RAW T-SQL QUERIES:
@Captain No, not really. If you are holding onto a
List<Dictionary<,>>
, only you think it's aList<IDictionary<,>>
, what happens when you try to insert something that's anIDictionary<,>
but isn't aDictionary<,>
? However if it acceptsIEnumerable<IDictionary<,>>
that works fine becauseIEnumerable<>
is covariant (since it's read-only).Generic types are invariant by default for good reasons (because it affects all operations, not just the ones you're thinking about). Thus, it'd need to be written as
IEnumerable<out IDictionary<string,string>>
to be covariant (or is thatin
instead ofout
? I don't work with these things enough to remember which is which, and instead just use the old favourite of “use the other one if the compiler complains”).
-
@dkf said in TTTTTESTING RAW T-SQL QUERIES:
IEnumerable<out IDictionary<string,string>>
C# has use-site variance?
-
@pie_flavor said in TTTTTESTING RAW T-SQL QUERIES:
C# has use-site variance?
No idea! I know that Java has syntax for controlling generic type covariance and contravariance, so I searched for the same thing in C# and found that FAQ, which had an example of the syntax. I don't know if I picked the right option, and I've not got a toolchain set up to find out.
-
@dkf said in TTTTTESTING RAW T-SQL QUERIES:
just use the old favourite of “use the other one if the compiler complains”
-
@dkf I was wondering if you were confusing C# with Java there. C# has declaration-site rather than use-site variance. That is, it's the class or method which declares
in
orout
, not the variable. And it's easy to remember whether it'sin
orout
- it's whether the type is an input or an output. It isIEnumerable<out T>
because T is an output, soIEnumerable<Subclass>
extendsIEnumerable<Superclass>
.Action<>
is declaredAction<in T>
becauseT
is an input, soAction<Superclass>
extendsAction<Subclass>
.
-
@pie_flavor said in TTTTTESTING RAW T-SQL QUERIES:
@Zecc Oh, my brain misplaced the parenthesis. Still, I've no idea why you think you need the
+ @"\"
.Presumably he wants to match
C:\Program Files\...
but notC:\Program Files Real\...
.EDIT: Err...forgot to go back and see where he was putting that...I blame the pollen haboob.
-
@boomzilla said in TTTTTESTING RAW T-SQL QUERIES:
Err...forgot to go back and see where he was putting that...
What am I not seeing?
@boomzilla said in TTTTTESTING RAW T-SQL QUERIES:
haboob
Ha. Boob.
-
@Zecc said in TTTTTESTING RAW T-SQL QUERIES:
@boomzilla said in TTTTTESTING RAW T-SQL QUERIES:
Err...forgot to go back and see where he was putting that...
What am I not seeing?
Hmm...I dunno. I'll bet I've done less .net than you and I'm not sure what
GetFullPath
does. But my answer seemed like the obvious answer to a question's like @pie_flavor's. I just don't know if it makes sense in context of the method.
-
@boomzilla said in TTTTTESTING RAW T-SQL QUERIES:
But my answer seemed like the obvious answer to a question's like @pie_flavor's. I just don't know if it makes sense in context of the method.
If "your answer" is "Presumably he wants to match C:\Program Files... but not C:\Program Files Real....", then yes that's why I added the extra slash.
My "what am I not seeing" question was regarding your edit. It sounded like after going back you saw something I was missing.
-
@Zecc said in TTTTTESTING RAW T-SQL QUERIES:
My "what am I not seeing" question was regarding your edit. It sounded like after going back you saw something I was missing.
I just looked again at the methods and wasn't sure what was going on in there. Sorry for the confusion.
-
I'm pretty happy with how this all turned out, and have some ideas for more scenarios to encode as JSON objects.
Just today, I've had 5 commits. Sure, they're little baby things, but each one is a bug that got fixed, with tests to... sound the alarm if it breaks again.
-
@Zecc But it wouldn't have any effect on that.
-
@Captain said in TTTTTESTING RAW T-SQL QUERIES:
Sure, they're little baby things, but each one is a bug that got fixed, with tests to... sound the alarm if it breaks again.
That's often the best sort of test.
-
OK, now I am deeply unhappy with my
testing stuffLinq and C#.I'm having some tests pass spuriously, and it's very annoying.
I'm "positive" I tested the case that's letting that happen, when I wrote the testing stuff, but of course I didn't write a unit test to test my tests.
The code that fails looks like:
public void SatisfiesWorksWithZip () { IEnumerable<Dictionary<string,string>> expect = new List<Dictionary<string, string>>() { new Dictionary<string, string>() { { "MockKeyA", "MockValueA" }} , new Dictionary<string, string>() { { "MockKeyB", "MockValueB" }} , new Dictionary<string, string>() { { "MockKeyC", "MockValueC" }} }; IEnumerable<Dictionary<string,string>> incorrect = new List<Dictionary<string, string>>() { new Dictionary<string, string>() { { "MockKeyA", "MockValueX" }} , new Dictionary<string, string>() { { "MockKeyB", "MockValueY" }} , new Dictionary<string, string>() { { "MockKeyC", "MockValueZ" }} }; Enumerable.Zip(expect, incorrect, (e, r) => new { Result = r, Expected = e }) .Select(i => SqlTest.Satisfies(i.Result, i.Expected)); }
But, LOL, this code I copied and pasted in the same file doesn't compile! It's telling me that I can't pass a Dictionary where an IDictionary is expected, again...
Recall that
Satisfies
just iterates through all the keys and compares against the result.public static void Satisfies(IDictionary<string, object> result, IDictionary<string, string> expected) { foreach (string k in expected.Keys) { Assert.AreEqual(expected[k], result[k].ToString()); } }
Oh after debugging it all... it turns out this all comes down to
Satisfies
returningvoid
, soSelect
wasFAILING SILENTLY
I wish Microsoft hadn't hired SPJ just to make a mockery of his work.
-
@Captain and you have done the exact same thing as last time, which is to attempt to pass something as covariant when it is not covariant, and blame it on the class-to-interface thing instead.
Dictionary<string, string>
does not coerce toIDictionary<string, object>
because the second parameter is not covariant, because you could then saya[b] = 4
which is definitely not a string. The solution, as before, is to take the type which most closely represents what you want to do with it. Either takeIDictionary<string, string>
, orIReadOnlyDictionary<string, object>
.Also trying to use Select with a void function, as last time. Again, use a foreach statement. You end up having to use one anyway since Select, like most other LINQ functions, is a lazy-eval. It wasn't failing silently. You were constructing a sequence and then never evaluating it.
-
@Captain said in TTTTTESTING RAW T-SQL QUERIES:
Enumerable.Zip(expect, incorrect, (e, r) => new { Result = r, Expected = e }) .Select(i => SqlTest.Satisfies(i.Result, i.Expected));
Pretty sure that won't do anything other than instantiate an IEnumerable.
-
@pie_flavor All of that worked.
-
@pie_flavor WTF do I have to type to "eval" a Select statement? Just being a top level expression in scope isn't enough?
FUCK THIS LANGUAGE.
These choices by Microsoft make the language worse than it has to be.
-
@Captain See my post. Just add a .ToList() or .ToArray() or .Count() or something.
-
@Captain when you say something like Select or Where, it does not evaluate the entire sequence and give you a new one with all the things appropriately transformed. It instead gives you a wrapper around the original sequence which maps the values as they come out. This is basically the implementation of Select:
public static IEnumerable<R> Select<T, R>(this IEnumerable<T> enumerable, Func<T, R> func) { foreach (var t in enumerable) { yield return func(t); } }
You have to iterate through sequences to evaluate them. This is why they are supposed to be transformative, and the actual actions get put in a foreach statement.
You can in fact do what @Zecc said, but it's better design to put the action part in a foreach instead of trying to do your terminal operation with a Select.
-
@pie_flavor said in TTTTTESTING RAW T-SQL QUERIES:
transformative, and the actual actions get put in a foreach statement.
Please explain what the difference between "transformative" and "actual" actions
-
@Captain Things with side effects, essentially. Leave functional operations to the functional bits and imperative operations to the imperative bits.
-
@pie_flavor UGH. Microsoft needs to fix this void bullshit.
-
@Captain Alternatively, you could write the code the way it is intended to be written.
If it were me, I'd just make the validation function return bool instead of throwing an exception, and then end the LINQ sequence with a
Any(x => !x)
, and feed that into an Assert from the caller's side.
-
@pie_flavor said in TTTTTESTING RAW T-SQL QUERIES:
Alternatively, you could write the code the way it is intended to be written.
lol wut
C#'s type system is broken.
Microsoft chooses to make type errors into runtime errors.
etc.
Sorry, I count on my tools to help me. I know I'd suck ass at C++ specifically because of this kind of nonsense gotcha bullshit.
-
Oh great, Visual Studio crashed and its Git client isn't picking up changes to a file...
-
@Captain C#'s type system is not broken, you just expect
void
to be a type instead of the absence of one and for mutable types to be covariant which is not possible.
Additionally, you were the one talking about runtime type errors. The problems you are talking about are the result of not having runtime type errors.
Stop assuming covariance on mutable types and stop assuming LINQ works exactly like Haskell.
-
@pie_flavor No, it's broken.
you just expect void to be a type instead of the absence of one
What the hell is "the absence of a type"?
How is a type system supposed to type match when objects can have "no type"? How are you supposed to match against that VERY common case?
If anything, a function that returns void just returns no value. And "no value" is in EVERY TYPE.
You don't even see the morass this slopping thinking is sending you into.
These were choices.
Additionally, you were the one talking about runtime type errors. The problems you are talking about are the result of not having runtime type errors.
NO ERRORS!!!
Just silent type failure and silent runtime failure!!!!
I can vaguely let the
Select
-is-so-lazy-even-top-level-evaluation-won't-run-it-so-you-need-to-call-something-on-it issue slide. Vaguely. It's surprising, but okay. But this nonsense with void is the source of so MANY problems.
-
@Captain said in TTTTTESTING RAW T-SQL QUERIES:
@pie_flavor No, it's broken.
you just expect void to be a type instead of the absence of one
What the hell is "the absence of a type"?
How is a type system supposed to type match when objects can have "no type"? How are you supposed to match against that VERY common case?
Objects cannot have "no type". A void function simply doesn't return any kind of object at all.
If anything, a function that returns void just returns no value. And "no value" is in EVERY TYPE.
You don't even see the morass this slopping thinking is sending you into.
These were choices.
Yes. Were they bad ones?
Additionally, you were the one talking about runtime type errors. The problems you are talking about are the result of not having runtime type errors.
NO ERRORS!!!
Just silent type failure and silent runtime failure!!!!
I can vaguely let the
Select
-is-so-lazy-even-top-level-evaluation-won't-run-it-so-you-need-to-call-something-on-it issue slide. Vaguely. It's surprising, but okay. But this nonsense with void is the source of so MANY problems.What do you mean silent type failure?
All three of these things - trying to use LINQ for regular iteration, trying to use void as a value, and trying to use mutable collections covariantly, are not signs that the language is garbage but that you are attempting to write as though you were writing some other language instead of C#. You are not. The language works how it works. It does so for reasons. Both the working and the reasons are well documented. And instead of learning, you complain about precisely the same set of three problems twice in a row, to precisely the same explanation.
Learn.
-
@pie_flavor said in TTTTTESTING RAW T-SQL QUERIES:
you just expect void to be a type instead of the absence of one
That's BS. The “absence of a type” is what you have when you have a function that doesn't ever return, or (depending on how error semantics are done) where the function always throws an exception. By comparison,
void
is the type that only has one value with no operations on it (and so which doesn't normally need to be talked about);void
isn't a reference type.
-
@dkf No, void does not have a value and is not a type. WTF are you on about?
Unless you're talking about the special-snowflakevoid.class
in Java which is only there so they didn't have to write two versions of the reflection.
-
If void isn't a type, then Microsoft doesn't know what a type is.
And they broke their type system with their stupid broken type.
The type system is unsound.
Fine, everything I wrote is MY FAULT.
And the compiler should have caught it statically.
All of it.
Not letting tests pass silently.
-
@pie_flavor said in TTTTTESTING RAW T-SQL QUERIES:
Yes. Were they bad ones?
YES
Their type system is
UNSOUND,
which is a really shitty thing for a strongly typed language to be.
-
@Captain said in TTTTTESTING RAW T-SQL QUERIES:
@pie_flavor No, it's broken.
you just expect void to be a type instead of the absence of one
What the hell is "the absence of a type"?
How is a type system supposed to type match when objects can have "no type"? How are you supposed to match against that VERY common case?
If anything, a function that returns void just returns no value. And "no value" is in EVERY TYPE.
No, that's a
null
orNothing
(I think that's C#'s term for it). A function that "returns"void
mean there won't ever be anything returned. The end of the function simply passes processing back to whatever the calling environment was doing.
-
@djls45 In haskell, it's called (). When you want a function that "returns void" , you type it as
foo :: IO ()
. And you might even have to literally typemain :: IO () do printStr "Stuff" moreStuff return ()
Now, does the compiler literally pass around a value at runtime when I return ()? No, it gets optimized out. (A sane use of laziness...)
But that's an optimization.
What I'm saying is that allowing "a function that returns no value" as a separate TYPE of thing as "a function that returns null or ()" is a MISTAKE (i.e., by Microsoft). Now there's the "null kind of nothing", and the "void kind of nothing", and together they serve no purpose but to allow mistakes to happen or introduce complication. Types that should match don't.
-
@pie_flavor said in TTTTTESTING RAW T-SQL QUERIES:
All three of these things - trying to use LINQ for regular iteration, trying to use void as a value, and trying to use mutable collections covariantly, are not signs that the language is garbage but that you are attempting to write as though you were writing some other language instead of C#. You are not. The language works how it works. It does so for reasons. Both the working and the reasons are well documented. And instead of learning, you complain about precisely the same set of three problems twice in a row, to precisely the same explanation.
Covariance had nothing to do with this issue, which I resolved the same way as before. With IEnumerable. Notice that I didn't complain about that.
The "silent type failure" I refered to occurred when the stupid compiler let me write a program that failed with a type error at runtime. And no, I wasn't using any fancy dynamic stuff.
Yes, I DO complain about C#'s crappy functional implementation. If C#'s imperative language offered the same guarantees as a functional strongly typed functional language is supposed to, I'd use that. But it doesn't. So I compromise and use the functional fragment. And then that sucks too. It's like it was designed to ensure job security at Mediocre Corp.
-
@Captain What did it let you do, and what type error did it fail with? That sounds wrong.
Also, what are these guarantees you speak of?
-
@Captain Perhaps so, but it's a difference between procedural and functional languages, and in particular it's a holdover from C++ and C, so it goes back much further than Microsoft.
To me it makes sense to distinguish between something that can have a value but just doesn't in this case and something that will never have a value.
-
@Captain said in TTTTTESTING RAW T-SQL QUERIES:
Yes, I DO complain about C#'s crappy functional implementation. If C#'s imperative language offered the same guarantees as a functional strongly typed functional language is supposed to, I'd use that. But it doesn't. So I compromise and use the functional fragment. And then that sucks too. It's like it was designed to ensure job security at Mediocre Corp.
If you want a functional language in the Visual Studio environment, you might prefer F# instead of the halfway-functional aspects of C#.
-
@Captain said in TTTTTESTING RAW T-SQL QUERIES:
What I'm saying is that allowing "a function that returns no value" as a separate TYPE of thing as "a function that returns null or ()" is a MISTAKE (i.e., by Microsoft). Now there's the "null kind of nothing", and the "void kind of nothing", and together they serve no purpose but to allow mistakes to happen or introduce complication. Types that should match don't.
"Should" and "mistake" (along with "serve no purpose") is entirely subjective. News flash: C# generally isn't like Haskell. Very few things are. If you get over that and write C# code instead of Haskell code but in C#, you will have a much better time of it.
Meanwhile, you can literally do exactly that and return()
.
-
@djls45 said in TTTTTESTING RAW T-SQL QUERIES:
To me it makes sense to distinguish between something that can have a value but just doesn't in this case and something that will never have a value.
Yes, that makes sense. This is slightly different.
What if, instead of
public void Foo
, we could write our signatures likepublic null Foo
, which makes it explicitly clear thatFoo
only ever returnsnull
?This is the situation I wish we were in.
@pie_flavor said in TTTTTESTING RAW T-SQL QUERIES:
"Should" and "mistake" (along with "serve no purpose") is entirely subjective.
Nope. There's plenty of theory on this. The type system is UNSOUND and Microsoft is WRONG.
-
@Captain said in TTTTTESTING RAW T-SQL QUERIES:
What if, instead of
public void Foo
, we could write our signatures likepublic null Foo
, which makes it explicitly clear thatFoo
only ever returnsnull
?That seems... completely useless. It's going to return an object that you can't do anything with. What additional functionality does having that
(Object)null
give you?
-
@Unperverted-Vixen It makes things that should type check type check, as opposed to pretending
void
is some non-type, which breaks many things when reasoning about code.Obviously, you're not going to do anything with that null. And a sane compiler would optimize any
return null
s out (a sane use of laziness).But to say that you can't map over a collection with a void function because "there is nothing to put in the collection" is
RIDICULOUS
I say, if there is nothing to put in, PUT THE NOTHING IN. :-)
-
@Captain Then return the nothing from your function as
()
. Or, alternatively, have a checker function that returns whether it succeeded rather than being void and throwing if it failed. But don't complain that things don't work like you're used to, as though that is the One True Way to do things. IMO void functions are nice because things that explicitly do not return any kind of value can be reasoned to operate entirely via side effects.
-
@Captain said in TTTTTESTING RAW T-SQL QUERIES:
@Unperverted-Vixen It makes things that should type check type check, as opposed to pretending
void
is some non-type, which breaks many things when reasoning about code.If I know it returns
null
, I'm not going to assign the result to anything, so there's nothing to type-check.But to say that you can't map over a collection with a void function because "there is nothing to put in the collection" is
RIDICULOUS
I might be misunderstanding your use of the verb "map" here - if so, apologies. But if I'm understanding correctly, you can map over the function just fine using the
foreach
keyword. They just didn't give you the ability out of the box to embed it into a fluent pipeline, for well-considered reasons.Implementing it yourself would've taken less time then you've spent posting about it, I think. :)
-
@Unperverted-Vixen Funny, I've said exactly that three times.
-
@pie_flavor said in TTTTTESTING RAW T-SQL QUERIES:
operate entirely via side effects.
But that wouldn't be functional!!!1!!1!1!!!1!!1!1! Side effects are the EVILZ!
-
@Captain said in TTTTTESTING RAW T-SQL QUERIES:
What if, instead of
public void Foo
, we could write our signatures likepublic null Foo
, which makes it explicitly clear thatFoo
only ever returnsnull
?You could get the same effect by saying that the type
void
only has one member value,null
. That'd make it a subtype of every reference type automatically without changing the meaning ofnull
much.But really you want to have a different type be the bottom, so that both reference and non-reference types can be greater than it.
-
public () CaptainIsNotReadingMyPosts() { return (); }
-
IMO void functions are nice because things that explicitly do not return any kind of value can be reasoned to operate entirely via side effects.
That's true whether
void
is inhabited or not.