1. 38
  1.  

  2. 13

    I would normally be right on board with what the author is saying because I rather enjoy functional programming. However, when it comes to sentences like “The truth is, FP communities are ahead of their OO equivalents already.” - I don’t even know what ‘ahead’ means - if it could even truly apply.

    1. 3

      I’ve been exploring this idea for over 2 years and not only did I build libraries in OO language that follow many FP principles but also whole applications. It’s obviously not as “pure” as in real FP langs but it doesn’t change the fact that mixing paradigms is possible and (for me) it turned out to be extremely useful.

      The beginning of my article points to the most important FP concept, immutability, applying just that in OO code makes a huge difference.

      “ahead” as in FP langs provide a great foundation for building software of the future and OO langs start to lag behind when it comes to modern ideas in programming. Then you see things like Java 8 introducing lambda expressions, or Ruby moving towards immutability with frozen strings by default in MRI 3.0.

      1. 5

        OO langs start to lag behind when it comes to modern ideas in programming.

        You have an odd definition of “modern” here. Lambdas, for example, kinda predate OOP.

        software of the future

        What does this even mean?

        Like, yes, there are a lot of cool things from the FP world that we can learn from–but let’s not drink the koolaid too much.

        1. 4

          It means that while OO communities keep building overly complex apps, struggling with mutable state and layers of abstractions, FP communities are doing the exact opposite, focusing on simplicity and more obvious abstractions. Immutability plays a huge role here. I believe that’s the reason why OO langs start introducing some FP concepts like lambdas to simply catch up.

          I was laughing at myself that I’m drinking FP koolaid more than once, and then months were passing, then years, and now I’m at a point where I look at my Ruby code and I see Clojure, Elixir and Haskell, while at the same time the style of coding I ended up with is better than anything I did before.

          BTW I gave a whole talk about this just last month at FullStackFest. Maybe this will explain my pov better (although it’s just slides, video is coming soon).

          Sorry about the confusing usage of the word “modern”, you are of course right. The point I try to make is that old ideas are being applied in a modern fashion, it’s visible especially in languages like Clojure and Elixir. When you consider what those languages are providing and then look at how OO langs are evolving you will realize that OO is trying to catch up with what we’ve learned over the past decades and what’s possible thanks to more powerful hardware.

          1. 2

            There exist plenty of ideas that are old, but only catch on later in a big way. I think lambdas are a valid mention here because as far as the mainstream goes, they are a new thing - if I can define new as roughly the last 5 to 10 years.

            I expect we can date lambdas back to about 1936.

          2. 2

            Are there any non-trivial examples exploring these ideas?

            I have already played with Haskell and Elixir (among others), so some of the ideas I am no doubt somewhat familiar with – but I don’t think I have seen a real good real use of them.

            1. 3

              I don’t have any public applications to show but as far as ideas applied in an OO language go - take a look at Ruby’s rom-rb. It has concepts that come straight from (coincidentally) Haskell (lazyness) and (coincidentally) Elixir (the pipeline operator). What was really interesting for me to see is that I ended up with those concepts after lots of experimentation and trying all kinds of OO approaches to solve some really complicated problems just to move away from OO and solve them in an FP fashion. Now, I did that accidentally before I actually dived into Haskell and Elixir, I guess it was a natural consequence of moving away from object-relational mapping and focusing more on simple data processing.

              1. 2

                Worth noting that the pipeline operator is “just” function application backwards, so it’s coming from something very essential.

        2. 4

          I have to hand it to the sequel ruby library by Jeremy Evans. It’s a dataset-first instead of model-first abstraction. Models are available as a layer on top of datasets, but are not required.

          def users_allowed_to_receive_alcohol
            state_codes = DB[:states].where(can_ship_alcohol: true).select(:code)
            user_ids = DB[:addresses].exclude(state_code: states).select(:user_id)
            DB[:users].where(id: user_ids).where{ age >= 21}
          end
          

          Gets you an chainable dataset representing the query

          SELECT * FROM users
          WHERE age >= 21 AND id IN (
              SELECT user_id FROM addresses
              WHERE stateCode IN (
                  SELECT code FROM states
                  WHERE can_ship_alcohol = true)))
          

          It does a great job of providing a unified abstraction in most cases, and it still allows you to use database specific features (PG’s json, pub/sub, cursors, etc) without breaking the DSL. You could argue for writing raw SQL, but that gets ugly quickly when you’re dynamically building a query.

          1. 2

            Yes, sequel is a very nice library. The active record pattern was a known bad thing back when NeXT called it “Enterprise Object Framework” and it’s a bad thing now.

            1. 1

              This is exactly why rom-sql is powered by Sequel Dataset API. I’m wondering why it’s not a separate gem.

              1. 1

                I was unaware of that. Thanks for the heads up.

            2. 2

              Never had a thing for ORM, great post.

              1. 2

                He didn’t provide any example, but I guess instead of this:

                $user = User::find($id);
                $user->email = 'new@example.com';
                $user->save();
                

                he would do something like this:

                user_save_email($id, 'new@example.com');
                

                I guess it’s a way to do it, but I don’t see how it’s easier to maintain, or less complex or anything really, just different. Since he used ORMs for 10 years, surely there must be something good about them. It just sounds like he grew bored of them and went for a different approach. But whether it’s a superior one or not, and why, is not clear from his article.

                1. 12

                  Note that the author is the author of ROM (http://rom-rb.org). He did not only use ORMs for 10 years, he also implemented a notable one in Ruby (DataMapper).

                  You misunderstand his point: in his opinion, save is not a concern of the user object and this object should have no knowledge of the storage. He’s not against Mapping (quite the opposite, ROM is only a mapper), his opinion is that mapping and domain model are two different concerns.

                  1. 5

                    If you are only setting fields, ORMs would probably be just fine. But it’s rarely that simple.

                    When you have complex relationships and need to run the requisite complex query demanded by whomever is wanting more data, ORMs quickly stop being helpful. You end up smashing the abstraction by writing SQL directly, especially for performance reasons. If your ORM tries too hard, it makes writing that SQL difficult by forcing you to use some bizarre SQL hybrid. Now you have to know both SQL and the SQL oddity, purely for the sake of, well, purity.

                    Even if you don’t use/have the hybrid you still have two versions of queries: ORM stuff and the raw SQL. You’ll want to hide the SQL behind an API (be it internal or otherwise). At this point, you might as well just use an API for it all since that raw SQL ain’t goin' anywhere.

                    The author probably used ORMs because that’s what one did 10 years ago. Like many of us who have tried to use them extensively, we’ve come to realize that, well, they seemed like a good idea at the time.

                    1. 5

                      It’s much deeper than that.

                      ORMs have to do crazy things to make your $user->save() possible. Let me just give you a couple of concerns:

                      • maintaining mutable object state - trivial for a single user, horribly complicated with an object graph with internal dependencies between objects (this can actually become ridiculously complex in cases like ActiveRecord in Rails with all the callbacks, built-in validations etc.)
                      • transforming input data into object properties, then transforming them back to something that can be persisted - again, simple in cases like a user with string/numeric values as properties where almost no coercion/transformation is needed, but it scales very quickly to something much more complex where the amount of cases to handle is infinite
                      • trying to be db backend-agnostic through abstract query interfaces leads to additional abstractions that complicate your stack and they leak anyway

                      Mutable objects are much more difficult to handle than simple values with functions that know how to use them to make changes in your database.

                      1. 4

                        Take a look at how Ecto (the Elixir database library) handles things. It operates on changesets, which are immutable data structures.

                        user = User.get(1)
                        changeset = User.changeset(user, %{email: "bratsche@lobste.rs"})
                        Repo.update(changeset)
                        

                        Or you could probably do the same with pipeline operators:

                        User.get(1)
                        |> User.changeset(%{email: "bratsche@lobste.rs"})
                        |> Repo.update
                        

                        Ecto is still pretty young compared to Ruby’s ActiveRecord, but I find that it feels less “magical” and is more understandable once I get the basics. And for things that it doesn’t support yet (such as subqueries) it’s simple enough to use raw SQL.

                      2. 1

                        I looked on rom-rb tutorial and I have hard time to understand when the data is saved on the database

                        1. 1

                          Ah, this tutorial is too old and it’s showing rails too so not the best place to learn about how rom works. You can check out command guide instead to learn how rom saves data in the db.