[HN Gopher] Dr. Software: An unfinished journey starting from di...
       ___________________________________________________________________
        
       Dr. Software: An unfinished journey starting from dirty code [pdf]
        
       Author : dragosslujeru
       Score  : 142 points
       Date   : 2021-06-28 06:24 UTC (16 hours ago)
        
 (HTM) web link (bitgloss.ro)
 (TXT) w3m dump (bitgloss.ro)
        
       | dgb23 wrote:
       | I skimmed the book a bit and caught a Java construct I never
       | heard of:
       | 
       | https://docs.oracle.com/en/java/javase/14/language/records.h...
       | 
       | I use the language very rarely. Is this a feature people like,
       | use, know about?
        
         | cyberbanjo wrote:
         | The version of java that features Records has only been
         | released since March 2020, so I would suspect no.
         | 
         | https://en.wikipedia.org/wiki/Java_version_history
        
           | mumblemumble wrote:
           | Something like 2/3 or 3/4 of Java users are still on Java 8.
           | So, yeah, no.
        
             | petalmind wrote:
             | Sorry, where did you get this estimation from? (Genuine
             | question).
             | 
             | thanks,
        
               | mormegil wrote:
               | Not OP, probably different numbers, but Snyk has recently
               | published a JVM ecosystem report containing a similar
               | question: https://snyk.io/jvm-ecosystem-report-2021/ (60%
               | of people use JVM 8 in production, but 40% use multiple
               | versions, so e.g. 62% use JVM 11)
        
               | mumblemumble wrote:
               | It's just sort of in the air. There are several
               | organizations that conduct and publish annual surveys
               | asking Java people what version they're on. Jetbrains and
               | Perforce do, and I think IBM has one, too. The numbers
               | get tossed around in Java tech talks so often that I've
               | long since stopped keeping track of which specific source
               | everyone's quoting, but the figures generally fall on
               | that range.
        
               | blacktriangle wrote:
               | They may not be relevant based on how large the Java
               | community is, but I think part of it is that the Java-
               | targeting languages like Kotlin, Clojure, and Scala are
               | still targeting Java 8.
        
               | mumblemumble wrote:
               | Though, I would argue that many Kotlin and Scala users
               | have a stronger incentive to migrate to Java 11 or later
               | than Java users do. Java 8's JIT compiler and default
               | garbage collector tend to punish you (performance-wise)
               | for using Kotlin or Scala; Java 11 and later have made
               | great strides to improve that.
        
               | divs1210 wrote:
               | Not sure about the other languages, but I've been using
               | Clojure with Java 14 and GraalVM 11 for quite some time.
        
               | mistrial9 wrote:
               | from a general survey point of view, there are
               | confounding aspects to a single number like that. Since
               | Java is an enterprise language by design, the difference
               | between 'moving versions due to management declaration'
               | and 'our major ecosystem component supports THIS' also
               | factor in to the overall picture.
               | 
               | As an outsider who does follow this for editorial reasons
               | from time to time, most recent info (last year) was yes,
               | projects surveyed are staying on (open) version 8.
        
               | [deleted]
        
       | skinkestek wrote:
       | Recently code reviewed some new Java code.
       | 
       | Author, who is brilliant in a number of ways had written the code
       | using new functional style.
       | 
       | This meant a bunch of anonymous function stringed together in
       | maps.
       | 
       | No comments of course since comments are generally bad these
       | days.
       | 
       | If one does functional programming in Java, don't do that.
        
         | rbonvall wrote:
         | Unfortunately there's this idea that functional programming is
         | just about using lambdas for everything. For me, proper
         | functional style is just about decoupling business logic from
         | state and side effects, and this can still be done with
         | idiomatic OOP code.
        
       | tabtab wrote:
       | Careful. Functional is often harder to debug. The very "state"
       | functional likes to avoid is often an excellent x-ray point(s)
       | for debugging. It's one of the reasons functional fails to take
       | off in the mainstream despite being around 60 odd years.
       | 
       | Service-ability often trumps parsimony in team code. I'm just the
       | observant messenger. There are exceptions, but they tend to be
       | transient.
        
         | divs1210 wrote:
         | Let me show you the interactive debugger for Clojure in Emacs:
         | 
         | https://docs.cider.mx/cider/debugging/debugger.html
        
           | tabtab wrote:
           | // procedural style          func foo01(a) {            b =
           | x(a);            c = y(b);            r = z(b,c);
           | return r;          }          // functional style
           | func foo02(a) {            return z(x(a), y(x(a)));
           | }
           | 
           | With foo01, one can readily study the value of "b", "c", and
           | "r" in a debugger and/or a Write() statement. With foo02 it's
           | harder to do the equivalent. A good debugger will let you
           | explore the output of a given function call in foo02, but you
           | still have to rework the code to put those into a Write()
           | statement. A direct debugger is sometimes not sufficient,
           | especially in big loops.
           | 
           | It's also easier to read the first style. It's more like a
           | bulleted list:                   - Step 1         - Step 2
           | - Step 3
           | 
           | The functional style is often an awkward horizontal string of
           | chained steps. Sure, the formatting can be adjusted, but it
           | takes more effort, training, and/or discipline to format
           | functional code to make it legible to ordinary code
           | maintainers. Procedural just handles riff-raff better;
           | functional requires ideal conditions/staff.
           | 
           | PS, do I really deserve a -4 score above?
        
         | rhines wrote:
         | You can still step through functional code an examine local
         | variables, you just have the assurance that the values of any
         | local state are purely dependent on the inputs to the function
         | and not any other external state. So it's ultimately easier to
         | debug - any values that affect the operation of a function are
         | explicitly set when calling the function, and you don't have to
         | worry about figuring out ways to ensure that runtime-generate
         | state matches whatever it was when the error originally
         | occurred.
        
           | tabtab wrote:
           | In theory you wouldn't have many "local variables" if you
           | followed functional practices.
           | 
           | Re: "you just have the assurance that the values of any local
           | state are purely dependent on the inputs to the function and
           | not any other external state."
           | 
           | I don't see that principle helping much in practice.
        
         | hjek wrote:
         | > Careful. Functional is often harder to debug. The very
         | "state" functional likes to avoid is often an excellent x-ray
         | point(s) for debugging.
         | 
         | Wat. Yes, if your bug relates to a certain state, then you'll
         | have to debug the state, but (a function or a program) being
         | stateless means automagically avoiding all bugs relating to
         | state; thus you don't need those "x-ray points". Also, you
         | don't need to know whether a given function mutates it's
         | argument or returns the result (or both), which is a pain in
         | JavaScript, and even with Common Lisp's sort "function". If
         | your language is immutable by default you just never have to
         | watch out for accidental mutations. No "x-ray" needed, because
         | functional programming (done properly) shouldn't be a leaky
         | abstraction.
        
           | Twisol wrote:
           | > but (a function or a program) being stateless means
           | automagically avoiding all bugs relating to state; thus you
           | don't need those "x-ray points".
           | 
           | That's not entirely true, at least the way I understand
           | "state". You still can, and do, model state in stateless
           | systems; the programming environment just doesn't give you
           | mutable, named cells for free. State is modeled by, for
           | instance, observing how the arguments change in each self-
           | call of a recursive function, or by the value observed
           | between stages of a functional pipeline. These are what I
           | would consider the analogous "x-ray points".
           | 
           | I don't disagree with you, but I find the benefits are more
           | about _lifting_ state out of the steps and making it visible,
           | rather than _eliminating_ the concept of state.
           | 
           | Relatedly, "Immutability is not enough", from a couple days
           | ago: https://news.ycombinator.com/item?id=27642263
        
             | hjek wrote:
             | > You still can, and do, model state in stateless systems;
             | 
             | Yea, in functional programming it's easier to focus on the
             | essential state inherent to the problem, and avoid
             | introducing accidental state.
             | 
             | > Relatedly, "Immutability is not enough", from a couple
             | days ago: https://news.ycombinator.com/item?id=27642263
             | 
             | Okay, interesting article, but I don't agree with this bit:
             | 
             | > It turns out that our purely functional rendering code is
             | sensitive to ordering in non-obvious ways. The first time I
             | encountered this kind of bug, it felt strangely familiar -
             | it's something that often occurs in imperative programs.
             | This is exactly the kind of problem that functional
             | programming was supposed to help us avoid!
             | 
             | I don't think functional programming is supposed to help
             | you with if you misunderstand the essential logic of the
             | problem. I'd argue that declarative languages (like Prolog
             | and SQL) can help avoid this type of ordering bug though by
             | making variable assignment explicit.
        
               | Twisol wrote:
               | I agree with you on the linked article ^_^ but it does at
               | least do a good job of showing how the concerns of state
               | _still exist_ in the absence of mutable data.
        
       | bastijn wrote:
       | > All projects that I got into had nasty, unnecessary issues.
       | Almost all of them were started in a rush, to get the business
       | off the ground and had created a parallel business of supporting
       | the customers through all the defects they had. It's almost like
       | the businesses were caught in a startup limbo, for years.
       | Sometimes decades! Working in such a company can take its toll on
       | someone.
       | 
       | So what is more important to have: (1) a product with bugs which
       | can be sold and raises money, (2) a piece of software that has no
       | bugs but also shipped x-times as late thus only learns you your
       | true requirements x-times as late.
       | 
       | For someone bashing on ivory tower architects in the subsequent
       | lines this sounds like pure irony. The mythical software engineer
       | that doesn't produce bugs.
       | 
       | > Most defects in software come from poor engineering.
       | 
       | In my experience most defects come from not knowing the exact way
       | your software will be used by the customer. Sometimes because of
       | not asking thoroughly but more often because the customer doesn't
       | know yet exactly. Also in my experience most bugs are actually
       | change requests in disguise. Actions that you didn't imagine and
       | never designed your software for but are executed by the customer
       | and now cause trouble to your software.
       | 
       | > Oh and yes, design, architecture and other fancy words, are
       | just engineering in the software industry, because it is not
       | mature enough to have that level of abstraction, in which the
       | architect draws the picture and the engineer implements it in
       | such a way that it almost 100% of the time is right on the money.
       | No. Far from it. What happens in reality is that the engineers
       | try desperately to make the system work somehow, while
       | maintaining the illusion of the story told by the architect.
       | 
       | I feel sorry for your loss. You must have worked in awful
       | environments to think about it this way.
       | 
       | The next few sections I skimmed through read like the author is
       | not proficient in software engineering at all which make me doubt
       | if this is experience from some coding (scripting) side-projects
       | rather than a full-time professional software engineer.
        
         | bob1029 wrote:
         | The trick between imperative/functional programming is that
         | there is a balance involved. Some things make a lot of sense to
         | put in a for loop and wrap with a try/catch. Interfacing with
         | the real world is where I write the most of my dirty code. In
         | software I am responsible for, the closer you get to the domain
         | model, the more functional it becomes.
         | 
         | In my mind, the entire point of imperative programming is to
         | construct a sort of "matrix" in which the functional
         | programming can exist without having to be aware of the
         | uncertain nature of the outside world.
         | 
         | If you want to build something complex really quickly, start
         | with the domain model. And start with excel, not visual studio.
         | Sit down with a spreadsheet in the conference room with all
         | your business stakeholders. Iterate that document until every
         | business person AND developer agree that it covers the needs on
         | both sides (facts + normalization). Once you have that document
         | completed, write the quickest & dirtiest MVP you can tolerate
         | against it. Convene another meeting with the stakeholders.
         | Check your gaps. Make another spreadsheet. Do this about 4-5
         | times, and then you can write "the one".
        
       | jgilias wrote:
       | It's a nice primer on writing functional Java. However, due to
       | some past experiences I feel a bit on-the-fence about it when it
       | comes to actual real world advice:
       | 
       | 1. I once happened to have a look at a Python codebase where some
       | poor soul had tried to write Java in Python. Quite literally. It
       | was not a good experience.
       | 
       | 2. Many moons ago I had a task of implementing a repetitive
       | something for many somethings in C++. Being the newbie I was I
       | just set out typing out all the methods. Then a more senior
       | colleague of mine, after having watched the ordeal for a few
       | minutes, asked for my keyboard to type out a signature of a
       | templated function to suggest that I could try 'something like
       | that'. Noticing my blank stare he proceeded to delete the
       | scribble with the words 'It's fine, you were well on your way to
       | finish it!'. I stopped him, and saved the snippet in a file
       | titled 'black_magic' to later grok it. Now I know. But I didn't
       | then. I think he was wise in his approach to reality.
       | 
       | 3. At some point I got accidentally involved in a C# project. To
       | my eyes it was full of accidental complexity. But I was not
       | expected to be a core contributing member later on. So I'd do my
       | own thing in parts of the code base allocated to me, changed all
       | the structs to classes as required in code review, and made sure
       | the whole thing fits together well with all the layers upon
       | layers of abstraction. Because it wouldn't be mine to maintain,
       | so it better be made the way people who will maintain it like it.
       | 
       | So, the general advice 'When in Rome...' may apply here. I mean,
       | what's the chance that after an 'enlightened' contractor changes
       | a Java code base to a functional paradigm comes someone
       | 'unenlightened' just to rewrite it back? Considerable, I think.
        
         | slver wrote:
         | > It's fine, you were well on your way to finish it!
         | 
         | Wholesome mentor is wholesome.
        
         | mumblemumble wrote:
         | My take in the specific case of functional programming in Java
         | is that the language will fight you every step of the way.
         | 
         | Checked exceptions are decent example here. The set of checked
         | exceptions a function might throw is part of its signature, and
         | the built-in function types don't declare any checked
         | exceptions.
         | 
         | But I've run into others. I find that Optional<T> and Java
         | Streams have some design quirks that are mostly no big deal if
         | you're using them in predominantly object-oriented or
         | procedural code, but quickly become irritating if you're trying
         | to follow a scrupulously functional idiom. And some of the
         | design implications of how Java did generics at a language
         | level have a way of sneak attacking you when you're trying to
         | scrupulously follow a functional idiom. Not because of type
         | erasure (really, Haskell is the poster child of type erasure;
         | this definitely isn't about type erasure), but because some
         | limitations in what kinds of generic types the language will
         | actually allow you to express can quickly back you into mind-
         | bending workarounds like the the curiously recurring template
         | pattern.
         | 
         | THAT SAID
         | 
         | I'm really thinking here of functional programming in more of
         | the ML/Haskell tradition, which is a peculiar way of doing
         | things that does tend to lean pretty hard on the type system.
         | If we're just talking about using higher-order functions,
         | that's great. Higher order functions absolutely have a place in
         | object-oriented programming. Java should have had them decades
         | earlier than it did. The virtual machine that the JVM was
         | largely based on was for a language that used them heavily,
         | Strongtalk, and I'm not sure I understand why the Java team
         | took them out. I'm sure there was some technical challenge, but
         | I can't help but wonder if it's really because Java was born
         | during the great "Lisp vs Everyone Else" war of the 80s and
         | 90s, and the feature was purged for political reasons.
        
           | dimitrios1 wrote:
           | With HM type systems, the type system becomes an asset,
           | rather than something you have to fight against or satisfy in
           | peculiar ways. It's a breath of fresh air doing FP in a
           | statically typed language that's not based on type erasure.
        
             | josephcsible wrote:
             | > doing FP in a statically typed language that's not based
             | on type erasure
             | 
             | What language are you talking about here?
        
             | mumblemumble wrote:
             | I think you might be misunderstanding what type erasure
             | does. It's really just about whether type information is
             | retained at run time, in order to support run time type
             | checks. Hindley-Milner type checking, on the other hand,
             | happens at compile time.
             | 
             | In a rigorously statically typed language like Haskell that
             | does all the type checking at compile time, there is no
             | need to retain types at run time, so they are erased. Java
             | is a less statically typed language. Variables have types,
             | but you're allowed to upcast all the way up to Object, and
             | then do a dynamic type check and downcast at a later time.
             | It's just that the language won't do this transparently for
             | you the way it will in a more fully dynamically typed
             | language. In order to support this, though, Java cannot
             | erase types.
             | 
             | The controversy with Java is that, when generics were
             | introduced, they decided that generic type parameters
             | _would_ be erased. This greatly undercuts the soundness of
             | the type system. If you 're not careful (or if you're
             | trying to be clever), you can insert a String into an
             | object that had been declared to be a List<Integer>, and
             | you won't get any errors until you try to iterate over the
             | list and a run-time type check suddenly blows up your
             | program because some code finds itself interacting with a
             | String even though the compiler's static type check had
             | confirmed that the code _should_ have only ever been able
             | to operate on Integers.
             | 
             | The thing to call attention to here, though, is that this
             | is at least as much about Java's relatively weak static
             | type system as it is about erasing type parameters. If Java
             | (the language) had something closer to a Hindley-Milner
             | type system with stronger static type checks, then it might
             | have been able to catch shenanigans like that at compile
             | time. It's a question of framing: the problem _could_ have
             | been handled by using type reification to enable stronger
             | run-time type checks, but it might also have been solved by
             | using stronger compile-time type checks to eliminate the
             | need for those run-time checks in the first place.
             | 
             | That said, this is pretty deep out in speculative
             | territory, since it's doubtful that either option could
             | have been executed without major breaking changes, which
             | Java definitely wasn't going to do.
        
               | mscuwa wrote:
               | > If you're not careful (or if you're trying to be
               | clever), you can insert a String into an object that had
               | been declared to be a List<Integer>
               | 
               | It's less about type erasure and more about missing
               | covariance/contravariance in Java generics. Scala also
               | has type erasure in generics (same JVM), but won't allow
               | that (unless you explicitly upcast). HM doesn't recognize
               | inheritance, so it's in a completely different category.
        
         | goto11 wrote:
         | Yep, following the idioms of a language is almost always better
         | than trying to make it look like some other language, even if
         | the emulated language is more fashionable.
        
       | throwaway33321 wrote:
       | Seeing such content on the front page makes me believe that
       | either a) people only read the title or b) simply the bar has
       | been set too low or c) there are too many junior level
       | programmers.
        
         | anhner wrote:
         | So junior level programmers are not allowed to upvote stuff on
         | the front page, or?
        
           | throwaway33321 wrote:
           | No it just means that promoting garbage will result in people
           | treating that garbage as good practice particularly when
           | coming from HN.
        
           | [deleted]
        
       | hota_mazi wrote:
       | What a strange title.
       | 
       | Shouldn't it be "Refactoring from imperative to FP"?
       | 
       | You can still keep the OOP attributes of your code while
       | embracing FP, there is nothing mutually exclusive about these two
       | paradigms.
        
         | dang wrote:
         | Submitted title was "Dr. Software - Refactoring from OOP to FP
         | [pdf]". We've changed that now, in keeping with the site
         | guidelines: " _Please use the original title, unless it is
         | misleading or linkbait; don 't editorialize._"
         | 
         | https://news.ycombinator.com/newsguidelines.html
        
       | mikewarot wrote:
       | This isn't about refactoring OOP to FP, the author invents an
       | example (strawman?) to refactor, and the first thing he does is
       | complain about how it is a badly written CLI application. I
       | stopped there.
        
       ___________________________________________________________________
       (page generated 2021-06-28 23:03 UTC)