TTTTTESTING RAW T-SQL QUERIES


  • Discourse touched me in a no-no place

    @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 a List<IDictionary<,>>, what happens when you try to insert something that's an IDictionary<,> but isn't a Dictionary<,>? However if it accepts IEnumerable<IDictionary<,>> that works fine because IEnumerable<> 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 that in instead of out? 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”).


  • Considered Harmful

    @dkf said in TTTTTESTING RAW T-SQL QUERIES:

    IEnumerable<out IDictionary<string,string>>

    C# has use-site variance?


  • Discourse touched me in a no-no place

    @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”

    😆


  • Considered Harmful

    @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 or out, not the variable. And it's easy to remember whether it's in or out - it's whether the type is an input or an output. It is IEnumerable<out T> because T is an output, so IEnumerable<Subclass> extends IEnumerable<Superclass>. Action<> is declared Action<in T> because T is an input, so Action<Superclass> extends Action<Subclass>.


  • ♿ (Parody)

    @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 not C:\Program Files Real\....

    EDIT: Err...forgot to go back and see where he was putting that...I blame the pollen haboob.


  • 🚽 Regular

    @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.


  • ♿ (Parody)

    @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.


  • 🚽 Regular

    @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.


  • ♿ (Parody)

    @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.


  • Considered Harmful

    @Zecc But it wouldn't have any effect on that.


  • Discourse touched me in a no-no place

    @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 stuff Linq 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 returning void, so Select was

    FAILING SILENTLY

    I wish Microsoft hadn't hired SPJ just to make a mockery of his work.


  • Considered Harmful

    @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 to IDictionary<string, object> because the second parameter is not covariant, because you could then say a[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 take IDictionary<string, string>, or IReadOnlyDictionary<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.


  • 🚽 Regular

    @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.

    512dffc7-a011-47e9-b602-6a9962a59bc6-image.png



  • @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.


  • 🚽 Regular

    @Captain See my post. Just add a .ToList() or .ToArray() or .Count() or something.


  • Considered Harmful

    @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


  • Considered Harmful

    @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.


  • Considered Harmful

    @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...


  • Considered Harmful

    @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.


  • Considered Harmful

    @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.


  • Discourse touched me in a no-no place

    @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.


  • Considered Harmful

    @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-snowflake void.class in Java which is only there so they didn't have to write two versions of the reflection.



  • @pie_flavor

    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 or Nothing (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 type

    main :: 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.


  • Considered Harmful

    @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#.


  • Considered Harmful

    @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 like public null Foo, which makes it explicitly clear that Foo only ever returns null?

    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.


  • And then the murders began.

    @Captain said in TTTTTESTING RAW T-SQL QUERIES:

    What if, instead of public void Foo, we could write our signatures like public null Foo, which makes it explicitly clear that Foo only ever returns null?

    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 nulls 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. :-)


  • Considered Harmful

    @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.


  • And then the murders began.

    @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. :)


  • Considered Harmful

    @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!


  • Discourse touched me in a no-no place

    @Captain said in TTTTTESTING RAW T-SQL QUERIES:

    What if, instead of public void Foo, we could write our signatures like public null Foo, which makes it explicitly clear that Foo only ever returns null?

    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 of null 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.


  • Considered Harmful

    @dkf

    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.


Log in to reply