Hacking C#: Development for the Truly Lazy - Simon Painter - NDC Copenhagen 2022

Поділитися
Вставка
  • Опубліковано 6 лис 2024

КОМЕНТАРІ • 85

  • @tordjarv3802
    @tordjarv3802 2 роки тому +45

    Maybe I'm to much of a physicist but I think that 9*tempCentigrade/5 + 32 is more readable than anything presented in this video.

    • @pagorbunov
      @pagorbunov 2 роки тому +2

      You need to abstract cause it was just a simple sample.

    • @simonpainter2242
      @simonpainter2242 2 роки тому +8

      In the real world, I probably would do something like that, especially as it doesn't really matter for this one example. I just didn't want to get too bogged down in real-world detail when I was talking through the example.

    • @tordjarv3802
      @tordjarv3802 2 роки тому +2

      @@pagorbunov while it is true that using other forms of chaining operations together in more complicated situations is better, this was a talk about being a lazy programmer so making an example where the end result is more complicated than the starting point kind of defeats the purpose of the example.

    • @tordjarv3802
      @tordjarv3802 2 роки тому +2

      @@simonpainter2242 It is ok. I really enjoyed your talk. I particularly liked your approach to add default values in case of missing values.

    • @simonpainter2242
      @simonpainter2242 2 роки тому

      @@tordjarv3802 That's a good point. I'll have a think about that particular example for the next iteration of this talk, Thanks :)

  • @redpepper74
    @redpepper74 2 роки тому +9

    After learning Java and Python for a year or so and picking up some Haskell, C# looks like the best of both worlds when it comes to OOP and functional. Extension methods seem great for general-purpose libraries,
    and LINQ looks like straight-up good declarative programming. It’s exciting seeing all the stuff .NET can be used for, can’t wait to get started.

    • @simonpainter2242
      @simonpainter2242 2 роки тому +5

      I like it. LINQ is one of C#'s very best features. Along with Pattern Matching and record types. I hope you enjoy working with it!

  • @tejeshreddy6252
    @tejeshreddy6252 2 роки тому +3

    This was an interesting presentation, Simon. Although I don't agree with a lot of your points, as some other commenters have pointed out, I appreciate the effort you've put into this. I also appreciate the civility you've displayed to bad faith comments, it is often hard to be unfazed by trolls in the comments. Kudos to you!

    • @simonpainter2242
      @simonpainter2242 2 роки тому +2

      Thanks. At the very least I hope I was entertaining :)

  • @alexclark6777
    @alexclark6777 2 роки тому +7

    36:00 Is the ContainsConsecutiveNumbers going to scale? Given that it's recursive (and I'm not 100% sure but I don't think C# will do some kind of fancy tail-recursion optimization) isn't this likely to smash the stack over a certain array length? It may have been a bit easier on the heap (and probably the stack!) to just iterate and compare current to previous, stopping when either the condition has been falsified (current != previous) or verified (end of array).
    In fact that might make for an interesting generic function, a "private static bool ConsecutiveAll" which takes an IEnumerable and does exactly that.

    • @simonpainter2242
      @simonpainter2242 2 роки тому +2

      It likely won't scale. Sadly C# doesn't implement any of the nice tail optimised recursion functionality you get in F#. Since writing this talk (about 3 or 4 years ago) I've learned it's also possible to do some of that with use of the Zip function in LINQ as well.

  • @ConductiveFoam
    @ConductiveFoam 2 роки тому +1

    This was very interesting, if not for the specific extensions then for the approach. Thinking I'll be able to use this for some of the legacy stuff I maintain for my day job actually..
    Thanks a bunch!
    I've got an extension I like quite a bit: Extracting the non-null elements in an enumerable
    public static IEnumerable NonNull(this IEnumerable source) => source.Where(e => e is not null).Select(e => e!)

    • @simonpainter2242
      @simonpainter2242 2 роки тому

      nice, I like that one. It's not a problem I encounter often, but I can see it being useful.

  • @DuRoehre90210
    @DuRoehre90210 2 роки тому +4

    Yeah, I miss coding C#. Most of this looks familiar to my previous achievements, having a toolkit of generic LINQ extensions methods for "any purpose". Extra boosted by Resharper.
    Python is picking up a on a few modern C# features like string interpolation, partly better and partly worse, but similar. And yet, it remains much less smooth than C# extension methods.
    24:32 I wouldn't necessarily agree. Adding ToArray or ToList ensures that the operation is finished, which might be important in case of databases (because locking). Passing IEnumerable around extends the runtime to ..

    • @AndersBaumann
      @AndersBaumann 10 місяців тому

      ToList() is very dangerous when you are working with a large database. I have experienced out-of-memory exceptions multiple times because of a ToList. In my company we have added code that puts a limit to the number of objects in memory to be robust against a misplaced ToList.

  • @akitelesforo
    @akitelesforo Рік тому +1

    Great talk, appreciate how you play c#, beauty of linq

  • @CRBarchager
    @CRBarchager 2 роки тому +11

    13:53 Shouldn't the second line of map be x => x / 5 ?

    • @larsthomasdenstad9082
      @larsthomasdenstad9082 2 роки тому +5

      Good catch. I hope your oven baked duck turned out OK.

    • @prouleau4440
      @prouleau4440 2 роки тому +3

      For me, that demonstrate the signal-to·noise ratio of that monad is terrible. The classic math formula on 1 line is way simpler and everyone learned to read and write that at primary school.
      Beside that, many interesting tricks.

    • @simonpainter2242
      @simonpainter2242 2 роки тому +2

      @@prouleau4440 Thanks. Perhaps I should swap the example at some point. I went with the temperature conversion as the simplest example I could think of, since I didn't want to get too bogged down in real-world detail.

    • @simonpainter2242
      @simonpainter2242 2 роки тому +2

      Thanks. I've had an email about that too. I've been doing this talk for years and no-one's ever noticed that mistake until today!

    • @rastislavsvoboda4363
      @rastislavsvoboda4363 2 роки тому +1

      @@simonpainter2242 couple of years ago I've used in real app:
      public static class DialService
      {
      public static bool DialTel(string number)
      {
      return number
      .Map(ToRawNumber)
      .Map(ToTelUrl)
      .Map(UIApplication.SharedApplication.OpenUrl);
      }
      public static bool DialSkype(string number)
      {
      return number
      .Map(ToRawNumber)
      .Map(ToSkypeUrl)
      .Map(UIApplication.SharedApplication.OpenUrl);
      }
      private static string ToRawNumber(string number)
      {
      return new StringBuilder(number)
      .Replace(" ", string.Empty)
      .Replace("-", string.Empty)
      .Replace("(", string.Empty)
      .Replace(")", string.Empty)
      .ToString()
      .Trim();
      }
      private static NSUrl ToSkypeUrl(string rawNumber)
      {
      return NSUrl.FromString($"skype:{rawNumber}?call");
      }
      private static NSUrl ToTelUrl(string rawNumber)
      {
      return NSUrl.FromString($"tel:{rawNumber}");
      }
      }

  • @kazimierzszadkowski1800
    @kazimierzszadkowski1800 2 роки тому +12

    In my opinion proposed "improvement" of ToFahrenheit is insanity. One liner would be best. Version with variables is still ok, readable and simple. Version with "Map" forces me to think what is actually going on there. Don't like it at all :(

    • @pagorbunov
      @pagorbunov 2 роки тому

      You need to abstract cause it was just a simple sample.

    • @PbPomper
      @PbPomper 2 роки тому

      I fully agree. Some of these proposals are not readable at all. Everyone loves small methods, but it shouldn't go at the expense of readiblity. But his most important message came across very well and I fully agree with abstracting boilerplate code by using generic extensions. Basically DRY and OCP.

    • @Robert-G
      @Robert-G 2 роки тому +1

      not to mention the runtime costs are orders of magnitude above the straightforward no-nonsense version

    • @simonpainter2242
      @simonpainter2242 2 роки тому +2

      ​@@PbPomper That was roughly the main principle I was trying to bring across. Most of the ideas I was presenting here were intended as inspiration for a way of thinking about C#, rather than "these are all of the extensions you need and how to use them". I didn't think readability was too bad, but that may be a personal preference thing. Always keen to hear if you've got more & better ideas though? These are just my personal time-savers that I tend to throw into new projects

    • @simonpainter2242
      @simonpainter2242 2 роки тому

      @@Robert-G I'm not sure they're necessarily orders of magnitude worse. I do tend to focus on brevity and readability over performance though. My reasoning is that it's easier these days to throw a bit of money at Azure than to have the developers spend longer working on an enhancement.

  • @lawrencejob
    @lawrencejob 2 роки тому +3

    Map deserves a deeper dive than it got in this presentation - it came across like speaker didn’t know about IEnumerable.Select but of course that’s not the case.
    I do think such a general use of monads defeats the point of using monads though (unless you have an agenda like handling nulls/optionals or errors) - this topic should have gotten more time imo bc I think it’s a hidden gem in this talk that probably confused people (reading comments like “Map==Select??”)

    • @simonpainter2242
      @simonpainter2242 2 роки тому +1

      I think I should make a point of explaining the difference between Map and Select the next time I do this. It's obvious to me, but I've been doing it for years, so it's easy for me to inadvertently miss out an important explanation.
      I didn't want to get too deep into FP and Monads in this talk - I have another in which I do a much deeper dive. I thought it might be interesting to at least touch on the subject. I probably should have chosen a better example, perhaps, though.

  • @CRBarchager
    @CRBarchager 2 роки тому +2

    42:30 The question section is difficult to follow along as you cannot hear the question. It would be great if the speaker would repeat the question before answering it.

    • @simonpainter2242
      @simonpainter2242 2 роки тому +3

      Apologies. I usually try and repeat the question. I must have missed this one. I'll try and be more vigilant in future!

    • @CRBarchager
      @CRBarchager 2 роки тому +1

      @@simonpainter2242 Thank you for answering.

  • @omarbousbia6916
    @omarbousbia6916 2 роки тому +2

    indexes are a cool feature in C#, but one downside of it is that you can't do go to definition on them

    • @simonpainter2242
      @simonpainter2242 2 роки тому +1

      that's true, but when they're used in the right place, it's a small price to pay, I feel

  • @michaelnurse9089
    @michaelnurse9089 2 роки тому

    UA-cam is buggy. I upvoted once for the talk and once for the Thundercats reference. Instead of recording two upvotes it did none. Strange.

    • @simonpainter2242
      @simonpainter2242 2 роки тому +1

      oh well, at least you enjoyed the talk, I hope?

  • @Robert-G
    @Robert-G 2 роки тому +7

    dictionaries that return null on not found are only useful in specific cases, and then they will be specific wrappers around an actual dictionary.
    Finding null is not the same as not having the value. Putting that into the bcl is not going to happen. There’s a GetValueOrDefault on immutable diction, it might come to the Enumerable extension class at some point…
    btw: That’s what TryGetValue is for, tells you whether you found it, and then let you use it, without looking it up twice, as the code is doing here (contains & indexer)
    I thought most of the tricks are really hacks that increase runtime cost sneakily without people knowing why their server are spending a lot more time on GC sweeps. And they also make stuff more convoluted not easier to follow at all.
    However, there were some nice nuggets I’ll take away from it. Thanks :-)

    • @simonpainter2242
      @simonpainter2242 2 роки тому +1

      Thanks. I'm glad I was some small use :) I'd tend to use the IDictionary wrapper I proposed here when I have a default value in mind. I tend to avoid uses of Null wherever it's possible. I'm not sure I agree it makes the code more convoluted, I was trying to cut down on the amount of code on display. My reasoning is that no-one necessarily cares what's behind the boilerplate extension method.
      I hope I was of some use though. Thanks so much for watching :)

    • @Robert-G
      @Robert-G 2 роки тому

      ​@@simonpainter2242 dang small screens. my post read a bit more negative than intended.
      But I do have a bit of a default skepsis against adding stuff that allocates or is cool, vs a no-nonsense in-your-face version.

    • @simonpainter2242
      @simonpainter2242 2 роки тому +1

      @@Robert-G I can see your point & I don't disagree. Amongst my aims here though, was to disguise recurring bits of boilerplate that we're all used to using with a meaningful description. In my head, it's better for junior developers, who want to quickly understand the meaning of a codebase, but without needing to decode the meaning of every little block of C#.

    • @Robert-G
      @Robert-G 2 роки тому

      @@simonpainter2242 I use the one where the variable part of the code can be passed as a closure all the time (you called it donut :D ).
      That’s perfect for reuse and also to hide code with lots of chest hair that needs to work in all kinds of scenarios, but shouldn’t be intimidating to use.
      Absolutely with you on that.

    • @simonpainter2242
      @simonpainter2242 2 роки тому

      @@Robert-G I honestly have no idea what the correct term for the Doughnut is :p I suspect it might be a "Thunk", but I'm probably wrong. It's definitely a powerful technique though & very in line with the SOLID principles.

  • @jalvrus
    @jalvrus 2 роки тому +4

    Extension methods and the ability to create "fluent" APIs is one of the best things to ever happen to C#. The funny thing about that is that it was all created to serve Linq-to-SQL, which is hardly ever used. JFYI, your dictionary extensions could be more efficient: this.TryGetValue(x, out var value) ? value : defaultValue; Also, don't forget default values for parameters: ToLookupWithDefault(this, K x, V defaultValue = default) => ...

    • @Kefir0
      @Kefir0 2 роки тому

      > Linq-to-SQL, which is hardly ever used
      What do you mean, LINQ to SQL translation is used in EF / EF Core which are everywhere.

    • @simonpainter2242
      @simonpainter2242 2 роки тому +2

      Thanks. I'll have a look at the TryGetValue code ready for the next time I do this talk.

  • @mar_sze
    @mar_sze 2 роки тому +5

    There were some nice things in this but also surprisingly many that I would consider actually pretty bad practice.

    • @simonpainter2242
      @simonpainter2242 2 роки тому

      which bits? I'm happy to look into any issues before I do this talk again

    • @mar_sze
      @mar_sze 2 роки тому

      @@simonpainter2242 I think most has already been mentioned other comments, and I mostly agree with it, such as:
      - Recursive approaches might scale badly
      - Using ContainsKey + get instead of TryGetValue
      - Returning null for missing keys
      - Over-func`ing. I use funcs and extension methods a lot (and I agree with the "rules" you set up, well done!), but also we need to keep in mind they do come with a cost and sometimes might make readbility worse than improve it (e.g. the chained .Map() instead of a simple expression)
      Btw, I never thought of using index with params, so that was a nice little nuget.
      Another trick I like: in some cases you could use generics instead of a type dictionary, which will be faster:
      private static class Impl { public static Func? Func }
      Impl.Func = s => s.Trim();
      Impl.Func = d => Math.Round(d);
      public static T Clean(T value) => Impl.Func?.Invoke(value) ?? value;

    • @Mr7Shane
      @Mr7Shane 2 роки тому

      @@simonpainter2242 The example of "Map", what is your problem with the local variables? What do you mean "waste local variables" and "var a is wasting spacing".

    • @simonpainter2242
      @simonpainter2242 2 роки тому

      @@Mr7Shane they're instantiated and used for a single line, then never used again. If the function were incredibly long, they're still being held in memory until the end of the function, even though they are of no further use.

  • @rastislavsvoboda4363
    @rastislavsvoboda4363 2 роки тому +2

    10:47
    this is IsNotNullOrEmpty, or did I miss something?
    public static bool IsNullOrEmpty(this IEnumerable @this) =>
    !@this?.Any() ?? true;

    • @simonpainter2242
      @simonpainter2242 2 роки тому +1

      Isn't IsNullOrEmpty for Strings? The example I gave was to do the same thing with Enumerables

    • @rastislavsvoboda4363
      @rastislavsvoboda4363 2 роки тому

      @@simonpainter2242 I mean your code is IsNotNullOrEmpty, not IsNullOrEmpty

    • @simonpainter2242
      @simonpainter2242 2 роки тому

      @@rastislavsvoboda4363 I see what you mean, I'll have a look and see whether a correction is needed. Thanks!

  • @cccyberfamilydk
    @cccyberfamilydk 2 роки тому +2

    Do you have a github link?

    • @oleggavrilov7083
      @oleggavrilov7083 2 роки тому

      So you could report and block that nonsense? Smart move mate

    • @simonpainter2242
      @simonpainter2242 2 роки тому +3

      @@oleggavrilov7083 No need to be unkind.

    • @oleggavrilov7083
      @oleggavrilov7083 2 роки тому

      @@simonpainter2242 please let me decide for myself

  • @cverde1234
    @cverde1234 2 роки тому +1

    I'd like to see the like/dislike ratio on this video. There are some good points but also some mistakes and bad code.

    • @simonpainter2242
      @simonpainter2242 2 роки тому

      Weirdly I have twice as many likes for this video as my FP video from Porto. I have no idea how many dislikes I have (and honestly I don't think I could live with that knowledge if it were available). Hopefully it's a sign that folks are feeling generous and forgiving my faults :) That or folks *really* hate FP.

  • @BryonLape
    @BryonLape 2 роки тому

    Developers not gathering requirements is the first big mistake.

    • @simonpainter2242
      @simonpainter2242 2 роки тому

      I can't disagree with you here, although in my experience, the biggest problem is the customer/business not actually *Knowing* what they want in the first place! Another reason I try to write very "low noise" code - it's easier to change if there's a proper confirmation of an unclear requirement.

    • @BryonLape
      @BryonLape 2 роки тому +1

      @@simonpainter2242 Never ask a customer what they want, they rarely know, and often when they think they do, its wrong. Ask them what they want to do. What are they trying to accomplish. How does that fit into the flow of their day.

  • @jessemorgan8226
    @jessemorgan8226 2 роки тому +1

    Most of these are the exact kind of black-box 'art' that make you cringe when you have to take over some pre-existing codebase and you can see the developer has gone out of their way to implement things using the most 'intelligent' implementations they can think of, using as many syntactic bells and whistles they can find, feeling proud of themselves that they have made such masterpieces of 'elegance' - but completely failing to recognise that so much of the elegance is in their head and not the code that it is actually obfuscating the codebase and overcomplicating it for anyone but them.
    He even mentions that there are probably newer syntactic bells and whistles in newer C# versions and that some of his designs may be 'out of date'
    I am also struck by the extra number of function calls his code will be generating causing more and more stack frame setup and usage.
    Simpler code is not out of date because there are new idioms available in newer language versions.
    Simpler code is not less lines either.
    Simpler code is code that takes the least time to reason about to a coder who has never looked at the codebase before.
    Newer syntax is great for heavy lifting where it is appropriate and makes things better. Use what is fit for purpose not what is the newest.

    • @simonpainter2242
      @simonpainter2242 2 роки тому +3

      I think it's one of those things where a bit of common sense is called for. I agree that it can be taken too far, and code can end up hard to read. When I add in these sorts of extensions though, I try and make each one as small and discrete as possible, and with a clear naming convention, so hopefully no-one would even *need* to look at the code behind it.
      When this sort of approach is done right (easier said than done!) I'd argue that it's actually easier to reason out what the code is doing, as there's less "code noise" obscuring the intention of the code. I'm basically advocating for a declarative code style over Imperative.

  • @samuelschwager
    @samuelschwager 2 роки тому +3

    Map already exists, it is called Select :P

    • @oleggavrilov7083
      @oleggavrilov7083 2 роки тому

      Finally

    • @simonpainter2242
      @simonpainter2242 2 роки тому +4

      Map and Select aren't the same thing. Select operates on each element of an Enumerable separately. Map operates on the entire source object. If you called Map(x => ... on an Enumerable, then x would be the Enumerable itself

  • @GIRARDINF
    @GIRARDINF 17 днів тому

    I wrote to you, in french, but it is easy to translate
    Come to me, help us each over to change developpement way.
    Together, we can change the world of programming.

  • @etiennelemieux472
    @etiennelemieux472 2 роки тому

    13:57 This version is buggy. x/5 became x/9...

    • @simonpainter2242
      @simonpainter2242 2 роки тому +1

      Thanks. I'll have a look & see if I can fix the slide for next time

    • @etiennelemieux472
      @etiennelemieux472 2 роки тому +1

      @@simonpainter2242 oh glad you answered, didn't know you would read this. I found your presentation very interesting (and entertaining), with many resources and ideas to re-use.

    • @simonpainter2242
      @simonpainter2242 2 роки тому +1

      @@etiennelemieux472 thanks :) and yes, I always try and keep an eye on the comments threads.