From Dependency injection to dependency rejection - Mark Seemann

Поділитися
Вставка
  • Опубліковано 10 гру 2024

КОМЕНТАРІ • 58

  • @paulfrischknecht3999
    @paulfrischknecht3999 3 роки тому +13

    Good presentation. The flip however was absolutely the sort of cleverness that you don't want to introduce. Don't try to be clever if you don't have to. A problem anyways with all applicative functional languages is exactly this, that they put a very heavy emphasis on the order of arguments. In most cases, the order of arguments does not convey meaning...

    • @GulshanurRahman
      @GulshanurRahman 2 роки тому

      Although subjective, I think, lighter/cheaper or more readily available arguments should go first in the order.

    • @KyleLanmon
      @KyleLanmon 4 місяці тому

      @@GulshanurRahmanit's interesting because my inclination is the opposite. The more essential a parameter is, the closer to the front of the argument list it goes

  • @FastFSharp
    @FastFSharp 6 років тому +7

    Great talk. He takes a simple scenario and shows how to do it in a more robust and testable way. I really appreciated it because I made the exact mistakes he illustrated just last week in some F# code. I'm going to go refactor it now! I think where this would really shine is in a larger project.

  • @Gabriel-mf7wh
    @Gabriel-mf7wh 4 роки тому +11

    OOP is totally insane, I never understood it. Functional is much cleaner

    • @mateusoliveira2776
      @mateusoliveira2776 3 роки тому +5

      Every time you want to learn a new language in OOP you have to understand what they mean by sealed, open, closed, static, object, like... Jesus Christ, I just need my functions and lambdas lmao

  • @iamkaransethi
    @iamkaransethi 11 місяців тому

    Its a marvellous presentation, reason: it is hard to make a complex topic look so simple and understandable. Great work, Mark!

  • @bocckoka
    @bocckoka 6 років тому +5

    all functions in Haskell are pure.

  • @ivanschuetz9458
    @ivanschuetz9458 5 років тому +14

    The talk is good but I find it a bit misleading. The last refactoring could have been done in C# as well, with somewhat uglier syntax but the structure would be the same. Now the logic was extracted to a pure function, which is great, but we still have to perform the side effects in the consumer of the function, where we have the same problems again - how do the dependencies arrive there? How would you wire this if it's called by a web framework? These are the questions we wonder about when using FP in "real world" projects, which unfortunately were not answered here.
    Basically:
    1) Bottom line of the talk: Move as much logic as possible to pure functions (makes sense using a functional or OO language).
    2) It was interesting, but not strictly necessary to involve functional languages. The points could have been made without.
    3) With this level of understanding, one could argue that all we need is OO with "helpers" containing static methods.

    • @swarupmahapatra1
      @swarupmahapatra1 Рік тому

      The speaker has basically described the domain layer of DDD. Domain Layer is IO unaware and very easy to test.

  • @samfischi
    @samfischi 6 років тому +4

    A great talk! Of course, there are several necessary simplifications to make it easier to understand, but that doesn't take away.
    A very minor nitpick: He mentions "non-strict" several times when in fact he means purely functional. Strictness has nothing to do with how he rejects the dependencies of his functions.

  • @dmytrooleinichenko9865
    @dmytrooleinichenko9865 Рік тому +2

    If omit all stuff related to pure/impure functions which is ok, we come to "static" DB call. Which is a tight coupling and it is a reason why using DI to avoid it. We could use static calls in the very beginning code with C# controller without FP approach. I am a little bit confused with such answer.

    • @user-dc9zo7ek5j
      @user-dc9zo7ek5j Рік тому +1

      DB.DoSomething is used because functions are the norm in fsharp, in OOP you would still have a class with dependencies. The topic was not about whether or not you can use static calls. The presented logic can be done in any language. The idea is, that you use concrete implementations where you need them, in C# and JAVA we're used to declaring dependencies inside the classes and have the logic mixed with the depencies, which produce side effects. This makes each unit harder to test, because you have to instantiate the class in order to test it, but you must also mock all the dependencies, and for each test, you must know which dependency to seed with data in order to reproduce the case you're trying to test. The solution that he presented is just to have 2 functions: 1 with concrete implementation, 1 with dependencies which glues the implementation with database/filesystem/responses. You can test both separately, but you must understand that what you want to do is in function 1, and what you need to do in order to compile is in function 2. The example he gave is pretty clear, we do not expect in "ReserveTable" to perform a retrieval of list of reservations, nor to see if any waiter is available, we just wanted the reservation to be created. By breaking all of the requirements into pure functions, GetIsTableReservedForToday, GetIsWaiterAvailable, CreateReservation..., the only variable that's left is the DB, which we don't need to test anyways, because we can test each one of those functions independently, and they're much simpler to reason, to the point where we don't even need to test them.
      In short, structure your code in such a way, that the "business" logic is pure, the way you feed the logic can be impure but don't mix them. This will result in clarity and reduced logical errors.

  • @paulfrischknecht3999
    @paulfrischknecht3999 3 роки тому +3

    With the functional refactoring, the implementation absolutely leaks... If it turns out you don't need the whole reservation list, you can throw away your argument list...

    •  2 роки тому

      F# has sequences, which are lazily evaluated lists (pretty similar to IEnumerable).

  • @antonlatukha5845
    @antonlatukha5845 Рік тому

    BTW the Haskell example in the end can be simplified way further. Usage of transformers (T) and liftIO, code feels like someone tries to write in imperative in purely functional Haskell, for that `do` notation is used in the language. If to show the end result of functional approach, it should be cleaner representation of the language.
    Generally Haskell clean code would be:
    Do everything in pure functions, and find a name for that whole, aka `procReservations`.
    And then 2 IO functions are needed:
    1. Some function based on `fmap` (traverse, sequence, foldr)
    2. Some function that outputs result into IO (printStrLn, writeToDB, etc).
    So the main function would be:
    ```
    main :: IO ()
    main =
    writeToDB =

  • @PaulSebastianM
    @PaulSebastianM 2 роки тому +2

    The more I watch his talks, the more I feel the urge to pick up F# or Haskell.

    • @40oz82
      @40oz82 Рік тому

      Have you?

    • @PaulSebastianM
      @PaulSebastianM Рік тому

      @@40oz82 playing around with F# and really loving the ideas behind it and the teachings of Scott Wlaschin on his fsharpforfunandprofit site. Can't belive more LOB aren't written in it. Especially love the idea of making error states unrepresentable!

    • @Brian-ro7st
      @Brian-ro7st 8 місяців тому

      Clojure

  • @pavelagarkov2634
    @pavelagarkov2634 7 років тому +8

    The interesting part is also how this final pure functional solution would look like in C#...

    • @slowpnir
      @slowpnir 6 років тому +2

      Liek a piece of you know what.

    • @ArchiFloyd
      @ArchiFloyd 5 років тому

      You'll find a high level view of that in this: ua-cam.com/video/F9bznonKc64/v-deo.html

  • @odytrice
    @odytrice 7 років тому +3

    Replace "Not Functional" with "Not Purely Functional". Other than that, it's a great talk. The pure functional implementation is simpler. I'm all for simplicity

  • @leonid4416
    @leonid4416 9 місяців тому +1

    So, in the end we basically removed crucial business rule "Sum of quantities of all reservations shouldn't exceed the capacity SIMULTANEOUSLY". This rule was clear in the original function, because we've clearly read reservations with the date of a new reservation and sum only their quantities.
    In the "refactored" function there is no explanation of what the hell "reservations" are. Maybe it's all reservations in the world, maybe reservations from a neighbor restaurant since 1970, maybe just random reservations. No one knows because now it's not in the domain/business code.
    To know this business rule you need to go to the bootstrap garbage code (...Composition) where it doesn't belong. This is a design smell

  • @RealDieselMeister
    @RealDieselMeister 7 років тому +6

    Sorry, but in a Domain Driven Design in a object oriented language, you move the "Impurities" alos to the out sind. Your Domain Model ist perfecly testable. The Database, the UI are moved to the outside of it.

    • @DenisG631
      @DenisG631 5 років тому

      So you wanna say that his OOP example is not done according to DDD rules?
      Wouldn't you just inject the DB or not DB related code as a repository? This way it is still abstracted

    • @raphaelschmitz4416
      @raphaelschmitz4416 3 роки тому +1

      As far as I understand it, the difference is that in OOP, you have to inject mocks, while in FP you already have only the part you want to test.

    • @RealDieselMeister
      @RealDieselMeister 3 роки тому +4

      @@raphaelschmitz4416 Hi. The Time I wrote my comment (3 years ago), I wasn't aware of FP and it'S principals at all. Now that I am in F# I understand this video and content much more. ;) I know what a monad is. And I love FP so much ...

    • @swarupmahapatra1
      @swarupmahapatra1 Рік тому +1

      The speaker basically extracted the Domain layer equivalent of DDD. Nothing big deal

  • @mahoneyj2
    @mahoneyj2 Рік тому

    Great talk, really great examples and easy to follow

  • @kovolexiy
    @kovolexiy Рік тому

    What tool can be used to create such a presentation?

  • @thatcat3132
    @thatcat3132 6 років тому +2

    a little bit weird talk to be honest. dependency injection is a partial application, but it is not functional because his types do not align? A notion of dependency rejection is nothing more than just a composition of effectful functions. Just curious what would be the name for Kleisli's or tagless final...

    • @АнимусАнанимус
      @АнимусАнанимус 4 роки тому +1

      Injection of impure dependencies as i see can't be used in haskell, only in f#. So if it would be real a haskell with it type strictness, i think it would be functional.
      But i dont see any way to do it in haskell (fix me if i wrong and show me how), because you cant apply something (a -> IO b) to function (a -> (a -> b) -> b) what is a pure function.
      I really tried to do this, but i didnt find any allowed monadic functions (bind, map, apply, pure, etc) combinations to get a type (a -> (a -> b) -> b) -> (a -> (a -> IO b) -> IO b)

  • @GeorgeTsiros
    @GeorgeTsiros 3 роки тому +1

    8:25 there shouldn't be a need for a 'validator'. If the validation fails, it means the object is invalid. Which means it shouldn't exist, it shouldn't even have been created. Which means its _constructor_ , or whoever _constructed_ it, should have thrown an exception. What is shown here is _bad_ .

  • @nescafezos4265
    @nescafezos4265 2 роки тому +2

    I wish F# had syntax more like that:
    let tryAccept = (
    capacity: int,
    readReservations: (DateTimeOffset) -> List,
    createReservation: (Reservation) -> int,
    reservation: Reservation
    ): ?int ->
    {
    let reservedSeats =
    readReservations(reservation.Date) |> List.sumBy(x -> x.Quantity);
    if (reservedSeats + reservation.Quantity r.isAccepted = true)); // not sure about "With" though
    }
    return null;
    }

  • @victornoagbodji
    @victornoagbodji 7 років тому +1

    very interesting talk. thanks!

  • @alexandrekfoury9289
    @alexandrekfoury9289 Рік тому

    Great blog

  • @KarenTazayan
    @KarenTazayan 5 років тому

    Great talk!

  • @malvoliosf
    @malvoliosf 3 роки тому +4

    I gotta say: I disagree with almost everything in this video.
    First, if passing a potentially impure function into an otherwise-pure function makes the latter function impure, then map() is impure, filter() is impure.
    Second, what did the purification process outlined accomplish? I have moved largely trivial business logical into a pure function, but the code that I actually need, the code that gets and sets the data, is not only impure, but is strongly coupled to its dependencies.

    •  2 роки тому +1

      Why? You are not forced to pass an impure function to map() and filter().
      What you achieve with composition is getting all the dependencies, AKA impure code, together. You have to think of the composition function in the video (which is a simple example) as the equivalent of the dependency resolution in OO (the place where you couple your program with the all the specific dependencies you want, usually by using a DI framework to create a service collection).

  • @mateberes
    @mateberes 7 років тому

    brilliant, thank you!

  • @jmazin1001
    @jmazin1001 7 років тому

    thx for a great talk!

  • @driziiD
    @driziiD 4 роки тому +2

    took 40 minutes to explain "Dependency Inversion" and "Functional Core Imperative Shell". watch these instead :
    www.destroyallsoftware.com/talks/boundaries
    www.destroyallsoftware.com/screencasts/catalog/functional-core-imperative-shell

  • @kenichimori8533
    @kenichimori8533 7 років тому

    Equal Difference.

  • @kenichimori8533
    @kenichimori8533 7 років тому +1

    Compherence.

  • @ComprehendRealityNow
    @ComprehendRealityNow 7 років тому +29

    interesting? brilliant? great talk? You guys are crazy, he hasn't done anything except move the impure dependency injection up a layer. absolutely nothing has changed after 59 minutes of this. The new impure injection is the two DB function calls. He doesnt even mention how they get accessed. Obviously because it would show you the entire last hour of your life was wasted. There were two impure injections at the start of the example and two impure injections at the end of the example. Moving them one function to the other didn't change anything.
    What's worse is he's leaked the implementation details out a layer, the caller must now know we're using a DB.

    • @odytrice
      @odytrice 7 років тому +35

      Nope the caller now decides where to get the data. The DB is an implementation detail. I could come from a file or whatever IO device you want. Like he said, the goal is to move the impurities to the edge of your system. Impurities are what make testing difficult because you'd have to mock/stub them. So yes, It's a brilliant talk

    • @arnoldhau1
      @arnoldhau1 7 років тому +5

      So your dislike that there where no wonders performed. Oh surprise.
      And else its very well explained and to push the dependencies to the boundaries is quite a logical solution if you want to use functional programming, quite to the opposite of how you do it in OO. Both are valid, depends on what you want.
      So what would you do instead?

    • @AlexFeature
      @AlexFeature 7 років тому

      Agreed. Total garbage.

    • @odytrice
      @odytrice 7 років тому +7

      +Alexander Pavlovsky Listen to 54:50 again The idea is to isolate as much impure operations as possible from the Pure parts. This is really useful because the pure parts are easy to test without needing mocking/stubbing and the internal logic becomes simple and easy to reason about. That's how its done in Haskell

    • @ArwinvanArum
      @ArwinvanArum 7 років тому +1

      I agree that the talk is useful. Dependency injection still allows you to do things that you shouldn't want to do. I've seen some really weird stuff that it was being abused for, and refactored a fair bit of code in my time that brings the DI to the top level class only and passes on relevant bits from there to the proper classes, much like the io - pure - io pattern here. It's still not entirely clear to me how to cleanly do stuff like inject a centrally chosen implementation of ILogger properly into a large number of classes. Perhaps it's just a matter of writing your own local/static class that is used at the top level of a main/factory class to pass on the right interface implementations to the modules?

  • @expatca4444
    @expatca4444 3 роки тому +1

    di sucks

  • @slowpnir
    @slowpnir 6 років тому +1

    54:40 GOD WHY
    Too bad we can't disallow him writing code in Haskell. Why can't you hide `lift`s? Is the `do`-syntax disallowed by your religion? Are you writing a piece of maintainable code or participating in obfuscation contest? And... `MaybeT`? Seriously? Good luck compiling package exporting this, lad, if your dependencies aren't covered in dust.
    He should pull out some free monads instead, this talk would be at least interesting in that case.

  • @SodAlmighty
    @SodAlmighty 2 роки тому +1

    This guy has an even more condescending voice than I do, and that's saying something. Also, he talks too fast, and on a monotone. He's hard to follow.