Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • World
  • Users
  • Groups
Skins
  • Light
  • Cerulean
  • Cosmo
  • Flatly
  • Journal
  • Litera
  • Lumen
  • Lux
  • Materia
  • Minty
  • Morph
  • Pulse
  • Sandstone
  • Simplex
  • Sketchy
  • Spacelab
  • United
  • Yeti
  • Zephyr
  • Dark
  • Cyborg
  • Darkly
  • Quartz
  • Slate
  • Solar
  • Superhero
  • Vapor

  • Default (No Skin)
  • No Skin
Collapse
Code Project
  1. Home
  2. Other Discussions
  3. The Weird and The Wonderful
  4. The changing landscape of OOP (from class to struct)

The changing landscape of OOP (from class to struct)

Scheduled Pinned Locked Moved The Weird and The Wonderful
csharpcomfunctionalperformancetutorial
62 Posts 24 Posters 4 Views 1 Watching
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • R raddevus

    That's a nice example of a good use of creating types for the parameters and it makes sense. Also, I'm just at the beginning of the author's example also and it seems he is taking the example much further so it probably isn't that the author is actually saying "well, just wrap all those primitives in structs" but is building the case for it as he continues his longer example. I was just astonished to see this "newer" idea of wrapping primitives like that. I will continue reading the book because it is making me think different and the author's point is to make "more readable" code too and any hints toward that always go a long way. Thanks for your interesting post which really adds to the conversation.

    J Offline
    J Offline
    Jeremy Falcon
    wrote on last edited by
    #17

    Might wanna check my new, new reply. I'm being annoying in it. :laugh:

    Jeremy Falcon

    1 Reply Last reply
    0
    • J Jon McKee

      I've been working a lot lately with Spring Webflux/Reactor and they liberally use the Duration class for any time specs.

      //so instead of
      long ticks
      long ms
      long s
      //etc, etc, you see
      Duration t
      //and you create values using stuff like
      Duration.ofSeconds
      Duration.ofMilliseconds

      By not obsessing over primitives, they made it so that all methods that use times can accept any time. You don't have to constantly remind yourself what the context for that time value is (e.g. seconds, milliseconds, etc), because the method doesn't specify the context, you do. So I love the idea of better contextualizing values beyond their strict storage type. As long as there's a useful context that adds value. From your example, I think an Angle abstraction that handled both radians and degrees could prove useful in a similar manner to Duration, for example. As given, I'm not sure abstracting a double to an Angle solely to remove the primitive is a good pattern though. My assumption is that the intention is to force the developer to explicitly contextualize the double value, but the thing is if the developer didn't care about the context before, they aren't going to care now. They'll just wrap the double they have and move on (e.g. ex.handleAngle(new Angle(someDoubleThatIsntAnAngle))). Elevating a primitive in this way doesn't actually achieve anything that variable naming and/or named arguments couldn't already do. Just having a nondescript Angle with a double size property does nothing to further describe a double angle parameter. There has to be more sauce to it to make the abstraction worth it in my opinion.

      J Offline
      J Offline
      Jeremy Falcon
      wrote on last edited by
      #18

      Realistically though, how often do you need to contextual _inputs_ like that? If it's external user input, it should always be sanitized first. Which means you can transform any exceptions in that layer. If it's internal user input, how often do you really change contexts like that in practice? Don't get me wrong, nothing against structs as a param, but having said logic to handle the contextualization in every last routine that uses it (it's for inputs) isn't ideal. I'd argue structs are useful for abstracting complex data types only, irrespective of the context in which they are called with.

      Jeremy Falcon

      J 1 Reply Last reply
      0
      • R raddevus

        Greg Utas wrote:

        It can easily cause bugs when, for example, a plain integer is interpreted as a time duration. Was that in seconds? Milliseconds? Microseconds?

        Yes, I agree. That is a very good example of where this struct wrapper would be highly beneficial. And, I do like the discussion around "using more structs". It is just interesting that in the past, it was "everything is a class" and to see that re-thought.

        Greg UtasG Offline
        Greg UtasG Offline
        Greg Utas
        wrote on last edited by
        #19

        In C++, there's no difference between class and a struct, so that comparison with C# is likely to be misleading. In C++, the two keywords often have different semantics by convention, but they're the same to the compiler. If you look at the STL's , there's no way to confuse a duration in seconds with one in milliseconds. Their object wrapper precludes it.

        Robust Services Core | Software Techniques for Lemmings | Articles
        The fox knows many things, but the hedgehog knows one big thing.

        <p><a href="https://github.com/GregUtas/robust-services-core/blob/master/README.md">Robust Services Core</a>
        <em>The fox knows many things, but the hedgehog knows one big thing.</em></p>

        1 Reply Last reply
        0
        • R raddevus

          Just started reading this (just released) book, The C# Type System (no starch press)[^] and the first chapter is kind of blowing my mind. Step 1 Author starts out with the following example and says, "You must use better named variables so dev users know what they mean."

          Displacement(double t, double v, double s)
          {
          var x = v * s * Math.Cos(t);
          var y = v * s * Math.Sin(t) - 0.5 * 9.81 * Math.Pow(s, 2);
          return (x, y);
          }

          Yes, that makes sense. Step 2 Then he says, "Oh, you can add meaning with this new idea of named arguments so users don't have to remember order that they should be passed in."

          var result = Displacement(angle: .523, speed: 65, elapsedTime: 4);

          Ok, yes, that is good advice with the modern capabilities. Step 3 May Blow Your Mind He mentions that the code is still confusing because all three arguments are the same primitive type (double) and this leads into...

          From the book:

          Primitive Obsession code smell, which describes any code that has an overreliance on primitive types—that is, those types that are built into the language, such as int, double, and string.

          The solution is... Wrap All the Primitive Types In Structs 🤯🤯🤯🤯🤯

          public struct Angle
          {
          public double Size {get; set;}
          }
          public struct Speed
          {
          public double Amount {get; set;}
          }

          The Paradigm Has Shifted Now, when the user attempts to call the Displacement method the compiler will know that the argument type is wrong. Now, there's no way to pass the wrong value into the method, because the compiler will know the type. Wow, that is a very different paradigm!! Step 4 Is Immutability Now, make each struct immutable

          Richard DeemingR Offline
          Richard DeemingR Offline
          Richard Deeming
          wrote on last edited by
          #20

          I think it's similar to the old "bool-vs-enum" arguments that have been around for many years. An appropriately-named enum can certainly make the calling code easier to read than a bunch-o-bools, although it can lead to some interesting definitions[^]. :) Given how recent the book is, I'm surprised the author hasn't progressed to record structs[^]; perhaps that will come later?

          public readonly record struct Speed(double Amount);


          "These people looked deep within my soul and assigned me a number based on the order in which I joined." - Homer

          "These people looked deep within my soul and assigned me a number based on the order in which I joined" - Homer

          T J 2 Replies Last reply
          0
          • R raddevus

            Just started reading this (just released) book, The C# Type System (no starch press)[^] and the first chapter is kind of blowing my mind. Step 1 Author starts out with the following example and says, "You must use better named variables so dev users know what they mean."

            Displacement(double t, double v, double s)
            {
            var x = v * s * Math.Cos(t);
            var y = v * s * Math.Sin(t) - 0.5 * 9.81 * Math.Pow(s, 2);
            return (x, y);
            }

            Yes, that makes sense. Step 2 Then he says, "Oh, you can add meaning with this new idea of named arguments so users don't have to remember order that they should be passed in."

            var result = Displacement(angle: .523, speed: 65, elapsedTime: 4);

            Ok, yes, that is good advice with the modern capabilities. Step 3 May Blow Your Mind He mentions that the code is still confusing because all three arguments are the same primitive type (double) and this leads into...

            From the book:

            Primitive Obsession code smell, which describes any code that has an overreliance on primitive types—that is, those types that are built into the language, such as int, double, and string.

            The solution is... Wrap All the Primitive Types In Structs 🤯🤯🤯🤯🤯

            public struct Angle
            {
            public double Size {get; set;}
            }
            public struct Speed
            {
            public double Amount {get; set;}
            }

            The Paradigm Has Shifted Now, when the user attempts to call the Displacement method the compiler will know that the argument type is wrong. Now, there's no way to pass the wrong value into the method, because the compiler will know the type. Wow, that is a very different paradigm!! Step 4 Is Immutability Now, make each struct immutable

            R Offline
            R Offline
            RainHat
            wrote on last edited by
            #21

            He failed step 3. Angle is not very well implemented, as it does not specify what unit it uses. Either call it Radians, or specify the unit in the constructor and getter. Ironically, this could possibly be implemented by inheritance. Angle being the base class and Degrees, Radians, Turns and Gons (optionally Longitude and Latitude) being the sub classes, although this would force it to be a class. As implemented the client of this function could still create an angle of 90 (thinking degrees) and pass this in and get a point at 90 radians. I agree that is is a good idea to make structs immutable. I have seen so many bugs caused by trying to alter a Point or Size but the code is altering a copy, not the original. Is implementing structs for these a good idea? Possibly. I have implemented Angle as a class before - mainly for user display. I have also just used angleDegrees and angleRadians. Choose wisely. The advantages are it forces the correct type to be passed in. The disadvantages are a little bit of speed and extra code. Would naming the angle as angleRadians be enough to eliminate most errors? Do we need explicit casts on Radians so we can use (Radians)Math.PI? Personally I prefer a plain double, named well, although an explicit type can be useful.

            C H 2 Replies Last reply
            0
            • R RainHat

              He failed step 3. Angle is not very well implemented, as it does not specify what unit it uses. Either call it Radians, or specify the unit in the constructor and getter. Ironically, this could possibly be implemented by inheritance. Angle being the base class and Degrees, Radians, Turns and Gons (optionally Longitude and Latitude) being the sub classes, although this would force it to be a class. As implemented the client of this function could still create an angle of 90 (thinking degrees) and pass this in and get a point at 90 radians. I agree that is is a good idea to make structs immutable. I have seen so many bugs caused by trying to alter a Point or Size but the code is altering a copy, not the original. Is implementing structs for these a good idea? Possibly. I have implemented Angle as a class before - mainly for user display. I have also just used angleDegrees and angleRadians. Choose wisely. The advantages are it forces the correct type to be passed in. The disadvantages are a little bit of speed and extra code. Would naming the angle as angleRadians be enough to eliminate most errors? Do we need explicit casts on Radians so we can use (Radians)Math.PI? Personally I prefer a plain double, named well, although an explicit type can be useful.

              C Offline
              C Offline
              charlieg
              wrote on last edited by
              #22

              "I prefer a plain double, named well" I agree. Working in embedded systems where units are very important, I try to always include units at the end of the variable name. It causes a mental check when you start misusing the variable. But I also like a little more rigidity to avoid really stupid errors. I'm thinking of the Mars Climate Orbiter that lawn darted due to a units conversion issue. Errors like this boggle my mind. Every engineering system should be in metric. Period. If you want to convert something to English - that's a presentation issue, but I digress. This conversation is an excellent read.

              Charlie Gilley “They who can give up essential liberty to obtain a little temporary safety deserve neither liberty nor safety.” BF, 1759 Has never been more appropriate.

              T N 2 Replies Last reply
              0
              • Richard DeemingR Richard Deeming

                I think it's similar to the old "bool-vs-enum" arguments that have been around for many years. An appropriately-named enum can certainly make the calling code easier to read than a bunch-o-bools, although it can lead to some interesting definitions[^]. :) Given how recent the book is, I'm surprised the author hasn't progressed to record structs[^]; perhaps that will come later?

                public readonly record struct Speed(double Amount);


                "These people looked deep within my soul and assigned me a number based on the order in which I joined." - Homer

                T Offline
                T Offline
                trønderen
                wrote on last edited by
                #23

                Richard Deeming wrote:

                An appropriately-named enum can certainly make the calling code easier to read than a bunch-o-bools, although it can lead to some interesting definitions[^].

                In my student days, DEC had a discussion group system called COM, not unlike the far more well known NetNews, running on DEC-10 and DEC-20 mainframes. Whenever the software asked the user a yes/no question (such as "Delete entry?") there were in fact three options: Yes, No, Maybe. If the user chose Maybe, a random generator was used to choose between Yes and No. This was a fully documented, well known feature. I believe this feature existed in the old DEC-10/20 version. COM was completely rewritten as a platform independent system and renamed PortaCOM. I used the system in the transition from COM to PortaCOM; maybe the 'Maybe' option came with the PortaCOM rewrite. I didn't discover it until we had switched to PortaCOM.

                1 Reply Last reply
                0
                • C charlieg

                  "I prefer a plain double, named well" I agree. Working in embedded systems where units are very important, I try to always include units at the end of the variable name. It causes a mental check when you start misusing the variable. But I also like a little more rigidity to avoid really stupid errors. I'm thinking of the Mars Climate Orbiter that lawn darted due to a units conversion issue. Errors like this boggle my mind. Every engineering system should be in metric. Period. If you want to convert something to English - that's a presentation issue, but I digress. This conversation is an excellent read.

                  Charlie Gilley “They who can give up essential liberty to obtain a little temporary safety deserve neither liberty nor safety.” BF, 1759 Has never been more appropriate.

                  T Offline
                  T Offline
                  trønderen
                  wrote on last edited by
                  #24

                  charlieg wrote:

                  I'm thinking of the Mars Climate Orbiter that lawn darted due to a units conversion issue.

                  A less known story of a similar kind: In its infancy in the late 1960s, the Norwegian computer manufacturer Norsk Data started buying standard 19" power supplies, and tried to build their own 19" racks. They ended up as being rather roomy, the power supplies almost fell down. It was tracked down to the mechanics having calculated the metric equivalent of 19" on the old Norwegian inch of 25.6 mm, rather than the American 25.4 mm inch. 19 times 0.2 mm = 3.8 mm. Not terribly much extra, but enough to require some extra care for the power supplies to be securely fastened. (According to Wikipedia, the SI inch definition of 25.4 mm was almost ten years old at the time, but it takes much more than ten years to change tooling and conversion factors. (And I am not going to bring up metrication in the US of A to prove my point.))

                  1 Reply Last reply
                  0
                  • J Jeremy Falcon

                    Realistically though, how often do you need to contextual _inputs_ like that? If it's external user input, it should always be sanitized first. Which means you can transform any exceptions in that layer. If it's internal user input, how often do you really change contexts like that in practice? Don't get me wrong, nothing against structs as a param, but having said logic to handle the contextualization in every last routine that uses it (it's for inputs) isn't ideal. I'd argue structs are useful for abstracting complex data types only, irrespective of the context in which they are called with.

                    Jeremy Falcon

                    J Offline
                    J Offline
                    Jon McKee
                    wrote on last edited by
                    #25

                    I agree it's easy to go overboard with it. That's why I mentioned I feel like there has to be "more sauce" to the abstraction - e.g. an abstraction that abstracts multiple parameters, an abstraction that adds functionality, etc. As a more concrete example with the Angle idea - you have Radians and Degrees as options (so Angle is basically an Either sum type) and Radians and Degrees are isomorphic. Why is that useful? Here's some pseudo-code:

                    class Angle = Radian | Degree
                    (+) :: Angle a -> Angle b -> Angle c
                    (+) x = match x
                    | Radian z => \y -> z + toRadian(y)
                    | Degree z => \y -> z + toDegree(y)

                    public double addRightAngle(double degrees) => degrees + 90; //Fails if you pass in radians
                    public double addRightAngle(double radians) => radians + (90*(pi/180)); //Fails if you pass in degrees
                    public Angle addRightAngle(Angle angle) => angle + new Degree(90); //Succeeds in all cases
                    public Angle addRightAngle(Angle angle) => angle + new Radian(1.5708); //Succeeds in all cases

                    How useful this is depends on how important angles are to your code-base, but I think abstracting inputs is very powerful. Another example is if you're doing functional programming and have a function that accepts impure inputs like a database function. You can group all impure inputs together into a tuple and shift that tuple to the right of the parameter list. This effectively turns your function into a pure function that returns an impure Reader with that environment tuple as input and the result as output (i.e. "functional" dependency injection). Makes a lot of things easier especially unit testing. Credit to Mark Seemann for that insight[^].

                    J 1 Reply Last reply
                    0
                    • R raddevus

                      That's a nice example of a good use of creating types for the parameters and it makes sense. Also, I'm just at the beginning of the author's example also and it seems he is taking the example much further so it probably isn't that the author is actually saying "well, just wrap all those primitives in structs" but is building the case for it as he continues his longer example. I was just astonished to see this "newer" idea of wrapping primitives like that. I will continue reading the book because it is making me think different and the author's point is to make "more readable" code too and any hints toward that always go a long way. Thanks for your interesting post which really adds to the conversation.

                      J Offline
                      J Offline
                      Jon McKee
                      wrote on last edited by
                      #26

                      That's awesome! I've been learning universal algebra and category theory recently for a similar purpose. Having a new perspective on things really opens up your problems solving ability. I feel like I'm less of a hammer looking at everything like a nail.

                      1 Reply Last reply
                      0
                      • R RainHat

                        He failed step 3. Angle is not very well implemented, as it does not specify what unit it uses. Either call it Radians, or specify the unit in the constructor and getter. Ironically, this could possibly be implemented by inheritance. Angle being the base class and Degrees, Radians, Turns and Gons (optionally Longitude and Latitude) being the sub classes, although this would force it to be a class. As implemented the client of this function could still create an angle of 90 (thinking degrees) and pass this in and get a point at 90 radians. I agree that is is a good idea to make structs immutable. I have seen so many bugs caused by trying to alter a Point or Size but the code is altering a copy, not the original. Is implementing structs for these a good idea? Possibly. I have implemented Angle as a class before - mainly for user display. I have also just used angleDegrees and angleRadians. Choose wisely. The advantages are it forces the correct type to be passed in. The disadvantages are a little bit of speed and extra code. Would naming the angle as angleRadians be enough to eliminate most errors? Do we need explicit casts on Radians so we can use (Radians)Math.PI? Personally I prefer a plain double, named well, although an explicit type can be useful.

                        H Offline
                        H Offline
                        haughtonomous
                        wrote on last edited by
                        #27

                        Surely most of these problems can be circumvented by using sensible variable or parameter names, eg AngleInRadians", "ElapsedTimeInMillisec" etc? Saves a lot of coding time, and however you try to avoid it, if a developer is determined to be dumb, he or she will be, whatever.

                        J 1 Reply Last reply
                        0
                        • R raddevus

                          Very interesting post. Thanks for sharing. :thumbsup:

                          trønderen wrote:

                          There are so many grains of gold in old, phased-out technology.

                          I agree, but this is what drives IT, I guess. It's either jump on the bandwagon or it rolls over you. :-D It's shiny and new!! :rolleyes:

                          H Offline
                          H Offline
                          haughtonomous
                          wrote on last edited by
                          #28

                          There are too many Magpies in software development. Cannot resist anything shiny and new! And too many who instead of offering their opinion for critique, seek to impose it as the New Standard.

                          1 Reply Last reply
                          0
                          • R raddevus

                            Just started reading this (just released) book, The C# Type System (no starch press)[^] and the first chapter is kind of blowing my mind. Step 1 Author starts out with the following example and says, "You must use better named variables so dev users know what they mean."

                            Displacement(double t, double v, double s)
                            {
                            var x = v * s * Math.Cos(t);
                            var y = v * s * Math.Sin(t) - 0.5 * 9.81 * Math.Pow(s, 2);
                            return (x, y);
                            }

                            Yes, that makes sense. Step 2 Then he says, "Oh, you can add meaning with this new idea of named arguments so users don't have to remember order that they should be passed in."

                            var result = Displacement(angle: .523, speed: 65, elapsedTime: 4);

                            Ok, yes, that is good advice with the modern capabilities. Step 3 May Blow Your Mind He mentions that the code is still confusing because all three arguments are the same primitive type (double) and this leads into...

                            From the book:

                            Primitive Obsession code smell, which describes any code that has an overreliance on primitive types—that is, those types that are built into the language, such as int, double, and string.

                            The solution is... Wrap All the Primitive Types In Structs 🤯🤯🤯🤯🤯

                            public struct Angle
                            {
                            public double Size {get; set;}
                            }
                            public struct Speed
                            {
                            public double Amount {get; set;}
                            }

                            The Paradigm Has Shifted Now, when the user attempts to call the Displacement method the compiler will know that the argument type is wrong. Now, there's no way to pass the wrong value into the method, because the compiler will know the type. Wow, that is a very different paradigm!! Step 4 Is Immutability Now, make each struct immutable

                            P Offline
                            P Offline
                            Peter Adam
                            wrote on last edited by
                            #29

                            For me, C# lacks the easy way of custom types provided by Object Pascal, especially limited-length strings/etc. like

                            type
                            ShortStr = string[255];
                            TTextBuf = array[0..127] of Char;
                            TElfWordTab = array [0..2] of Cardinal;

                            Richard DeemingR H 2 Replies Last reply
                            0
                            • P Peter Adam

                              For me, C# lacks the easy way of custom types provided by Object Pascal, especially limited-length strings/etc. like

                              type
                              ShortStr = string[255];
                              TTextBuf = array[0..127] of Char;
                              TElfWordTab = array [0..2] of Cardinal;

                              Richard DeemingR Offline
                              Richard DeemingR Offline
                              Richard Deeming
                              wrote on last edited by
                              #30

                              That's funny - someone else was discussing that very topic on Reddit yesterday. :) https://www.reddit.com/r/csharp/comments/16sj11z/string_length_attribute_doesnt_work_for_me/[^]


                              "These people looked deep within my soul and assigned me a number based on the order in which I joined." - Homer

                              "These people looked deep within my soul and assigned me a number based on the order in which I joined" - Homer

                              1 Reply Last reply
                              0
                              • J Jeremy Falcon

                                raddevus wrote:

                                Anyways, what do you think about this "Primitive Obsession code smell"?

                                It seems like the book author is someone trying to sound smarter than they are. Just because someone writes a book doesn't make them a genius. Now, I do agree that primitive obsession is bad, but so is struct obsession. A struct won't inherently prevent a coder from mistaking milliseconds for seconds (to borrow from this thread's example). But, what it does do is offer more complexity in an application that may otherwise not be needed. A lot of these "new" ideas are just rehashed old ideas from JavaScript. I'm dead serious. It's just people looking for something to do rather than go outside. In JavaScript, some folks love to use an object as a parameter for everything. It's the loose and fast version of a struct in C#. It's just as ridiculous to expect an object as a single param in every last routine. The problem is, the obsession or abuse of any one concept. Average coders take one little thing and run with it because it's the new shiny doodad. Abusing structs is no better. It's just change for change's sake while pretending to be smart. It's about balance.

                                raddevus wrote:

                                Step 4 Is Immutability

                                To the point of the book, to make each struct read only is a good idea. But, to the point of "prefer composition over inheritance", both Java and C# were literally designed with an OOP paradigm in mind. Move to a functional language if you want to start acting functional. In regard to immutability, you can use a sealed class in Java and C# as well. The irony is, all this struct talk is reminding me of C. People always said C sucked because it doesn't support classes. And yet, here we are. People just following the hype train because people looking to change something for no real gain and refuse to go outside. And I say this as a dude who loves functional programming, C# wasn't designed that way.

                                Jeremy Falcon

                                G Offline
                                G Offline
                                Gary Wheeler
                                wrote on last edited by
                                #31

                                Jeremy my man, you said it all and very well indeed.

                                Software Zen: delete this;

                                J 1 Reply Last reply
                                0
                                • J Jon McKee

                                  I agree it's easy to go overboard with it. That's why I mentioned I feel like there has to be "more sauce" to the abstraction - e.g. an abstraction that abstracts multiple parameters, an abstraction that adds functionality, etc. As a more concrete example with the Angle idea - you have Radians and Degrees as options (so Angle is basically an Either sum type) and Radians and Degrees are isomorphic. Why is that useful? Here's some pseudo-code:

                                  class Angle = Radian | Degree
                                  (+) :: Angle a -> Angle b -> Angle c
                                  (+) x = match x
                                  | Radian z => \y -> z + toRadian(y)
                                  | Degree z => \y -> z + toDegree(y)

                                  public double addRightAngle(double degrees) => degrees + 90; //Fails if you pass in radians
                                  public double addRightAngle(double radians) => radians + (90*(pi/180)); //Fails if you pass in degrees
                                  public Angle addRightAngle(Angle angle) => angle + new Degree(90); //Succeeds in all cases
                                  public Angle addRightAngle(Angle angle) => angle + new Radian(1.5708); //Succeeds in all cases

                                  How useful this is depends on how important angles are to your code-base, but I think abstracting inputs is very powerful. Another example is if you're doing functional programming and have a function that accepts impure inputs like a database function. You can group all impure inputs together into a tuple and shift that tuple to the right of the parameter list. This effectively turns your function into a pure function that returns an impure Reader with that environment tuple as input and the result as output (i.e. "functional" dependency injection). Makes a lot of things easier especially unit testing. Credit to Mark Seemann for that insight[^].

                                  J Offline
                                  J Offline
                                  Jeremy Falcon
                                  wrote on last edited by
                                  #32

                                  Jon McKee wrote:

                                  Here's some pseudo-code:

                                  Got it. I didn't think of it in the context of replacing overloads. Just calling it that because if I where to code up your first two calls I'd at least have two strong (primitive-based) types that would differentiate the signature. I've been in JavaScript too long where that's not really done. :-O

                                  Jon McKee wrote:

                                  This effectively turns your function into a pure function that returns an impure Reader with that environment tuple as input and the result as output

                                  That one I gotta look into man. My understanding of pure functions is that all inputs are deterministic. So, not following how shifting parameter order changes that, since non-deterministic input is still going into the routine. Will check out the link though. Btw, thanks for knowing what you're talking about. Makes these conversations much better.

                                  Jeremy Falcon

                                  J 1 Reply Last reply
                                  0
                                  • G Gary Wheeler

                                    Jeremy my man, you said it all and very well indeed.

                                    Software Zen: delete this;

                                    J Offline
                                    J Offline
                                    Jeremy Falcon
                                    wrote on last edited by
                                    #33

                                    Thanks buddy.

                                    Jeremy Falcon

                                    1 Reply Last reply
                                    0
                                    • Richard DeemingR Richard Deeming

                                      I think it's similar to the old "bool-vs-enum" arguments that have been around for many years. An appropriately-named enum can certainly make the calling code easier to read than a bunch-o-bools, although it can lead to some interesting definitions[^]. :) Given how recent the book is, I'm surprised the author hasn't progressed to record structs[^]; perhaps that will come later?

                                      public readonly record struct Speed(double Amount);


                                      "These people looked deep within my soul and assigned me a number based on the order in which I joined." - Homer

                                      J Offline
                                      J Offline
                                      Jeremy Falcon
                                      wrote on last edited by
                                      #34

                                      I'm a total n00b here with record structs... but reference types that offer value-based equality checking? That's actually pretty cool.

                                      Jeremy Falcon

                                      1 Reply Last reply
                                      0
                                      • H haughtonomous

                                        Surely most of these problems can be circumvented by using sensible variable or parameter names, eg AngleInRadians", "ElapsedTimeInMillisec" etc? Saves a lot of coding time, and however you try to avoid it, if a developer is determined to be dumb, he or she will be, whatever.

                                        J Offline
                                        J Offline
                                        Jeremy Falcon
                                        wrote on last edited by
                                        #35

                                        :thumbsup::thumbsup::thumbsup:

                                        Jeremy Falcon

                                        1 Reply Last reply
                                        0
                                        • R raddevus

                                          Just started reading this (just released) book, The C# Type System (no starch press)[^] and the first chapter is kind of blowing my mind. Step 1 Author starts out with the following example and says, "You must use better named variables so dev users know what they mean."

                                          Displacement(double t, double v, double s)
                                          {
                                          var x = v * s * Math.Cos(t);
                                          var y = v * s * Math.Sin(t) - 0.5 * 9.81 * Math.Pow(s, 2);
                                          return (x, y);
                                          }

                                          Yes, that makes sense. Step 2 Then he says, "Oh, you can add meaning with this new idea of named arguments so users don't have to remember order that they should be passed in."

                                          var result = Displacement(angle: .523, speed: 65, elapsedTime: 4);

                                          Ok, yes, that is good advice with the modern capabilities. Step 3 May Blow Your Mind He mentions that the code is still confusing because all three arguments are the same primitive type (double) and this leads into...

                                          From the book:

                                          Primitive Obsession code smell, which describes any code that has an overreliance on primitive types—that is, those types that are built into the language, such as int, double, and string.

                                          The solution is... Wrap All the Primitive Types In Structs 🤯🤯🤯🤯🤯

                                          public struct Angle
                                          {
                                          public double Size {get; set;}
                                          }
                                          public struct Speed
                                          {
                                          public double Amount {get; set;}
                                          }

                                          The Paradigm Has Shifted Now, when the user attempts to call the Displacement method the compiler will know that the argument type is wrong. Now, there's no way to pass the wrong value into the method, because the compiler will know the type. Wow, that is a very different paradigm!! Step 4 Is Immutability Now, make each struct immutable

                                          S Offline
                                          S Offline
                                          Steve Naidamast
                                          wrote on last edited by
                                          #36

                                          It seems like the sample the author provided is overkill for the passing of 3 simple variables. What is the purpose of any of this, I have no idea, except to make programming more confusing to the developer. What is wrong with passing a well-named variable as a primitive, especially with the hardware we work with today? In any event, I use Structs\Structures to pass data and Classes to execute methods. However, unless there was a specific reason to pass certain data in a Struct\Structure (ie: such as the need to pass all of the data, which makes up a particular Struct\Structure, passing such data as individual variables makes a lot more sense then wasting time on creating Structs\Structures to do so...

                                          Steve Naidamast Sr. Software Engineer Black Falcon Software, Inc. blackfalconsoftware@outlook.com

                                          R 1 Reply Last reply
                                          0
                                          Reply
                                          • Reply as topic
                                          Log in to reply
                                          • Oldest to Newest
                                          • Newest to Oldest
                                          • Most Votes


                                          • Login

                                          • Don't have an account? Register

                                          • Login or register to search.
                                          • First post
                                            Last post
                                          0
                                          • Categories
                                          • Recent
                                          • Tags
                                          • Popular
                                          • World
                                          • Users
                                          • Groups