[ $davids.sh ] — david shekunts blog

🚦To Return or to Discard

# [ $davids.sh ] · message #169

🚦To Return or to Discard

**Now, I'll be posting a series of summaries based on the above surveys.

The coolest indicator in this survey is that 36% of respondents would like to return errors, but instead discard them.

Most often, this is due to the fact that in their language, it's common to discard errors, but people understand that the "standard" is not always the "right way".

Also, in the comments to this post, @highloadzone and I (thank you very much for the discussion) had a massive discussion on the topic of errors and traces, which I recommend reading.

There, I came to the following thought:

Real "errors" are those that we allow to bring down our application, and they should be discarded.

And all other "errors" should be considered as "failures" or "non-successes", and they should be returned.

If you follow this path, you'll get the best of both worlds:

  • Dangerous errors will be caught at the top of the stack and will have the right to even bring down the application;
  • And "failures", which are syntactically convenient to express in the form of an Error structure, will always be returned from functions, because we'll have to process this failure in some way 1 or more times.

I realized that the topic is extremely large, so I'll dedicate a separate section to it in the FOP book, where I'll talk about the principles laid down by Rob Pike in the design of Go, error handling in functional languages, the problems of hidden control flow, and Railway Oriented Programming.

  • @ Andrey Rudin · # 370

    who is the author of this? https://fop.davidshekunts.ru/origins/you-have-never-used-oop I have so many questions for you! )

  • @ [ $davids.sh ] · # 371

    For any questions, feel free to reach out to me) We can discuss everything right here in this thread, and I’ll turn our conversation into a separate post) I welcome all comments (they help expand the ideas in the book)

  • @ Andrey Rudin · # 372

    Here or in private messages? Or is there a separate thread for that article?

  • @ [ $davids.sh ] · # 373

    Go right here, I love public discussion, even if it's criticism.

  • @ Andrey Rudin · # 374

    Great. About this provocative headline. You've never used it if you have ...Service and so on. So, OOP is about everything being objects and exchanging messages.

  • @ Andrey Rudin · # 375

    I'm not talking about SOLID and all that right now, completely without it.

  • @ Andrey Rudin · # 376

    By the way, just curious—what platform is this? What's it running on? :) Really loved it, honestly.

  • @ Andrey Rudin · # 377

    Question number 1. If it's not OOP, what constitutes data in the OOP world? Like the same thing, but data should be hidden, everything goes through methods. So, in the OOP world, an object can't just be data?

  • @ [ $davids.sh ] · # 378

    Yes, the title is intentionally provocative, about "all objects exchanging messages" – you're absolutely right, but the most important part of "canonical" OOP is that all "objects" encapsulate both behavior and data.

    When you create a ...Service that manages other models, you lose the encapsulation of those models and start working with them in a more procedural style (which is what FP is about), so I'm saying this is closer to FP than OOP.

    Because OOP is primarily about architecture, not syntax (there are many examples where purely OOP code is written in Haskell/Elixir by combining data and behavior).

  • @ [ $davids.sh ] · # 379

    Notion + react-notion-x + Vercel

    I will write an article on Habr about how to do this, but you can simply take my repository (https://github.com/Dionid/fop-site) and replicate the same thing completely free of charge.

  • @ Andrey Rudin · # 380

    No, don't go too far. I

  • @ Andrey Rudin · # 381

    I get it all. But I've accumulated a few questions, you caught me at a bad moment, so to speak ))))

  • @ Andrey Rudin · # 382

    Thank you very much!

  • @ [ $davids.sh ] · # 383

    Perhaps it will be called a POJO, and if specifically from the OOP world, then an Anemic / Slim Model.

    The main thing I want readers to understand is that this is not canonical OOP; it’s a variation of it, and practically from a different perspective.

    That is, if you widely use Anemic / Slim Model + Service Object, you’ll end up with procedural programming.

    But the question arises: why even use OOP then? And if someone doesn’t want to, they can look into procedural programming, BUT there are few materials that describe it well enough + it’s outdated, which is why I created FOP, within which I took the best from procedural programming, structured it, and added some functional programming.

  • @ Andrey Rudin · # 384

    So, in object-oriented programming languages, such as Java, it is fully object-oriented.

  • @ Andrey Rudin · # 385

    it contains data.

  • @ Andrey Rudin · # 386

    Everything is an object, the same goes for Dart, for example, C#.

  • @ [ $davids.sh ] · # 387

    You can write purely procedural code in Java (maybe even functional)

    Because syntax and paradigm don't depend on each other, syntax can "help" but doesn't define it

  • @ Andrey Rudin · # 388

    there is data, for example let's take Integer

  • @ Andrey Rudin · # 389

    it is an object that has a class.

  • @ [ $davids.sh ] · # 390

    You are confusing an object from a language and an "object" from OOP.

  • @ Andrey Rudin · # 391

    It doesn't matter.

  • @ Andrey Rudin · # 393

    let's skip the syntax, here we have a NUMBER

  • @ Andrey Rudin · # 394

    this is an object, let's say we have two objects of type NUMBER.

  • @ Andrey Rudin · # 395

    We can do something with them, like +, -, and so on.

  • @ Andrey Rudin · # 396

    A number doesn't care what is done to it, but it is part of OOP.

  • @ Andrey Rudin · # 397

    There is a class, from the class we create an object, our object is a number, we interact with it in some way, and we get a new number.

  • @ Andrey Rudin · # 398

    A number doesn't care that it exists, where it's used, or that it allows something to be done with it.

  • @ Andrey Rudin · # 399

    we use it with respect :) as an object

  • @ Andrey Rudin · # 400

    just in case, that was a reference to Yegor Bugayenko ;) if it wasn't clear, and his book "Elegant Objects."

  • @ [ $davids.sh ] · # 401

    Are we talking about any programming language or about the fact that in any programming language we want to turn a number into an "object" that will have methods?

  • @ Andrey Rudin · # 402

    Well, that's just lyrics, a programming language, or whatever it has, really.

  • @ Andrey Rudin · # 403

    OOP - object-oriented programming. Is a number an object? Yes. We focus on the object, yes. We are engaged in programming, yes.

  • @ [ $davids.sh ] · # 404

    Then, in most languages, your number will simply be a reference to memory with the value of the number, not an object.

    It won’t have any methods; it will just be a bare number.

    So, perhaps you want us to focus on languages where numbers are turned into objects with methods?

  • @ Andrey Rudin · # 405

    Does a number have a state? Yes. Can we send a message to a number? Yes. That means it's part of OOP, right?

  • @ Andrey Rudin · # 406

    Well, let's do it that way, we'll take Java as the most canonical OOP language, right?

  • @ [ $davids.sh ] · # 407

    Great, so at the very least we’ve immediately agreed that there are specific languages whose syntax implies that they turn even primitives into objects with methods, right?

  • @ Andrey Rudin · # 408

    Yes

  • @ Andrey Rudin · # 409

    There are no problems with this, I mean understanding that in Java a primitive (a memory cell) is wrapped in an object. There is state, we can send messages, right?

  • @ [ $davids.sh ] · # 410

    So, look, I often write in the book that "syntax is not the same as paradigm/architecture," and that I describe OOP and FP specifically as "architecture," which can be used both in a language with built-in OOP and in a purely functional/procedural language.

    So yes, a number or any piece of data can be an object with methods—that would be OOP syntax—but whether the codebase uses OOP architecture is ultimately a choice for each individual.

  • @ Andrey Rudin · # 411

    Stop, stop, stop, what’s this about architecture? You’re talking about canonical OOP, and then suddenly about architecture?

  • @ [ $davids.sh ] · # 412

    Starting from this chapter, I explain what "OOP as architecture" is.

  • @ Andrey Rudin · # 413

    Got it, there’s a reference to the previous article https://fop.davidshekunts.ru/origins/you-dont-know-oop. I’ll go read it now and come back later 😄

  • @ Andrey Rudin · # 414

    So, for now, let's think about it: we have concepts like OOP syntax, OOP paradigm, and OOP architecture.

  • @ Andrey Rudin · # 415

    Are these all different things?

  • @ Andrey Rudin · # 416

    The OOP paradigm is about objects exchanging messages, hence it is an architecture.

  • @ [ $davids.sh ] · # 417

    I distinguished between "syntax" (for example, in Java and JS everything is an object) and paradigm/architecture (because they have many overlaps), but even these concepts can be separated (I would have had to describe too many subtle differences, so I combined these concepts).

  • @ [ $davids.sh ] · # 418

    Generally, yes.

  • @ Andrey Rudin · # 420

    okay, good, so what's wrong with the syntax and architecture?

  • @ Andrey Rudin · # 421

    The syntax also supports the architecture.

  • @ [ $davids.sh ] · # 422

    The syntax is fine, no questions about it.

  • @ Andrey Rudin · # 425

    Using syntax, we describe the architecture, meaning we write in the OOP architectural style in some specific programming language, right?

  • @ [ $davids.sh ] · # 426

    No, syntax does not define architecture, BUT it can undoubtedly influence it.

  • @ Andrey Rudin · # 427

    I didn't say defines, I said describes, implements in a specific programming language.

  • @ Andrey Rudin · # 428

    Syntax can vary depending on the programming language. For example, Object.message or Object->message.

  • @ [ $davids.sh ] · # 429

    Optional, as I said: you can write in Haskell with a purely OOP architecture or in Java with a purely procedural one

  • @ [ $davids.sh ] · # 430

    Both examples are OOP syntax.

    Functional programming would be get(object, property).

  • @ Andrey Rudin · # 431

    It would seem so, but I disagree with you here. Either our understandings differ, or you haven't fully explained yours in the text.

  • @ Andrey Rudin · # 432

    It's good that you wrote this, there's something before get too, right? Let's say it's SomeService.

  • @ [ $davids.sh ] · # 433

    No) That's the whole point of procedural and functional programming)

  • @ [ $davids.sh ] · # 434

    I think we need to first clarify what we mean by syntax, paradigm, and architecture.

  • @ Andrey Rudin · # 435

    Well, wait a minute, you could argue that the code in the canonical User object's method is written linearly, sequentially, procedurally, which means there's no OOP smell here at all :)

  • @ Andrey Rudin · # 436

    Let's go back a bit, let's look at User

  • @ Andrey Rudin · # 437

    Since Integer is quite abstract, let's use User as an example.

  • @ Andrey Rudin · # 438

    Here's how you could do it in canonical OOP, simplified:

    Let's say you have a User class.

    class User:
        def __init__(self, username, role):
            self.username = username
            self.role = role
    
        def is_admin(self):
            return self.role == "admin"
    
    # Example usage:
    user1 = User("Alice", "user")
    user2 = User("Bob", "admin")
    
    if user1.is_admin():
        print("Show admin link for Alice")
    else:
        print("Hide admin link for Alice")
    
    if user2.is_admin():
        print("Show admin link for Bob")
    else:
        print("Hide admin link for Bob")
    

    Explanation:

    1. User Class: We have a User object.
    2. role Attribute: The User object has an attribute (like role) that stores their role (e.g., "user", "admin", "editor").
    3. is_admin() Method: The User class has a method called is_admin(). This method simply checks if the role attribute is equal to "admin".
    4. Conditional Display: In your application (website or other), when you need to decide whether to show an "admin" link or button, you would call the is_admin() method on the current logged-in user object. If it returns True, you display the link/button; otherwise, you hide it.

    This is a very basic approach. In a real-world application, you might have more complex role management, permissions, or even inheritance for different user types. But for the core task of checking if a user is an administrator, this method is a clean and object-oriented way to do it.

  • @ [ $davids.sh ] · # 439

    OOP syntax – language constructs and its structure, for example, that a number is an object with methods and using their methods via dot-notation (number.toString())

    OOP paradigm – a set of coding rules, including 4 main pillars (encapsulation, abstraction, inheritance, and polymorphism)

    OOP architecture – separation of business entities into "objects" encapsulating logic and behavior (User.changeEmail(newEmail))

    FOP syntax – there is no such thing, FOP is developed for multi-paradigm languages, and therefore allows the use of any syntax

    FOP paradigm – a set of coding rules, including 3 pillars (Separation of data and behavior, Composition, Polymorphism on interfaces)

    FOP architecture – separation of business entities into objects that store only data, and use cases that use any data to reproduce business logic (changeUserEmail(user, newEmail))

  • @ [ $davids.sh ] · # 440

    In canonical OOP, I should have an object that encapsulates the data needed to execute logic and behavior.

    A simple option is user.isAdmin(), a more complex one is userPermission.checkUserIsAdmin() where userPermission internally has a reference to the necessary user and calls their method, like user.getRole().

  • @ [ $davids.sh ] · # 441

    Within the FOP, I'll simply have an isUserAdmin(...) function where you can pass any of the objects, for example isUserAdmin(user) or isUserAdmin(user, role) if the data is stored separately.

    The user object will contain just the data, and all the logic will be in functions like isUserAdmin.

  • @ Andrey Rudin · # 442

    Ugh :( I apologize, work came up, I'll be back a little later.

  • @ Andrey Rudin · # 443

    So here you attributed encapsulation to syntax, and in this message, you've attributed it to a paradigm, which you previously separated from syntax and compared to architecture.

  • @ Andrey Rudin · # 444

    I'm back )

  • @ Andrey Rudin · # 445

    although I'll be leaving again soon :(

  • @ Andrey Rudin · # 446

    Well, that's not the point, the main thing is not to lose the thread of the narrative

  • @ Andrey Rudin · # 447

    Here, okay, user.getRole() and what should getRole() do?

  • @ [ $davids.sh ] · # 448

    Thank you very much for this remark! I'll fix it)

  • @ Andrey Rudin · # 449

    Let's say we have this, pseudocode, but the essence is roughly this: getRoles() or getRole('admin') -> true/false

  • @ [ $davids.sh ] · # 450

    Return user role

    This can happen in a number of ways: (1) perhaps it's just a test property on the user, then it will simply return it, (2) perhaps it's in a separate object, then it will go into it and retrieve it (also via a method), (3) perhaps it's a property in the form of a number that User maps to a string, (4) perhaps this method doesn't return all roles, there's also getSuperRole, where it returns super roles, (5) perhaps this method returns an array of roles, and if you want the main one, then there will be getMainRole

    And perhaps, first there will be the first option, and then with the system's complexity, each subsequent one will be added.

    Why do we retrieve the role via the getRole method and not directly user.role, because it would be a violation of encapsulation, which would take away the biggest advantage of OOP: with the development of the getRole method, we won't have to rewrite it from the outside, only from the inside (Open Close Principle or something like that).

  • @ Andrey Rudin · # 451

    What I mean is that the user themselves is stored in the database. To create a user, we need to go there, get them by ID, and then create them.

  • @ Andrey Rudin · # 452

    Secondly, data needs to be loaded lazily, so our born user isn't loaded all at once but partially. Then, we need to, for example, get a list of roles to find out if they are an admin. Someone needs to go to the database, return this user data, and then they will return it to UserPermission.

  • @ Andrey Rudin · # 453

    What I mean is that there's a lot around the user, the user can have a lot of things, the user will access the data source for roles, or some abstract data repository.

  • @ Andrey Rudin · # 454

    So, what I want to say is, why do we need to recreate all of this if we only need to know if someone is an admin or not?

  • @ Andrey Rudin · # 455

    user, permission, repository, a lot of things.

  • @ Andrey Rudin · # 456

    Why isn't PermissionService an object that itself accesses the database and returns data to us?

  • @ [ $davids.sh ] · # 457

    Honestly, I don't quite understand what you're talking about: you asked about getRole, and I explained why getRole in canonical OOP, rather than just fetching the necessary data.

    In a functional programming paradigm (FOP), I could afford to make a database query and fetch the necessary data. If it became more complex, I would extract a separate function getUserRole and do what's needed there with any number of objects I'm interested in, without linking them to each other.

  • @ [ $davids.sh ] · # 458

    It seems I understood better: the question is why not make PermissionService.getUserRole() instead of User.getRole()?

  • @ Andrey Rudin · # 459

    including.

  • @ Andrey Rudin · # 460

    Why do you think PermissionService is less OOP than PermissionUser -> User.getRoles -> ... and so on?

  • @ Andrey Rudin · # 461

    What I mean is that OOP is a concept of working with objects through messages.

  • @ Andrey Rudin · # 462

    architecture, SOLID principles, patterns, these are already the next semantic layer, but conceptually, it's OOP one way or another.

  • @ Andrey Rudin · # 463

    Let's take this code as an example. A User deletes a User. Am I giving a User to another User to delete? How can I understand this at all? Who created the second User? Why is a User deleting a User?

  • @ Andrey Rudin · # 464

    I just want to say that many people have big, big prejudices about OOP, and architecture enthusiasts are "sick" people. What can be done in 20 lines of code in a "procedural" style, they do in "OOP" but for this, you need to write 2000 lines of code. This is, damn it, nonsense.

  • @ Andrey Rudin · # 465

    This looks strange, operations on an object within an object.

  • @ Andrey Rudin · # 466

    What if I told you that functions are just methods of a global object called God, and that's why they're called without an object like func(params) instead of object.func(params)?

  • @ Andrey Rudin · # 467

    I don't remember where else I saw it, but maybe it's this text...

  • @ Andrey Rudin · # 469

    Why can't we have an object type like SALE_PROCESS for example?

  • @ [ $davids.sh ] · # 470

    And you would be absolutely right) That's the whole point: our OOP object should be the application itself / library / module, whatever you call it

    I write about this in the next article

  • @ [ $davids.sh ] · # 471

    What will be the difference between #1 and #2 at this link?

    https://gist.github.com/Dionid/616eb8915d1c2b9bb74a56342cf3dbac

  • @ Andrey Rudin · # 472

    nothing

  • @ [ $davids.sh ] · # 473

    Let me clarify again: OOP + Anemic/Slim model + Service Object, from an architectural standpoint, is closer to procedural programming than to canonical OOP, even though it can still be called "OOP" (because it uses its syntax and can employ the paradigm, but architecturally it's no longer the same).

  • @ Andrey Rudin · # 474

    but one in OOP style, the other in functional.

  • @ [ $davids.sh ] · # 475

    Syntactically, yes! But architecturally, both options are closer to (if not outright) functional/procedural programming than to canonical OOP.

  • @ Andrey Rudin · # 476

    Why should they be different? It's programming in both places, it's just that in one case it's a function, and in another it's a method or a message for an object.

  • @ Andrey Rudin · # 477

    If we accept as a statement that canonical OOP necessarily means objects, each of which is responsible only for itself, and that it should not contain objects with public data, nor objects without data. And if we consider that OOP is only within such narrow limits, then yes, I agree with you, but you cannot write programs like that in 99% of cases. Or you can, but 99% of the program's code will be useless bullshit, for accessing data from an object, mapping, and so on. Therefore, ONLY pure canonical OOP will never exist anywhere.

  • @ Andrey Rudin · # 478

    But to claim that we didn't program in OOP because you used a procedural style in some of your programs, that's also not entirely correct, don't you think?

  • @ [ $davids.sh ] · # 479

    And the problem with OOP is precisely in your last sentence: "programs in canonical OOP will never and nowhere exist."

    BUT an absolutely procedural program is quite possible.

    Then the question is: why follow a paradigm that is incomplete and forces you to resort to hacks and workarounds of the paradigm itself, while imposing restrictions and complications that require you to learn additional patterns (SOLID is a band-aid created by OOP to solve OOP's own problems; they don't exist in functional and procedural paradigms), when you can simply NOT use OOP at all and use something that covers 100% of your cases?

    I am against OOP on ...Service because it's a mix with procedural programming, which at the same time leaves all the problems of OOP.

    I propose to simply abandon it, and if you enjoy writing using ...Service, it means you can likely switch entirely to FOP and get rid of all the problems that can occur in OOP + gain access to a bunch of features from functional and procedural programming.

  • @ [ $davids.sh ] · # 480

    Because the decision of whether your code will be OOP or procedural affects how each team member will write the next function.

    1. If you decide to write in canonical OOP, it means I can always know that anything related to User will be on it.

    2. If we use a procedural approach, it means I will look for a function that solves a specific case and I will know that work on User can happen in a bunch of places.

    3. If we use non-canonical OOP, then part of it will be on User and part will be scattered in different Services.

    In the first and second cases, I know where to write the logic next.

    In the third, I have to guess every time where it would be good to place it, and I really dislike this uncertainty.

  • @ Andrey Rudin · # 481

    Okay, look, someone came up with a cool thing: creating objects and sending them messages. That's it. Then all sorts of crazy stuff was invented, which is fine, but the main thing is that people invented limitations for themselves that you can't cross, otherwise you're not a true OOP programmer, shame on you, get out of the profession.

  • @ [ $davids.sh ] · # 482

    By the way, canonical OOP is partially possible: if you strictly follow OOP, then thanks to Aggregates, you will be able to create acyclic graphs with strict dependencies and methods only on objects.

    But this is incredibly difficult (so much so that it's only possible on a small scale).

  • @ Andrey Rudin · # 483

    This is a cult. OOP for normal people is when it helps you where it's needed.

  • @ [ $davids.sh ] · # 484

    I completely agree here

  • @ [ $davids.sh ] · # 485

    Then let's do this: if you like using ...Service, I'll just recommend trying OOP or procedural programming.

    You'll likely be surprised at how convenient you find it in the end.

  • @ Andrey Rudin · # 486

    The creators of these concepts decided to leave this out and, like, figure out for yourselves how it should be on your project)

  • @ Andrey Rudin · # 487

    but!

  • @ Andrey Rudin · # 488

    I'm for everything being "muddy," and I'll explain why. Because it opens up space for "creativity," everyone tries to invent something of their own, and in the end, more interesting options emerge.

  • @ Andrey Rudin · # 489

    I don't believe in this :) I have no idea who does this and why. ) Yes, the bloody enterprise is forced to do this, as there are large teams, each with its own responsibility, its own domain, now microservices. But the price of all this is catastrophically high.

  • @ Andrey Rudin · # 490

    I don't reject this; writing a program simultaneously in OOP, procedural, and where needed, functional styles, is normal! )

  • @ Andrey Rudin · # 491

    For me, important criteria are development speed and the ability to expand functionality. Lately, testability has also become important, but there are no problems with any of these in either place. Therefore, I am gladly studying your material.

  • @ [ $davids.sh ] · # 492

    Well, here it's everyone's choice.

    I realized that a mix with OOP gives me more problems than when I write completely procedurally / functionally, because I follow a specific set of rules:

    1. Data has no behavior
    2. All functions can use all data

    I don't expect some behavior to be on the data and some somewhere in a ...Service.

    When I write a function and need to do something with the data for the first time, I'll do it directly in that function, and only when the context repeats will I extract this function into a library (WYNWYN).

    In contrast to OOP, where people will try to immediately extract operations that happen only once into a Model / Repository / Service or somewhere else, turning them into a God Object, and after that, when they need to do roughly the same thing, they will modify the existing method, potentially breaking something already working, or create a new one, increasing the size of the God Object.

  • @ [ $davids.sh ] · # 493

    Again, within OOP, WYNWYN can be preserved, BUT most often, juniors or, conversely, OOP sectarians will not do this because they have a clearly defined thought: if it relates to some object, then it must be on it, regardless of whether it is used only once in one place.

    I don't expect all my colleagues to be competent enough to make the right choices, so I simplify: I remove the choice by specifying clear boundaries (separation of data and behavior).

    And I believe that programs are many times clearer and more reliable when they have more code repetition than when they mindlessly try to adhere to DRY, which also few understand (this is also discussed in WYNWYN).

  • @ Andrey Rudin · # 494

    I agree with you, but I've played around with different concepts, and now I don't care what I'm called :) I'll mix and use the best from here, there, and everywhere :) If I need an object that does something, I'll create an object and ask it to do something for me. If my procedural code is 30 lines long and my OOP code is 300 lines long, I'll do it procedurally. In many places, especially on the front end, I combine different pieces using functional programming into a whole because it's convenient. I'm all about efficiency.

  • @ [ $davids.sh ] · # 495

    With such an approach, I would call you an extremely competent developer and would be happy to work on the same team)

    It's a shame there aren't many people like that, because of the others, we have to come up with additional restrictions