Handling Side Effects In Functional Code With Railway-Oriented Programming

แชร์
ฝัง
  • เผยแพร่เมื่อ 20 ก.ย. 2024

ความคิดเห็น • 85

  • @MilanJovanovicTech
    @MilanJovanovicTech  ปีที่แล้ว +1

    Want to master Clean Architecture? Go here: bit.ly/3PupkOJ
    Want to unlock Modular Monoliths? Go here: bit.ly/3SXlzSt

  • @davideastman5806
    @davideastman5806 ปีที่แล้ว +5

    I like it. Reminds me of writing Rx.js on our Angular front end. And I appreciate you making videos about these kinds of new/unorthodox approaches because if nothing else it stretches out the ol' brain a bit and that's good for the community.

  • @Wordsalad69420
    @Wordsalad69420 ปีที่แล้ว +5

    Assuming this is above C# 7, you can name your tuple components so it’s more specific than Item1 and Item2.

    • @MilanJovanovicTech
      @MilanJovanovicTech  ปีที่แล้ว

      How would you name them in a generic context?

    • @CodeBallast
      @CodeBallast ปีที่แล้ว

      ​@@MilanJovanovicTechThis should work: . Haven't tested it...

    • @MilanJovanovicTech
      @MilanJovanovicTech  ปีที่แล้ว

      @@CodeBallast It does... But try it with a generic method

    • @Wordsalad69420
      @Wordsalad69420 ปีที่แล้ว

      @@MilanJovanovicTech This works:
      Result Function(Result arg1, Result arg2) => (arg1.Value, arg2.Value);
      var tuple = Function(Result.Success(5), Result.Success(6));
      Console.WriteLine(tuple.Value.Arg1);

  • @stefan-d.grigorescu
    @stefan-d.grigorescu ปีที่แล้ว +5

    Personally, I like using a combination of imperative and functional programming, as the language offers us.
    For example, when some functional chain gets more complex, I prefer breaking it into partial results with suggestive variable names. It offers a better experience at both debugging and reading the code.

  • @winchester2581
    @winchester2581 ปีที่แล้ว +2

    I'm in love with this Result implementation. it's so easy and has a possibility to be extended without refactoring, great! As I started learning Rust on the background, I totally understood that results with errors is a great feature and idea itself

    • @MilanJovanovicTech
      @MilanJovanovicTech  ปีที่แล้ว +4

      There you go, someone with an open mind who gets it, and sees the benefits. Thank you! 😁

    • @angelldark6426
      @angelldark6426 ปีที่แล้ว

      Easy ?? Who are you ?? God or devil???
      How are you understood?
      I'm copying this clas and they didn't work...

  • @nayanchoudhary4353
    @nayanchoudhary4353 ปีที่แล้ว +8

    Yes, you are correct. I'm definitely going to say that this is harder to read and the cost is paid in terms of educating the new team members with more time spent on bind and tap type extensions. Less lines of code? Not less enough for the extra load on cognitive ability imposed.

    • @Wordsalad69420
      @Wordsalad69420 ปีที่แล้ว +3

      Reads pretty easily to me. You just have to know what Tap, Bind and Map are. This code is much cleaner than the original code.

    • @MilanJovanovicTech
      @MilanJovanovicTech  ปีที่แล้ว +3

      The cost of educating the team is probably a ~30-45 min. intro on the basics, and everyone can start being productive. All this fuss about not readable and more cognitive load is coming from people that never even tried FP or ROP, so how can the argument be valid?

    • @nayanchoudhary4353
      @nayanchoudhary4353 ปีที่แล้ว

      @@MilanJovanovicTech In real world, the least complex solution wins. We need to understand the cost and benefits trade off. Also, kindly don't assume that everyone gets on board with such custom frameworks. It's not just training, but the real win is in adoption. It's definitely not a capability question.

    • @baranacikgoz
      @baranacikgoz ปีที่แล้ว

      A standard iq'ed developer should understand this in 30minutes

    • @Wordsalad69420
      @Wordsalad69420 ปีที่แล้ว

      @@nayanchoudhary4353 Really not that hard dude. Maybe/Option and Result are literally part of several functional programming languages.If you're written Typescript you know all about map and tap.

  • @nove1398
    @nove1398 ปีที่แล้ว +1

    This definitely had a learning curve but so does every style. I can see how powerful this style of coding can be. I do not believe reading this would be more difficult than reading a Linq statement

    • @MilanJovanovicTech
      @MilanJovanovicTech  ปีที่แล้ว +2

      Precisely, but most people won't even give it a chance 😅

  • @kristianaranda
    @kristianaranda ปีที่แล้ว +2

    Hi Milan!
    I'm a fan of your videos and I think we follow the same people in the programming world because I agree with you in many of the patterns you present in your videos. I also use "Result", "Bind", "Match", etc,... in the application layer and in the domain layer.
    But I would like to point out that I only use "Result" to indicate an error in a user input instead of throwing exceptions. To me, exceptions should be just that "Exceptions". That is, although I like to use "Result" objects for specific circumstances, just as exceptions are only used for real exception cases, to return an object from the repository, I do not use "Result", since here we are not validating a user input. A repository (provider or wahtever) returns an object or not, but it is not a "Result". In this case I use the "Option" or "Maybe" paradigm, also from functional programming. Also with a "Maybe" object we can still use "Railway Programming" and combine it with Results.
    Also, I have seen first hand in my teams that abuse of Result can lead to wrong implementations of the pattern which can lead to painful refactorings. Therefore, I use it very cautiously and only where it really applies, in my opinion of course.
    thanks for your videos!

    • @MilanJovanovicTech
      @MilanJovanovicTech  ปีที่แล้ว

      How is Maybe different from a Result when returning from a repository?
      - Maybe will have a `HasValue` flag
      - Result will have a `IsSuccess` flag
      The only thing different is the intent - so I only partially agree with your point

    • @kristianaranda
      @kristianaranda ปีที่แล้ว

      @@MilanJovanovicTech Well, I believe that intention is precisely the key. Use each thing precisely for what it was designed to avoid misunderstandings. The Maybe implementation I use, has no hasValue flag or similar, a Maybe can be Some or None, simply that. At the same time we avoid that the client is forced to check if the returned object is null or not (it is possible that the client forgets to do that check, we all know the errors that have occurred in the history of nulllable objects....😅). In my opinion, the code is more declarative and easy to reason about, just what functional programming promotes.

  • @efimov90
    @efimov90 ปีที่แล้ว +1

    14:20 absolutly agree about worse readability, but not because of chaining, but because not realized potential. Can be improved significantly by providing more explicit naming and maybe more extensions methods and by realization of async potential by making all calls async and using Cancelation token.

    • @MilanJovanovicTech
      @MilanJovanovicTech  ปีที่แล้ว +1

      This is what they're called in FP, which is why only OOP people are complaining on this video 😅

  • @chrismoutray9206
    @chrismoutray9206 ปีที่แล้ว +1

    Carrying values forward is a real pain, in your example it was handy that you were able to extend the entity to have the gathering and member. But it doesn't always make sense to do this.

  • @kodindoyannick5328
    @kodindoyannick5328 หลายเดือนก่อน

    Great content! Thanks Milan!

  • @dionismendanha4649
    @dionismendanha4649 ปีที่แล้ว +2

    can you make a video doing a unit test for this function?

  • @kostasgkoutis8534
    @kostasgkoutis8534 ปีที่แล้ว +2

    Impressive, but very condensed. I would only use .Map() and .Bind() for a Result type. Not because the other methods don't work, it just ends up being more unreadable (in C#). Let semicolon and whitespace do their thing.

  • @andreibicu5592
    @andreibicu5592 7 หลายเดือนก่อน

    Hi Milan,
    I like your video as I myself use this functional approach on my current project. All is custom code, no external library.
    I started with the Result class and ended up having many extensions. I tried to use as few namings as possible, to avoid confusion like: Match, OnSuccess, OnFailure. This is all. I saw libraries that use the extensions you defined Tap, Bind, but I really don't understand why do we have to come up with different namings for something that should be simpler. Tap and Bind could be called OnSuccess instead.
    Am I missing something?
    Also, I'd love to see a video with the differences between the Result and Maybe/Option types. More exactly when to use one over the other.
    In my opinion if you could return an error then use a Result, otherwise an Option. For instance what if you're trying to get an inexistent user from the db? Is that a Result or an Option? You could expect not to find the user, but we could also encounter another issues (connection). Probably a result is still the right choice.
    There is no clear boundary between them and everyone online has different opinions. Looking forward for yours!
    Thx!

    • @MilanJovanovicTech
      @MilanJovanovicTech  7 หลายเดือนก่อน

      You can choose the naming convention you like. OnSuccess is to generic for me. I prefer the RXJS convention, which uses `tap`, for example.
      As for returning Maybe/Option - I've found that nullable reference types work just as fine for this use case.

  • @microtech2448
    @microtech2448 ปีที่แล้ว +1

    So your Result class gonna be full fledged library one day 😅

    • @MilanJovanovicTech
      @MilanJovanovicTech  ปีที่แล้ว +2

      Nah, don't have such aspirations

    • @microtech2448
      @microtech2448 ปีที่แล้ว

      @@MilanJovanovicTech Do you have source code which you demo in video on a github repo, if yes, can you please share link?

  • @birukayalew3862
    @birukayalew3862 ปีที่แล้ว +1

    Yes, it's concise. However, it's difficult to read and learn, especially for junior developers. How is the debugging issue?

    • @MilanJovanovicTech
      @MilanJovanovicTech  ปีที่แล้ว

      Assume I give you a 30min crash course of the basics. What's confusing with ROP style of code?

    • @birukayalew3862
      @birukayalew3862 ปีที่แล้ว

      @@MilanJovanovicTech wow the sound is great. Thanks a lot.

  • @Tamer_Ali
    @Tamer_Ali ปีที่แล้ว +1

    Thanks Milan, looking for more of functional code using C#.
    I have a question, How to apply transaction to this code?

    • @MilanJovanovicTech
      @MilanJovanovicTech  ปีที่แล้ว

      Could be another call to Tap where you open a transaction
      Or create a method that runs in a transaction

    • @Tamer_Ali
      @Tamer_Ali ปีที่แล้ว

      @@MilanJovanovicTech you are going to create another video for that?

    • @MilanJovanovicTech
      @MilanJovanovicTech  ปีที่แล้ว +2

      @@Tamer_Ali Not any time soon, need to finish other projects. But at some point I'll circle back to ROP and do a "from scratch" video to try to explain the benefits better. People are so confused and can't see the value in it.

  • @tothue
    @tothue ปีที่แล้ว +2

    I really like the Result abstraction, and the operators helps getting rid of a lot of unwrapping.
    One problem I often counter, is accessing some of the previous results in the chain (here solved by adding a property to the invitation). With linq support you can solve this - something like:
    await (
    from gathering in Result.Create(await _gatheringRepository.GetByIdWithCreatorAsync(request.GatheringId, cancellationToken))
    from member in Result.Create(await _memberRepository.GetByIdAsync(request.MemberId, cancellationToken))
    from invitation in gathering.SendInvitation(member)
    from _ in Result.Create(async () => {
    _invitationRepository.Add(invitation);
    await _unitOfWork.SaveChangesAsync(cancellationToken)
    _emailService.SendInvitationSentEmailAsync(
    member,
    gathering,
    cancellationToken
    )
    })
    select Unit.Value
    )
    To me this is more readable, but it often confuses OOP programmers (which I completely understands!). What are you thoughts on this?

    • @MilanJovanovicTech
      @MilanJovanovicTech  ปีที่แล้ว

      Quite a bit verbose, but I agree that it's definitely a problem when you want to reuse a previous value. I'll try to research a little if there's a better way to achieve this.

    • @tothue
      @tothue ปีที่แล้ว

      I don't think it is more verbose than using callbacks - I mean
      from x in result
      ...
      vs
      result.Map(x => ...)
      I would use the same approach for ienumerable and rx. The mental translation between linq and result operators can be a bit tricky though - but I guess that is some of hard parts when introducing Monads in C# (and in your team). You could probably use Where for side effect and some boolean operators (+ or &). I feel it could become a great abstraction, but it might be too big a change for many programmers.
      Another thing - what about stronly typed errors? If you repositories returns Result, and SendInvitation returns Result - how would you handle that? I would like to ensure the caller are forced to handle these and only these (not say MemberAlreadyExists). This is where I usually lay down on the floor, cry a bit and fall back to throwing exceptions...

  • @efimov90
    @efimov90 ปีที่แล้ว +1

    Isn't it cause problems in MVVM and MAUI or WPF? I mean interface named ICommand?

  • @angelldark6426
    @angelldark6426 ปีที่แล้ว +1

    How are you doing this 😮, omg, I'm want to do this, how time much are you learning.

    • @MilanJovanovicTech
      @MilanJovanovicTech  ปีที่แล้ว +1

      I spent more time than I'd like to admit tinkering with this approach 😅

  • @shadowsir
    @shadowsir ปีที่แล้ว +3

    Unfortunately, this looks and feels rather clunky in an OO-first language 😕 This works so much better with computation expressions in F#. Same problem I have with Sum types. There's the OneOf library to try to get them in C#, but it's extremely clunky to work with compared to FP languages.
    Even though trying to hack functional concepts in C# seens like a good idea in theory, it becomes very hard to read (and thus, maintain) in C# in practice.

    • @MilanJovanovicTech
      @MilanJovanovicTech  ปีที่แล้ว

      I think the main stepping stone will be your team's willingness to embrace FP, and not so much C#'s OO nature

  • @efimov90
    @efimov90 ปีที่แล้ว +1

    10:20 why don't you add Cancellation token to Func as Generic parameter?

    • @MilanJovanovicTech
      @MilanJovanovicTech  ปีที่แล้ว

      Sure, you can do that

    • @kirillhorn3186
      @kirillhorn3186 ปีที่แล้ว

      For what reason? Some asynchronous functions could be without cancellation token.

    • @efimov90
      @efimov90 ปีที่แล้ว

      @@kirillhorn3186 for reason to get it where it needed instead of having troubles with rewriting all chain later.

  • @IcaroFelix2023
    @IcaroFelix2023 4 หลายเดือนก่อน

    Is this result extensions based in some kind of library like CSharp Functional Extensions ?

  • @10199able
    @10199able ปีที่แล้ว +2

    CSharpFunctionalExtensions

  • @mohammadhosseinbasiri494
    @mohammadhosseinbasiri494 5 หลายเดือนก่อน

    How can we handle bulk operations fx a list of Dto coming from endpoint then we need to validate and insert in one transaction?

  • @yohm31
    @yohm31 ปีที่แล้ว +1

    No more exceptions, yeepee 🎉

    • @MilanJovanovicTech
      @MilanJovanovicTech  ปีที่แล้ว +1

      They can still happen, but you can introduce a TryCatch function to solve it

  • @techpc5453
    @techpc5453 ปีที่แล้ว +1

  • @angelldark6426
    @angelldark6426 ปีที่แล้ว +1

    I have are question. This function public static Result Success(TValue value) =>
    new Result(true, Error.None); return value operations ?.
    Forecsample
    public Result Bar1()
    {
    int res = 2+2;
    return Result.Success(res);
    }

    • @MilanJovanovicTech
      @MilanJovanovicTech  ปีที่แล้ว +1

      I'm not sure what the question is?

    • @angelldark6426
      @angelldark6426 ปีที่แล้ว

      @@MilanJovanovicTech at 4.14 minutes you were showing the Result class.
      it has a function
      public static Result Success(TValue value) => new(value,true, Error.none);
      can this function return a result?
      Forecsample
      public Result Bar1()
      {
      int res = 2+2;
      return Result.Success(res);
      }

    • @MilanJovanovicTech
      @MilanJovanovicTech  ปีที่แล้ว

      @@angelldark6426 Yes, but it should be a Result return type

  • @michaldivismusic
    @michaldivismusic ปีที่แล้ว +2

    Shorter, but less readable IMO.

    • @MilanJovanovicTech
      @MilanJovanovicTech  ปีที่แล้ว

      Have you done FP before?

    • @michaldivismusic
      @michaldivismusic ปีที่แล้ว

      @@MilanJovanovicTech I dabbled a bit, I can definitely agree that doing it a lot makes it easier to understand, that's part of the reason I don't find it very readable. But even after doing some FP, the biggest problem I have with it is I find it hard to tell where a method call starts and ends. There are too many levels of indentation and brackets for my taste.

  • @bundlesofun9568
    @bundlesofun9568 8 หลายเดือนก่อน

    It's pronounced "procedural"

    • @MilanJovanovicTech
      @MilanJovanovicTech  8 หลายเดือนก่อน

      What is?

    • @bundlesofun9568
      @bundlesofun9568 8 หลายเดือนก่อน

      @@MilanJovanovicTech lol just a tease friend. All code is "sooner or later" compiled into procedural code at the assembly and machine code levels. I've always personally felt like "rail-road" simply emulates the "true nature" of the cpu architecture.

  • @andrzejherman7597
    @andrzejherman7597 ปีที่แล้ว +2

    This becomes a terrible abstraction. I have to write 3-4 time more code to get simple functionality (eg. get some data from DB in API) only to fullfill "clean code". DISASTER !!!! Are we serious going to this direction ? I'm a dotnet developer with 15 years of experience. I've written a lot
    of production code, only with controllers, and services. All works perfect all the time. I see here more "theory" than practical code. It's my opinion. In Polish we say: "Przerost formy nad treścią", it can be translated like: "More unusefull form, less good content" :)

    • @MilanJovanovicTech
      @MilanJovanovicTech  ปีที่แล้ว +4

      Did you do Functional Programming in those 15 years?