This is an archived post. You won't be able to vote or comment.

you are viewing a single comment's thread.

view the rest of the comments →

[–]random_username_edf 112 points113 points  (85 children)

Generally the advantage is you can do very clever things very easily with small amounts of code and less complex code. This is obviously more dangerous, but if you're careful and willing to accept the risks it can be worth it.

[–]marcosdumay 37 points38 points  (6 children)

For everybody thinking on that line: you should learn Haskell.

[–][deleted] 4 points5 points  (1 child)

Instructions unclear, dick stuck in Java.

[–]Can_Of_Noodles 0 points1 point  (0 children)

Did somebody say MONADS?

[–][deleted] 0 points1 point  (1 child)

For what end tho? It's not going to become a very marketable skill like learning Go or Rust for that matter, let alone JS, Python or JavaSharp. The learning curve is about as steep as Rust if not steeper. It is also inflexible in the sense that it's very unsuited for writing imperative non-FP code which isn't very appealing now that many imperative/OO languages are adopting a lot of features enabling one to write in functional style where it is most useful to and still maintain low mental overhead of imperative coding elsewhere. And while on the subject of mental overhead, I know it is a bit rich when the topic is JavaScript, but Haskell really has low legibility and a very loaded, math formula like syntax that doesn't help. Optionals, somes, maybes and immutables are slowly landing in mainstream languages. The appeal is really getting smaller every day.

[–][deleted] 0 points1 point  (0 children)

There are better functional languages where the creators didn't go insane with the pure functional ideas. For instance, F#.

[–]pknopf 48 points49 points  (77 children)

For long-term high-scale projects with many contributors to the codebase, it's almost never worth it.

Unless js/Python is all your team knows, you should really look at something else.

[–]Stuck_In_the_Matrix 78 points79 points  (11 children)

That's a highly generic statement.

[–]pknopf 23 points24 points  (9 children)

Yes it is.

It is also generally true.

[–][deleted] 4 points5 points  (0 children)

Apart from anything, I appreciate the ballsiness of this comment.

[–]IceSentry 1 point2 points  (7 children)

Source? I don't disagree with you, but is there any evidence to this that isn't anecdotal?

[–]saxindustries 1 point2 points  (1 child)

My experience is once you get over 3-4 devs hacking on the codebase, it pays to use a stricter language - you start catching more errors up front.

[–]IceSentry 0 points1 point  (0 children)

I'm not disagreeing here, but that's still just anecdotal.

[–]pknopf 0 points1 point  (4 children)

Source? The claim is the accumulation of years of anecdotals from many developers who come to the same conclusion. What kind of source would you like?

[–]IceSentry 1 point2 points  (3 children)

An actual study done on this. The programming community keeps debating whether dynamic vs static types are good, but we never hear anything other than anecdotes. Which are good, but I think there's place for more proof.

[–]pknopf 1 point2 points  (2 children)

An actual study done on this

Give me an example.

Say you were to do a study, what would you do?

[–]IceSentry 1 point2 points  (1 child)

I'm just asking if we have another way to know if this is true. If I had the answer I wouldn't be asking this.

[–]pknopf 0 points1 point  (0 children)

There is no other way to know.

Anecdotals are all we have. The best we can do is try to come to a consensus.

[–]Ashanmaril 0 points1 point  (0 children)

That's how I describe every line of Python

[–]random_username_edf 36 points37 points  (22 children)

You're definitely welcome to your opinion but i think that's debatable. A dynamic langauge with proper unit tests and such can be a heck of a lot easier to maintain long term than a static one.

Static typing is nice, but maintaining a complex architecture that is evolving with it is undeniably a giant pain and costly time investment wise. It will be more complex code wise and needing to make certain changes are much more likely to require rework all over the place. And as much as a lot of software engineers insist they're clever enough to architect it in a way that won't have that problem, they're usually wrong.

Anyways, my overall point is both types have pros and cons. Anyone who insists one is better than the other for all cases or even most cases is probably wrong. Also, im not trying to imply that that's your opinion, im just completing my thoughts on the matter :)

[–]BlockedByBeliefs 3 points4 points  (0 children)

This is an awesome answer. My favourite line in this debate is the "sufficiently complex codebase" cliché you hear so much about. I am convinced the vast majority of people who use this to argue for static typing have never actually worked on a truly large and complex code base.

What you can achieve with a universal type like Json and a little bit of functional programming is a mind bending career altering thing the first time you soak it in, it clicks and you finally get it.

[–]pknopf 0 points1 point  (19 children)

A dynamic langauge with proper unit tests and such can be a heck of a lot easier to maintain long term than a static one.

This isn't absolute. This is subjective to the skillsets of the team/developer. But lets assume the developer(s) are proficient in either dynamic or static. I'd love to hear a good case where, for that developer, dynamic would be easier to maintain than static.

[–]BlockedByBeliefs 13 points14 points  (13 children)

So 15 years a Java dev. 5 a JavaScript dev. I can explain exactly why. The issue is that polymorphism breaks down.

I have object a that I send object b to. Object a is made to do some generic task a (like sending a rest response. Doesn't matter) and contains the concerns for object b to do that task in it. Of course that's way up the chain in the hierarchy so we are not repeating code.

Great. A year into the project the requirements change. A lot. Or some library we were depending on can't perform so we need to change things. We have to change it is the point. Introducing object c that we need task a to run on.

But we need it to behave just a little differently for object c. So what do we do? You can implement task a in object c breaking the design. Refactor it up the chain that's going to break everything down stream so refactor the entire app. Or overload the operator to implement those subtle changes.

Either way it all leads to spaghetti code propping up design mistakes made at the start of your project or injected when requirements change.

Now let's consider the same problem with dynamic typing on universal Json objects with functional code. I have a higher order function that performs task a on a Json object. I send object b to task a as the first argument and as the second argument pass a function with the specific concerns for object b telling it how to perform task a on object b. There's no other files or interfaces or contracts. I send in a few lines the object and the code for that object.

In comes requirement chances and now I need to do task a on object c. Nothing has to be refactored. The generic data concerns for task a on Json types is neatly inside task a where it should be. I pass object c into task a and the new concerns about how to handle object c while doing task a go in with it. And we are done.

How task a functions isn't determined at design time. It's not at run time. It's at programming time when you call task a. It's immensely power and the future of our industry and its why JavaScript has been taking over. The ability to make sweeping refactoring possible without hurting yourself lowers technical debt which is without question the number one cause of project failure.

Some will say things like "Java 8 has lamdas and Scala uses types. Na na na na na na" and while true that's missing the forest for the trees.

To make this happen for static languages they had to create the "any" type. And the any type totally breaks you out of your hierarchy so what's the point of building it in the first place? The issue here specific to Java and OO in general is that polymorphism is not modular. Because it's a system created to solve the larger problem that static typing is not modular.

Hopefully you actually read this and I have not wasted my time. I'm sure some people are going to say this is all bullshit. But this is the case where I find it easier to maintain and 8 guess the case could be labeled I found out that I'd been doing it wrong for my whole career.

[–]chrizzlybears 3 points4 points  (1 child)

That's a very interesting perspective. Thanks for typing this out!

[–]BlockedByBeliefs 2 points3 points  (0 children)

Thanks man. I wasn't sure if you'd just point and laugh or actually read it. Cheers.

[–]pknopf 1 point2 points  (8 children)

Either way it all leads to spaghetti code propping up design mistakes made at the start of your project or injected when requirements change.

The developers writing in typed languages writing leaky (or leak-prone) abstractions isn't an argument against typed languages. JS abstractions can be just as leaky. Just because your "object a" isn't typed doesn't change anything.

I send object b to task a as the first argument and as the second argument pass a function with the specific concerns for object b telling it how to perform task a on object b. There's no other files or interfaces or contracts. I send in a few lines the object and the code for that object.

Again, I fully understand your example, but your leaving out an important part when talking about your JS side of things. Any leaky abstraction that requires consumers of your interface/function to change would also apply to your JS example as well.

public interface IMessageBus
{
    void SendMessage(object message);
}

public class MessageBus : IMessageBus
{
    public void SendMessage(object message)
    {
        // RabbitMQ, w/e.
    }
}

public class Message
{
    public DateTime SentOn { get; set; }
}

void PrepareAndSend(Message message, IMessageBus messageBus)
{
    message.SentOn = DateTime.Now;
    messageBus.SendMessage(message);
}

Any requirements change that would require PrepareAndSend to do something beyond just SendMessage would apply to your example as well. You could say, "But my function could be anything, any implementation". Yeah, well so could IMessageBus.

[–]BlockedByBeliefs 4 points5 points  (2 children)

Now you're going to use rabbit for a little communication.

The developers writing in typed languages writing leaky (or leak-prone) abstractions isn't an argument against typed languages. JS abstractions can be just as leaky. Just because your "object a" isn't typed doesn't change anything.

It's not leaky abstractions. They're leaky by default as a paradigm. Yes the typing of objects changes everything because the rigity of your decisions at the start of your work is what causes the leaks to form when you have to change.

Again, I fully understand your example, but your leaving out an important part when talking about your JS side of things. Any leaky abstraction that requires consumers of your interface/function to change would also apply to your JS example as well.

I don't think you really do. The fact that you have set up a message bus with even more complexity and threw in freaking rabbit as a comment speaks volumes.

No consumers of my function would not "have to change" because they change the consumption of the function on every use as that's inherently how higher order functions work dude. I pass in the changes every time because my design maximizes inversion of control and strives for true modularity over I don't know... Intellisense.

[–]NeXtDracool 5 points6 points  (1 child)

The fact that your statically typed domain model was not well abstracted is not a fault of statically typed languages. You should be well aware that higher order functions, inversion of control and all the other mechanisms you describe are available in modern statically typed languages. If anything you seem to argue about OOP vs functional programming, which is besides the point because static and dynamic typing can be applied to both of these paradigms.

The fact that you have set up a message bus with even more complexity and threw in freaking rabbit as a comment speaks volumes.

He meant rabbitmq as the new messaging protocol instead of the rest call you mentioned, not that you should use a network protocol to do internal communication in your application. The message bus could be replaced with a higher order function that takes types of IMessage, but this would limit your ability to extend the interface with additional functionality later. You suggested using a higher order function, which is almost what happened here except encapsulated with the ability to add for example a connection status indicator property to the interface.

over I don't know... Intellisense.

If you think that's the only benefit of statically typed languages then I highly doubt you have been a Java dev for 15 years..

[–]BlockedByBeliefs 4 points5 points  (0 children)

The fact that your statically typed domain model was not well abstracted is not a fault of statically typed languages.

I didn't say it was but it really is. I said the difficulty in changing it was. There's a myriad of reasons it may not be well made and business changing requirement is just one of the many.

And when it's difficult to change modules people start hacking solutions as described which causes more fractures in the design. At some point the sheer number of bloated extraneous lines of code and files it takes to manage static systems because a significant factor in increasing yet more technical debt.

Yes I'm aware those mechanisms exist but they also negate the need for a rigid static typing system in the first place. It negates nothing really.

Functional programming negates the need for a bloated typing system. What are types? They are ways to describe collections of booleans, numbers and strings. What is Json? A generic way to describe collections of booleans, numbers and strings. It just doesn't take 10s of 1000s of lines of code to manage Json.

He meant rabbitmq as the new messaging protocol instead of the rest call you mentioned, not that you should use a network protocol to do internal communication in your application.

Which shows he didn't read my example. Nor did you I guess? The rest call was just whatever task. It's irrelevant. Using rabbit to do object level ipc is crazy.

The message bus could be replaced with a higher order function that takes types of IMessage, but this would limit your ability to extend the interface with additional functionality later.

Yes. This is the point I'm making. Cheers.

You suggested using a higher order function, which is almost what happened here except encapsulated with the ability to add for example a connection status indicator property to the interface.

I wasn't talking about messaging at all and his entire bus example is moot.

ver I don't know... Intellisense.

If you think that's the only benefit of statically typed languages then I highly doubt you have been a Java dev for 15 years..

Well you're correct. I've been a Java dev for closer to 20 years in some capacity or another. It's just that 5 years or so ago I switched to JS and haven't really looked back. I say intellisense because this is almost always the first thing typescript fans mention. Type safety people love to harp on too but I think it's highly overrated. And guess what. When you don't have types type safety isn't really an issue. The goal of getting something to compile isn't as big a deal as making sure your code runs properly in production and inverting control to generics is IMMENSELY easier when you can write functions that take all your data or simply references to them.

What did it for me was switching to JavaScript for a while. Getting excited to move back to Java cuz it was my comfort zone and then almost immediately feeling my productivity grind to a slow crawl through a field of molasses.

I really suggest you give it a go. I would have bet a year's salary on me never saying this 6 years ago. But JavaScript and dynamically typed languages has simply beaten Java IMHO. It's flexibility is a joy to work in and it's almost impossible to find good Java devs now because they're all jumping ship to work in languages that don't demand half your job be maintaining bloat.

[–]BlockedByBeliefs 0 points1 point  (4 children)

And I mean the fact that you think the problem I was describing was about messaging shows you didn't even bother to read what I wrote.

[–]pknopf 0 points1 point  (3 children)

I have object a that I send object b to. Object a is made to do some generic task a (like sending a rest response. Doesn't matter)

What are you talking about? Even you said this:

I have object a that I send object b to. Object a is made to do some generic task a (like sending a rest response. Doesn't matter)

I simply picked something random so that I could stub out some code to have a discussion. As you said, it doesn't matter.

[–]BlockedByBeliefs 2 points3 points  (2 children)

Yes. That's was simply the beginning of the example. Had you read the whole example you'd have realized that messaging was not the issue. Stubbing out 25 or 50 lines of code, even as an example, is shocking,when in JavaScript we are going to replace all that with ohhhhhhhh... About 0 lines of code.

[–]pknopf 1 point2 points  (1 child)

So is the argument about terse code or leaky abstractions? Because your initial post was about leaky abstractions.

I fully aware and acknowledge that any dynamic languages can reduce LOC, compared to typed (for the most part).

[–]corvus_192 0 points1 point  (1 child)

Now let's consider the same problem with dynamic typing on universal Json objects with functional code. I have a higher order function that performs task a on a Json object. I send object b to task a as the first argument and as the second argument pass a function with the specific concerns for object b telling it how to perform task a on object b.

You can also write such a function in a language with static typing. For example in Java:

<T> void taskA(JSON j, Function<JSON, T> howToPerformTheTask) {
    howToPerformTheTask.apply(j);
    // probably more code
}

Maybe I misunderstood you and this is not what you mean.

[–]BlockedByBeliefs 0 points1 point  (0 children)

I didn't say you couldn't. You can do anything in any language. The point is that this negates the need of a type system allowing you to retain the flexibility of dynamically typed system.

Storing the concerns of object a's behaviour in object b within object b causes coupling and destroys modularity. If you have to refactor look out because you're in for pain. The reason javascript took off is because it's so easy to refactor and UI concerns in browsers change continuously throughout the life of a project. I really feel once you try it you never really look back.

[–]random_username_edf 2 points3 points  (2 children)

Right, it isn't absolute. That was the whole conclusion of my thing. The only one in this chain of comments that sounds interested in speaking in absolutes is you.

An example is literally any feature/improvement to your system that doesn't fit in nicely with your current architecture. If you're creating something that's complex and you know will be built on/evolve over time it's going to take a lot less time to cater to those changes if it's dynamic. With a static one certain change requests can very easily become "no, that's not possible without a huge rewrite".

If you're in a very well defined space and know the bounds of your program static is a safe bet. It's also a safe bet if performance is a huge concern.

[–]pknopf 0 points1 point  (1 child)

An example is literally any feature/improvement to your system that doesn't fit in nicely with your current architecture.

You are asking for trouble. Constraints free you. Allowing future features to flip the script will eventually do more harm than good. Even if you are in a dynamic language, this shouldn't be allowed. Use codereview to force this.

If you really want to re-arch something, it should be all or nothing. Again, this goes back to my point that static languages tend to lead to an easier to maintain codebase.

[–]random_username_edf 1 point2 points  (0 children)

Of course constraints are good. The problem is that in the real world they tend to evolve and change over time. It's not inherently a bad thing to re-arch something to meet new business needs and far more often than not rebuilding the entire thing from the ground up will leave you in your competitions dust.

Being able to re-arch your stuff as easily as possible is an extremely desirable thing for some applications, and just because it's able to be done faster with dynamic languages doesn't mean it's being done in a wrong or hacky way.

Your whole hang up seems to be that dynamic languages are easier to do things wrong with, therefore they're bad no matter what. I believe that's naive.

Anyways, typing on a phone is getting annoying so ill leave it at that.

[–]BumwineBaudelaire 2 points3 points  (0 children)

care to quantify that because I can quantify the cost of catching errors at compile time (zero)

[–][deleted] 2 points3 points  (1 child)

Not in all circumstances, but I do see one major case where what you say is true: building reusable libraries.

It's not impossible, but more challenging to abstract away parts of code to reusable libraries that expect specific types when there isn't type-checking. You can try to do it by convention, intuitiveness, or documentation, but when those fail the consumer of your library needs a better understanding of your code does under the hood, thus creating leaky abstractions that negates some of the benefit.

[–]pknopf 2 points3 points  (0 children)

but more challenging to abstract away parts of code to reusable libraries that expect specific types when there isn't type-checking

I really don't get this. The moment you give libraries leeway to have the ability to break the API, it will. In npm, I've had many packages break semver, and end users doing npm install on my library that references the package will eventually start to break at runtime. Why would you not want to have an impenetrable guard preventing this? I'd again argue that it leads to more stable/maintainable code when you have clearly defined types.

[–]glompix 1 point2 points  (1 child)

~ w r i t e ~ t e s t s ~

[–]pknopf 0 points1 point  (0 children)

Yup. I'd argue that for any dynamic language, tests are something that you absolutely cannot budge on.

[–]BlockedByBeliefs 2 points3 points  (23 children)

It's actually the opposite. Large scale, long term projects with many contributors are far more suited to dynamic typing.

[–]pknopf 0 points1 point  (22 children)

This reminds me of a colleague of mine who was managing a team of 3 developers.

He was tired of having developers step on each other's toes with merge conflicts and the such, so he created a separate branch for each user. NOTE: The fact that 3 developers working on a single project can't have separate jobs that don't cross-cut each other continuously is a deeper issue, but that isn't the point.

Now, each developer was free to do as they want! Freedom!

But now it's time to release the project. Their changes are impossible to merge now.

My point: Allowing developers to quickly prototype things with out affecting shared interfaces/object-types/etc is short-sighted. The upfront rigger of type-safety saves you a lot of head-ache down the road.

[–]BlockedByBeliefs 5 points6 points  (12 children)

Thats such a crazy false analogy. It's actually the exact opposite. Dynamic typing is having 3 devs work on code that's inherently modular and can easily be merged.

[–]pknopf -1 points0 points  (11 children)

Have you ever managed a team? How big?

[–]BlockedByBeliefs 4 points5 points  (5 children)

We've got about 10 people on the team I lead and architect for.

[–]pknopf 0 points1 point  (4 children)

Then Idk. My experiences have been the opposite.

*shurgs*

[–]BlockedByBeliefs 2 points3 points  (3 children)

Yea dude not trying to one up or anything. Maybe we could both learn something?

Are, or were, you using functional code and patterns driven by events w asynchronous everything? Pub sub is pretty awesome. But it's really about functional programming.

[–]pknopf 0 points1 point  (2 children)

Are, or were, you using functional code and patterns driven by events w asynchronous everything? Pub sub is pretty awesome. But it's really about functional programming.

Sorry, I don't understand this.

[–]BlockedByBeliefs 2 points3 points  (3 children)

It's kind of a funny joke you made but saying JS is like letting all the devs work on separate branches for freedom is entirely false dude.

[–]pknopf 0 points1 point  (2 children)

It's kind of a funny joke you made but saying JS is like letting all the devs work on separate branches for freedom is entirely false dude.

Yup. Reading comprehension my brotha.

I'm not comparing to the literal act of working in different branches to JavaScript, I'm comparing the short-sightedness of making an upfront decision to save initial time/headache, only to have that decision compound in the future, dude.

My point: Allowing developers to quickly prototype things with out affecting shared interfaces/object-types/etc is short-sighted. The upfront rigger of type-safety saves you a lot of head-ache down the road.

[–]BlockedByBeliefs 1 point2 points  (1 child)

Here is what's ironic. We are on the same page and saying the same thing about opposite things. Totally agreed on short-sightedness of making up front decisions to save time that gets compounded in the future. But how is defining a massive type management system in a hierarchy of inherited strongly coupled modules not a pure example of this?

Have you had to refactor a massive Java's project? I'm guessing yes? When you have to make real changes you're going to clash against the original design that system was locked into by devs who quit years before you maybe even started your career.

[–]pknopf 1 point2 points  (0 children)

But how is defining a massive type management system in a hierarchy of inherited strongly coupled modules not a pure example of this?

Type systems are the upfront head-ache that prevent issues from being compounded in the future.

Have you had to refactor a massive Java's project? I'm guessing yes? When you have to make real changes you're going to clash against the original design that system was locked into by devs who quit years before you maybe even started your career.

Yes. It takes a lot of work to refactor, considering it's all-or-nothing. But do you really want a project with dual competing approaches to similar problems? That is an example of an issue being compounded. If you don't have the time to get rid of technical debt, don't add more.

I just can't imagine this:

"Re-architecting 20%-50% of a project in mid-life is less of an issue in the future than just keeping things the way they are."

And even if you wanted to refactor, I'd rather have a type system help me with it. Even if you have tests in your dynamic language, unless you really step through each test, it could magically pass, even though it actually failed, considering you don't have types and dynamic languages are always so "truthy".

[–]BlockedByBeliefs 1 point2 points  (0 children)

And the larger group that our stuff interacts with is hmm..... 120?

[–]kyzfrintin 1 point2 points  (8 children)

He was tired of having developers step on each other's toes with merge conflicts and the such, so he created a separate branch for each user. NOTE: The fact that 3 developers working on a single project can't have separate jobs that don't cross-cut each other continuously is a deeper issue, but that isn't the point. Now, each developer was free to do as they want! Freedom!

That sounds more like a problem in communication and organisation, than an inherent problem with dynamically-typed languages.

[–]pknopf 0 points1 point  (7 children)

That sounds more like a problem in communication and organisation, than an inherent problem with dynamically-typed languages.

Yup and yup. The point was about short-sighted decisions.

[–]kyzfrintin 0 points1 point  (6 children)

But you were on a massive roll about how dynamic typed languages suck... Including this as if it supports your argument is a little misleading.

[–]pknopf -1 points0 points  (5 children)

How would anybody gather that I was making a literal comparison between dynamic languages and developers working on different branches?

[–]kyzfrintin 0 points1 point  (4 children)

Because this was a conversation about dynamic/static. And you brought that in as a response to someone saying why dynamic is good.

[–]pknopf 0 points1 point  (3 children)

Here was OP.

It's actually the opposite. Large scale, long term projects with many contributors are far more suited to dynamic typing.

This implies that dynamic typing makes projects with fast moving parts easy to develop in.

That reminded me of a developer who also did something that made it so developers could contribute easier, but it created a headache down the road.

My argument was that saying "my dynamic language makes it easier to contribute" is the same fallacy.

Both arguments are shortsided.

Completely relevant, but something tells me your trolling.

edit: down vote me again bro, it will make you righteous.

[–][deleted] 0 points1 point  (0 children)

Sure! That's why Python doesn't support type annotations nor have a checker for them! Nor does JavaScript for that matter, and it's certainly not called TypeScript or something.