Handling Side Effects In Functional Code With Railway-Oriented Programming

  Рет қаралды 7,775

Milan Jovanović

Milan Jovanović

Күн бұрын

Пікірлер: 85
@MilanJovanovicTech
@MilanJovanovicTech Жыл бұрын
Want to master Clean Architecture? Go here: bit.ly/3PupkOJ Want to unlock Modular Monoliths? Go here: bit.ly/3SXlzSt
@davideastman5806
@davideastman5806 Жыл бұрын
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.
@MilanJovanovicTech
@MilanJovanovicTech Жыл бұрын
Exactly, quite similar to Rx.js!
@Wordsalad69420
@Wordsalad69420 Жыл бұрын
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);
@winchester2581
@winchester2581 Жыл бұрын
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 Жыл бұрын
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...
@nove1398
@nove1398 Жыл бұрын
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 Жыл бұрын
Precisely, but most people won't even give it a chance 😅
@nayanc4353
@nayanc4353 Жыл бұрын
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 Жыл бұрын
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 Жыл бұрын
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?
@nayanc4353
@nayanc4353 Жыл бұрын
@@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 Жыл бұрын
@@nayanc4353 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.
@stefan-d.grigorescu
@stefan-d.grigorescu Жыл бұрын
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.
@MilanJovanovicTech
@MilanJovanovicTech Жыл бұрын
Get the best of both worlds!
@chrismoutray9206
@chrismoutray9206 Жыл бұрын
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.
@MilanJovanovicTech
@MilanJovanovicTech Жыл бұрын
Agreed, this a short-coming of FP
@kodindoyannick5328
@kodindoyannick5328 5 ай бұрын
Great content! Thanks Milan!
@MilanJovanovicTech
@MilanJovanovicTech 5 ай бұрын
My pleasure!
@kristianaranda
@kristianaranda Жыл бұрын
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.
@dionismendanha4649
@dionismendanha4649 Жыл бұрын
can you make a video doing a unit test for this function?
@MilanJovanovicTech
@MilanJovanovicTech Жыл бұрын
Yes!
@efimov90
@efimov90 Жыл бұрын
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 Жыл бұрын
This is what they're called in FP, which is why only OOP people are complaining on this video 😅
@efimov90
@efimov90 Жыл бұрын
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.
@tothue
@tothue Жыл бұрын
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...
@IcaroFelix2023
@IcaroFelix2023 8 ай бұрын
Is this result extensions based in some kind of library like CSharp Functional Extensions ?
@MilanJovanovicTech
@MilanJovanovicTech 8 ай бұрын
Kinda
@mohammadhosseinbasiri494
@mohammadhosseinbasiri494 9 ай бұрын
How can we handle bulk operations fx a list of Dto coming from endpoint then we need to validate and insert in one transaction?
@MilanJovanovicTech
@MilanJovanovicTech 9 ай бұрын
Might make another video to cover that
@mohammadhosseinbasiri494
@mohammadhosseinbasiri494 9 ай бұрын
@@MilanJovanovicTech would be great
@andreibicu5592
@andreibicu5592 11 ай бұрын
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 11 ай бұрын
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.
@Tamer_Ali
@Tamer_Ali Жыл бұрын
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 Жыл бұрын
@@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.
@efimov90
@efimov90 Жыл бұрын
Isn't it cause problems in MVVM and MAUI or WPF? I mean interface named ICommand?
@MilanJovanovicTech
@MilanJovanovicTech Жыл бұрын
Different namespace
@birukayalew3862
@birukayalew3862 Жыл бұрын
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.
@angelldark6426
@angelldark6426 Жыл бұрын
How are you doing this 😮, omg, I'm want to do this, how time much are you learning.
@MilanJovanovicTech
@MilanJovanovicTech Жыл бұрын
I spent more time than I'd like to admit tinkering with this approach 😅
@microtech2448
@microtech2448 Жыл бұрын
So your Result class gonna be full fledged library one day 😅
@MilanJovanovicTech
@MilanJovanovicTech Жыл бұрын
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?
@yohm31
@yohm31 Жыл бұрын
No more exceptions, yeepee 🎉
@MilanJovanovicTech
@MilanJovanovicTech Жыл бұрын
They can still happen, but you can introduce a TryCatch function to solve it
@kostasgkoutis8534
@kostasgkoutis8534 Жыл бұрын
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.
@MilanJovanovicTech
@MilanJovanovicTech Жыл бұрын
The intent of the other methods is the point
@techpc5453
@techpc5453 Жыл бұрын
@MilanJovanovicTech
@MilanJovanovicTech Жыл бұрын
👋👋
@10199able
@10199able Жыл бұрын
CSharpFunctionalExtensions
@MilanJovanovicTech
@MilanJovanovicTech Жыл бұрын
I should check out that library
@shadowsir
@shadowsir Жыл бұрын
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
@angelldark6426
@angelldark6426 Жыл бұрын
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 Жыл бұрын
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 Жыл бұрын
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.
@andrzejherman7597
@andrzejherman7597 Жыл бұрын
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 Жыл бұрын
Did you do Functional Programming in those 15 years?
@bundlesofun9568
@bundlesofun9568 Жыл бұрын
It's pronounced "procedural"
@MilanJovanovicTech
@MilanJovanovicTech Жыл бұрын
What is?
@bundlesofun9568
@bundlesofun9568 Жыл бұрын
@@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.
Completely Get Rid of Exceptions Using This Technique
19:24
Milan Jovanović
Рет қаралды 28 М.
Functional Programming With C# Using Railway-Oriented Programming
19:15
Milan Jovanović
Рет қаралды 26 М.
Гениальное изобретение из обычного стаканчика!
00:31
Лютая физика | Олимпиадная физика
Рет қаралды 4,8 МЛН
coco在求救? #小丑 #天使 #shorts
00:29
好人小丑
Рет қаралды 120 МЛН
Леон киллер и Оля Полякова 😹
00:42
Канал Смеха
Рет қаралды 4,7 МЛН
Get Rid of Exceptions in Your Code With the Result Pattern
13:06
Milan Jovanović
Рет қаралды 58 М.
How Guard Clauses Can Make Your Code Better
13:54
Milan Jovanović
Рет қаралды 13 М.
How I Use The Generic Repository Pattern In Clean Architecture
17:15
Milan Jovanović
Рет қаралды 42 М.
The RIGHT Way To Use HttpClient In .NET
11:46
Milan Jovanović
Рет қаралды 66 М.
8 Tips To Write Clean Code - Refactoring Exercise
16:06
Milan Jovanović
Рет қаралды 37 М.
AI Is Making You An Illiterate Programmer
27:22
ThePrimeTime
Рет қаралды 125 М.
How To Build Loosely Coupled Microservices With MassTransit
23:01
Milan Jovanović
Рет қаралды 41 М.
DEEPSEEK Vs CHATGPT There Is A  Clear Winner !!
15:53
Rick Aqua
Рет қаралды 11 М.
Гениальное изобретение из обычного стаканчика!
00:31
Лютая физика | Олимпиадная физика
Рет қаралды 4,8 МЛН