Check out Dometrain and use code ZORAN for 15% off any course ► dometrain.com/?coupon_code=ZORAN Download source code ► www.patreon.com/zoranhorvat Join Discord server with topics on C# ► codinghelmet.com/go/discord Enroll course *Beginning Object-Oriented Programming with C#* ► codinghelmet.com/go/beginning-oop-with-csharp Subscribe ► th-cam.com/channels/xsWfh8LCcn55mFB6zGBT1g.html
Thanks for the video. Especially the naming for types helped me since I encountered the feeling that when I code in functional way, my functions seem "not in right spot" or "not highlighted enough". Functions are in this case more important to me since they hold logic while types simply hold data. Thank you very much.
@@zoran-horvat What is tagged unions ? I know about discriminated unions in F# and C#, are these concepts "tagged unions" and "discriminated unions" the same ?
@@tore28 It is a union which also contains a "tag" field (usually just a byte would suffice). The tag identifies the type of the content stored in the rest of the object. Tagged unions are used to support type switching on polymorphic types that do not possess a header common in object-oriented classes.
Actively trying to learn pure functional approach by learning Haskell, this video is quite useful for me, I can see an example of this style in familiar language to reach more understanding. Thank you.
Cool video. Functional concepts like bind are nice. IMO things like ErrorOr are the simplest way to implement sane functional programming in C#. Procedural functional programming if you will Async/await is basically a monad 🤷♂️
Great video, I use Static Classes (COUGH: C# version of Module's) all the time. It really help's speed up development. I still use OOP for some things, but I found using static classes and functional programming can clean up a lot plumbing, especially with .NET.
When you work on data with many (100 or more) rules. In parts of web backends not uncommon for example. You can do it with with oop but it is easier to read in functional stile.
Any model where the data model is known and never (or rarely) changes, but behavior grows every day, will likely be a good candidate for functional modeling. Doing the same work in an OO model would require several times longer code and will cause enormous issues. Funny enough, that is a common situation in business operations and connected services today.
Use both Use extension classes to create functionality. Use objects/classes for data. This mental model is easy to understand: data goes into a function, and comes out in a different shape
Love functional programming and Zoran is guru in it. Who would like to dive deeply I recommend listening his Functional Programming lectures on Pluralsight
CreateMany() code in 10:27 seems very strict, I will prefer it like if All are null then return null, else return only the one are not null. so if authors are ["a", "b", null, "c"] => will return ["a", "b", "c"] instead of null.
If you considered the idea of a smart constructor, you would see that it doesn't make much sense to request _all_ contained objects to fail before failing. What you are describing is the Bind function on IEnumerable. It is useful, but in different conditions.
If you had to model different types of xml nodes with different rules and linq XElement is not enough, would you go the functional route? Most nodes types would be known and not change in the future. And what about text inside those nodes? Im thinking about modelling to the smallest bit, like an object Character that has an index, a start column and end column (imagine that you can place the cursor before or after).
I think Functional is orthogonal to your question! If you need a custom type for a node, that's fine. If you want to model the behavior in the class, then go OOP. If you want to model the behavior in extensions, separate from the state - go functional!
6:25 I understand the intention here was to just show an example how to make subtypes, but can't help but be reminded of "Falsehoods Programmers Believe About Names" :)
That is only true as long as they are in the same assembly. If I moved the Models namespace into a separate assembly, which I would certainly do as the model grows large, then all code that references the internal members would fail to build.
While I appreciate how you've gone about it, I find the Book vs BookType confusing. If all Book is doing is providing static creation methods, why does it have to be a separate class (and thus the segue into how to name it), rather than just part of the record class? Is it to avoid the temptation of directly calling new Book()?
The reason is that it will not be all the book does. The next step from here is to start defining behavior, and we do that in the form of functions defined separately from the data type. The greatest benefit from functional programming is that, unlike in OOP, different portions of behavior are defined in different packages.
@@zoran-horvat But it is perfectly fine to declare static functions in the (abstract) super type. Thus, the naming theme "***Type" becomes superfluous.
@@thomasschroter3802 Yes, but that goes against the general idea that those functions can also be defined in another module or namespace. Why make a special cade of some functions, but treat others regularly?
Hi Zoran, this is the first time I have seen you use "record BookType" with a "static class Book" for the constructors. In other videos/example you just put the "static Book Create" inside the record. I also don't see you appending "Type" to the record in another examples. Is there any particular reason? Maybe it's not important, and just a different style of doing the same thing
@@slowjocrow6451 This video is demonstrating strict functional programming applied in C#. Other videos are more practical, and so lift off some of the conventional elements.
Overall I think the state of functional programming in java to be superior to c#. Especially 14:20 java supports closed hierarchies enabling exhaustive type pattern matching. I also think it's overcomplicated to hide the varients and create a match function. The sum type NameType with its varients FullNameType and MononymType can do these things with 1st class c# features (switch expressions) in the user code. Also separating the static code from the type is unnecessary and confusing. Just put the factory functions inside the record. This way you save yourself from the naming dilemma as well.
C# switch expressions are still cumbersome with no exhaustive checking. They must always include a throw instruction, which makes every screen if code look ugly. Once we get that feature, I guess I will stop favoring the Match method.
You can never guarantee that no one will ever extend your abstract class. It can be done in other assemblies. Sometimes even if the type is internal. If you skip the default switch case C# will add it for you. Try and test.
@@rafazieba9982 I am talking about the fact that java has a compile time and runtime mechanism to prevent unallowed inheritance. The syntax is somewhat confusing from a c# perspective as it uses the sealed keyword which means something different in c# and java.
@@rafazieba9982 public sealed class Super permits Sub1, Sub2 {} final class Sub1 extends Super {} final class Sub2 extends Super {} Super foo = new Sub2(); switch (foo) { case Sub1 s1 -> {} case Sub2 s2 -> {} //no default (exhaustive) }
More likely the IEnumerable... Deferred execution (or lazy enumeration) of LINQ + IEnumerable is how monads also work. You declare the HOW without actually performing any operation, yet; only declarations. Once all set, you can unwrap the monad (or enumerate the enumerable), where each operation declared will be now performed.
@@KeenkiDash I don't believe deferred execution is necessary for a monad definition. It's just a nice feature (or footgun) that functional libraries tend to have.
@@Robert-yw5ms A monad requires a factory which elevates a regular value into a monad, and the Bind function, plus a few other rules known as the monad laws.
I am ok with FP where it makes sense. But that Printable example is not such a case. It would make much more sense and avoid refactoring many places if the NameType simply defined a contract which concrete implementations must adhere to. Whenever a developer needs a new NameType, they add a new class, implement the contract, and the rest of the code keeps working without changes. Overall I am learning a lot from your videos but to me this example in particular is where OOP wins against FP - and that's ok, each has their advantages. As always, use the right tool for the job.
Why did you check string name for null but haven't done string? firstName in the function parameterz. Just forgot '?' ? (My ide would say it's always false)
That was just a line to show the exception in action and then to delete it. However, if the factory function was supposed to be called from the outside, then non-nullable references can still be set to null and you would have to guard.
It was not necessary to make NameType abstract. Then the Create method would not need to return null, but instead it would be possible to return the base type, i.e. the same NameType. And in the future, you can use pattern matching instead of fighting the nullable type.
@@zoran-horvat I wanted to answer in the comments on youtube, but I'll try. In your example, Create() -> Nametype? -> CreateMany(NameType?[]) -> NameType[]? -> if(NameType[]? is not null) Book.Create(). I suggest Create() -> Nametype -> CreateMany(NameType[]) in this method using pattern matching we remove the elements with the base type and return the same one -> NameType[] which can have a number of elements equal to zero and therefore -> if(NameType[].Count > 0) Book.Create().
@@zoran-horvat It will just be one of the types I expect, but without the data I need. And it's better than null. Let's say you have a certain request, and in response you can get either the basic type "Reply" or "PeopleReply". In the first case, you get just a type with no data in it, and in the second, a collection of people. In the client code, it is enough to check the type of response. And it's better than checking for null.At least if we are talking about functional programming.Operate with types, not nullables.
@@bulsond That is not the correct way, because the object of vase class foes not represent anything in particular, yet is assignable to a reference that has a meaning. The correct way is to make such an object optional.
I don't see the benefit of functional programing. Where do you put the breakpoints in those shiny onliner flatMap functions? Also, besidedes saving vertical screen space, what other benefits do I get from fucntiononal programming.. (Hard mode: no use of "mutability", "pure", "side-effects" words)
@@zoran-horvat unit tests, and breakpoints.. nothing against some functional programming stuff, like pattern matching. but going all in, seems a bit overkill. specially since my brain is used to oop, using guards, throwning exception for early exit. basically I bought the Uncle Bob memes.
@@zoran-horvat svejedno, svaka cast na dobrom kanalu. jedan od boljih opcenito, tek danas slucajno naletio na to... ali vidi se kvaliteta i sadrzaja i produkcije.
@@Kasiux Actually, it lacks the Map method, but you can add it easily with an extension method. It can satisfy the criteria. You can also turn nullable reference types into a monad the same way.
Yes, this means if someone creates a new type of name and attempts to use it, there's some broken down-stream effect because the logic doesn't know how to process it. Creating a new type will mean tracking down all strategy patterns that switch on the type and updating them. People will say "just have unit tests!," but I think that's insufficient, it makes me kind of dislike this approach unless used very sparingly.
As Zoran said, C# doesn't have the tools to do exhaustive pattern matching. There are a number of libraries doing it. The goal is that if you add a new type, then the projects using pattern matching on that type will not compile, and the error will tell you that you are missing a type.
@@andrewshirley9240 There is a great misconception about functional types where many programmers believe they are somehow less powerful than OOP classes. The truth is that those two concepts are orthogonal. While all FP consumers would fail to compile when a new type variant is added, the same stands for subclasses when the new method is added to the base class. The reason why FP is so practical today is that business applications require frequent addition of functions, but not new variants of existing types. That is precisely what hurts in object-oriented design.
@@zoran-horvat I don't see what ToList has to do with IEnumerable being a monad. You say 'creation function' so I think you must be talking about the unit needed for a monad (since that's how you create a monad from a value), but ToList doesn't serve as a unit in any sense.
@@Robert-yw5ms It does serve as Unit, because it supports wrapping a single object into an object implementing IEnumerable. Other example is the array literal, which is also constructing IEnumerable.
@@Robert-yw5ms Aaargh you are right - I wanted to say new List, which supports the initializer syntax, not ToList. I wrote 100 comments today, please understand me :)
Not necessarily, because C# allows you to write part of the domain functionally and the rest object-oriented. It also supports rich procedural syntax, which is useful in performance-critical portions of code. There are many benefits from using C# in design.
@zoran-horvat F# allows you to write terse OOP code just like C#. And allows you to write procedural code. Of course there are some hurdles, especially when you need to cast to interface due to Hindley-Milner type inference in F#, but so many hurdles for FP design exist in C# - primarily OOP oriented, no inbuilt mutability, non-exhaustive pattern matching, lack of native discriminated unions (and standard Optional objects, etc.) Given that .NET is polyglot environment, why not use F# for functional design and C# for OOP?
@@dusanknezevic9072 Then there is the inverse question: why not use C#? You cannot mix languages within the same project, so .NET will not help you with that.
@@zoran-horvatnot within the same project yeah but you can have two projects one for each that can work interchangeably (you do have to rebuild for the other project to update but that’s the only issue I know of) however you do need to make sure not to use F# types for stuff the C# project will use or it will get messy but while it can be done I would also not recommend doing so. There’s features in both F# and C# I wish where in the other that would make both a good option for this
I like the Option monad, but I absolutely love the Result monad (because it can provide information about the outcomes of operations). Hey....perhaps you can do a vid on defining extension methods of `Select` and `SelectMany` so that one can use your Option type with the so-called 'query syntax'??
Option monad is great. The best implementation IMO is ErrorOr. It's not Result in the strictest sense, but it gives a great failure path, and is really easy for most developers to understand
@@linuxon-s3f Well, TypeScript came pretty much last, and it took portions of the syntax that proved well in other languages. C# was in a similar situation back in the time when this syntax was added to it. As one colleague of mine told me when I was learning TypeScript: nowadays you decide whether the arrow is -> or => and that's pretty much it. Everything else is about the same across languages.
when a new author type is added, the formatting function will need to be redone (add a new Func parameter). If there are too many inherited types, your code will turn into noodles.
Now listen to this - it is your words: When a new method is added to the base class, all subclasses will need to be redone (add a new method override). If there are too many inherited methods, your code will turn into noodles. It is genuinely funny how easy it comes to criticize functional design, but fail to see that it is in every respect a mirror image of the object-oriented design. Every strength of one is the weakness of the other, but also - vice versa. I hope you will give this detail a fair portion of thinking before making another comment.
@@zoran-horvat Just curious how would you handle the match function when you get new NameTypes? f.e. We need to add support for a middle name, maiden name and a pseudonym(alias)
@@mister-yuki-wfh Adding new subtypes breaks all functions in functional programming the same way as adding a new method breaks all subclasses in object-oriented programming. The reason why functional programming is gaining so much popularity in recent years lies in understanding that we add new functions much more frequently than new variants in discriminated unions. Therefore, while the problem you are referring to does exist, it is significantly less frequent in practice than the corresponding problem in the design of classes.
Check out Dometrain and use code ZORAN for 15% off any course ► dometrain.com/?coupon_code=ZORAN
Download source code ► www.patreon.com/zoranhorvat
Join Discord server with topics on C# ► codinghelmet.com/go/discord
Enroll course *Beginning Object-Oriented Programming with C#* ► codinghelmet.com/go/beginning-oop-with-csharp
Subscribe ► th-cam.com/channels/xsWfh8LCcn55mFB6zGBT1g.html
Task is the most prominent Monad in c#, right?
Right. The other most prominent one is IEnumerable.
guys stoooop
It's a comonad thanks to ContinueWith ;)
Thanks for the video. Especially the naming for types helped me since I encountered the feeling that when I code in functional way, my functions seem "not in right spot" or "not highlighted enough". Functions are in this case more important to me since they hold logic while types simply hold data. Thank you very much.
So many ideas fell into place while watching this. thank you.
In F# I regularly have same name for type and module: type Book, module Book, because F# does not have restriction that C# has.
Yes, in C# both are just classes.
@@zoran-horvat Yep, a static class is just C# way of saying Module. If use a F# module in C#, it operates just like a static class.
Just give me my dang tagged unions already
I'm with you!
One day 🙏
@@zoran-horvat What is tagged unions ? I know about discriminated unions in F# and C#, are these concepts "tagged unions" and "discriminated unions" the same ?
@@tore28 It is a union which also contains a "tag" field (usually just a byte would suffice). The tag identifies the type of the content stored in the rest of the object. Tagged unions are used to support type switching on polymorphic types that do not possess a header common in object-oriented classes.
@@tore28Yes! "tag" is another word for "discriminator".
Actively trying to learn pure functional approach by learning Haskell, this video is quite useful for me, I can see an example of this style in familiar language to reach more understanding. Thank you.
Excellent video, you are top-tier.
Thank you, my understanding of the difference between OOP and functional was way off before your video.
Cool video.
Functional concepts like bind are nice.
IMO things like ErrorOr are the simplest way to implement sane functional programming in C#. Procedural functional programming if you will
Async/await is basically a monad 🤷♂️
Great video, I use Static Classes (COUGH: C# version of Module's) all the time. It really help's speed up development. I still use OOP for some things, but I found using static classes and functional programming can clean up a lot plumbing, especially with .NET.
Static classes are a regular tool in functional design. That is mostly what they are meant for.
I always have a hard time knowing when to use this. When using c# why not keep using OOP. Why do i go functional, and when do i use it?
When you work on data with many (100 or more) rules. In parts of web backends not uncommon for example. You can do it with with oop but it is easier to read in functional stile.
Any model where the data model is known and never (or rarely) changes, but behavior grows every day, will likely be a good candidate for functional modeling. Doing the same work in an OO model would require several times longer code and will cause enormous issues.
Funny enough, that is a common situation in business operations and connected services today.
Use both
Use extension classes to create functionality.
Use objects/classes for data.
This mental model is easy to understand: data goes into a function, and comes out in a different shape
Hi. Do you have a video or example code about the Either-Monad?
I will get to that soon. In the meantime, I have an explanation of Either in my Pluralsight course on functional programming with C#.
Love functional programming and Zoran is guru in it. Who would like to dive deeply I recommend listening his Functional Programming lectures on Pluralsight
CreateMany() code in 10:27 seems very strict, I will prefer it like if All are null then return null, else return only the one are not null. so if authors are ["a", "b", null, "c"] => will return ["a", "b", "c"] instead of null.
If you considered the idea of a smart constructor, you would see that it doesn't make much sense to request _all_ contained objects to fail before failing.
What you are describing is the Bind function on IEnumerable. It is useful, but in different conditions.
Thanks for the video
Very great explanation
Do you have a SOLID or Design Pattern course?
I have several courses on design patterns on Pluralsight and Udemy. Not on SOLID.
@@zoran-horvat
I'm so excited to watch it all. I'll do it
Thank you very much
If you had to model different types of xml nodes with different rules and linq XElement is not enough, would you go the functional route?
Most nodes types would be known and not change in the future.
And what about text inside those nodes?
Im thinking about modelling to the smallest bit, like an object Character that has an index, a start column and end column (imagine that you can place the cursor before or after).
I think Functional is orthogonal to your question! If you need a custom type for a node, that's fine. If you want to model the behavior in the class, then go OOP. If you want to model the behavior in extensions, separate from the state - go functional!
6:25 I understand the intention here was to just show an example how to make subtypes, but can't help but be reminded of "Falsehoods Programmers Believe About Names" :)
@@Bravo-oo9vd Yep, once you really need to model names, you find out. I only saw the national register of names once and that was enough for one life.
I don't understand, the variants are internal but they can be freely accessed in the same project, may be that was kind of and abstract example?
That is only true as long as they are in the same assembly. If I moved the Models namespace into a separate assembly, which I would certainly do as the model grows large, then all code that references the internal members would fail to build.
While I appreciate how you've gone about it, I find the Book vs BookType confusing. If all Book is doing is providing static creation methods, why does it have to be a separate class (and thus the segue into how to name it), rather than just part of the record class? Is it to avoid the temptation of directly calling new Book()?
The reason is that it will not be all the book does. The next step from here is to start defining behavior, and we do that in the form of functions defined separately from the data type.
The greatest benefit from functional programming is that, unlike in OOP, different portions of behavior are defined in different packages.
@@zoran-horvat But it is perfectly fine to declare static functions in the (abstract) super type. Thus, the naming theme "***Type" becomes superfluous.
@@thomasschroter3802 Yes, but that goes against the general idea that those functions can also be defined in another module or namespace. Why make a special cade of some functions, but treat others regularly?
Hi Zoran, this is the first time I have seen you use "record BookType" with a "static class Book" for the constructors. In other videos/example you just put the "static Book Create" inside the record. I also don't see you appending "Type" to the record in another examples. Is there any particular reason? Maybe it's not important, and just a different style of doing the same thing
@@slowjocrow6451 This video is demonstrating strict functional programming applied in C#. Other videos are more practical, and so lift off some of the conventional elements.
@@zoran-horvat thanks Zoran, have a good weekend
Overall I think the state of functional programming in java to be superior to c#. Especially 14:20 java supports closed hierarchies enabling exhaustive type pattern matching.
I also think it's overcomplicated to hide the varients and create a match function. The sum type NameType with its varients FullNameType and MononymType can do these things with 1st class c# features (switch expressions) in the user code. Also separating the static code from the type is unnecessary and confusing. Just put the factory functions inside the record. This way you save yourself from the naming dilemma as well.
C# switch expressions are still cumbersome with no exhaustive checking. They must always include a throw instruction, which makes every screen if code look ugly. Once we get that feature, I guess I will stop favoring the Match method.
You can never guarantee that no one will ever extend your abstract class. It can be done in other assemblies. Sometimes even if the type is internal. If you skip the default switch case C# will add it for you. Try and test.
@@rafazieba9982 I am talking about the fact that java has a compile time and runtime mechanism to prevent unallowed inheritance. The syntax is somewhat confusing from a c# perspective as it uses the sealed keyword which means something different in c# and java.
@@redcrafterlppa303 I don't know of any Java syntax that allows those two classes to inherit but nothing else.
@@rafazieba9982
public sealed class Super permits Sub1, Sub2 {}
final class Sub1 extends Super {}
final class Sub2 extends Super {}
Super foo = new Sub2();
switch (foo) {
case Sub1 s1 -> {}
case Sub2 s2 -> {}
//no default (exhaustive)
}
Thanks Zoran! I guess SelectMany could be one of the monads you're referring to?
More likely the IEnumerable... Deferred execution (or lazy enumeration) of LINQ + IEnumerable is how monads also work. You declare the HOW without actually performing any operation, yet; only declarations. Once all set, you can unwrap the monad (or enumerate the enumerable), where each operation declared will be now performed.
@@KeenkiDash I don't believe deferred execution is necessary for a monad definition. It's just a nice feature (or footgun) that functional libraries tend to have.
@@Robert-yw5ms A monad requires a factory which elevates a regular value into a monad, and the Bind function, plus a few other rules known as the monad laws.
I am ok with FP where it makes sense. But that Printable example is not such a case.
It would make much more sense and avoid refactoring many places if the NameType simply defined a contract which concrete implementations must adhere to.
Whenever a developer needs a new NameType, they add a new class, implement the contract, and the rest of the code keeps working without changes.
Overall I am learning a lot from your videos but to me this example in particular is where OOP wins against FP - and that's ok, each has their advantages. As always, use the right tool for the job.
Why did you check string name for null but haven't done string? firstName in the function parameterz. Just forgot '?' ? (My ide would say it's always false)
That was just a line to show the exception in action and then to delete it.
However, if the factory function was supposed to be called from the outside, then non-nullable references can still be set to null and you would have to guard.
It was not necessary to make NameType abstract. Then the Create method would not need to return null, but instead it would be possible to return the base type, i.e. the same NameType. And in the future, you can use pattern matching instead of fighting the nullable type.
What would the base type instance represent?
@@zoran-horvat I wanted to answer in the comments on youtube, but I'll try. In your example, Create() -> Nametype? -> CreateMany(NameType?[]) -> NameType[]? -> if(NameType[]? is not null) Book.Create().
I suggest Create() -> Nametype -> CreateMany(NameType[]) in this method using pattern matching we remove the elements with the base type and return the same one -> NameType[] which can have a number of elements equal to zero and therefore -> if(NameType[].Count > 0) Book.Create().
@bulsond If I gave you an object of the base type, which is now abstract, what would that object represent?
@@zoran-horvat It will just be one of the types I expect, but without the data I need. And it's better than null. Let's say you have a certain request, and in response you can get either the basic type "Reply" or "PeopleReply". In the first case, you get just a type with no data in it, and in the second, a collection of people. In the client code, it is enough to check the type of response. And it's better than checking for null.At least if we are talking about functional programming.Operate with types, not nullables.
@@bulsond That is not the correct way, because the object of vase class foes not represent anything in particular, yet is assignable to a reference that has a meaning. The correct way is to make such an object optional.
I don't see the benefit of functional programing. Where do you put the breakpoints in those shiny onliner flatMap functions? Also, besidedes saving vertical screen space, what other benefits do I get from fucntiononal programming.. (Hard mode: no use of "mutability", "pure", "side-effects" words)
What breakpoints?
Do you really develop software using breakpoints?
@@zoran-horvat unit tests, and breakpoints.. nothing against some functional programming stuff, like pattern matching. but going all in, seems a bit overkill. specially since my brain is used to oop, using guards, throwning exception for early exit. basically I bought the Uncle Bob memes.
btw, are you from croatia? zakon da sunarodnjak radi tako dobre tutoriale, svaka cast.
@@DavidSmith-ef4eh Zapravo sam iz Beograda, mada imam rodbinu u Vinkovcima i okolini a prezime vučem iz Slavonske Požege 18. vek otprilike.
@@zoran-horvat svejedno, svaka cast na dobrom kanalu. jedan od boljih opcenito, tek danas slucajno naletio na to... ali vidi se kvaliteta i sadrzaja i produkcije.
Is it Nullable? :)
No, it is a regular reference type that can be null. The Nullable generic struct is only used on value types.
Nullable is a monad, right? :)
Because of the HasValue and Value properties
@@Kasiux Actually, it lacks the Map method, but you can add it easily with an extension method. It can satisfy the criteria.
You can also turn nullable reference types into a monad the same way.
doesnt that mean if someone creates a nametype the printable doesnt work anymore
I'm not sure I understand the question.
Yes, this means if someone creates a new type of name and attempts to use it, there's some broken down-stream effect because the logic doesn't know how to process it. Creating a new type will mean tracking down all strategy patterns that switch on the type and updating them. People will say "just have unit tests!," but I think that's insufficient, it makes me kind of dislike this approach unless used very sparingly.
As Zoran said, C# doesn't have the tools to do exhaustive pattern matching. There are a number of libraries doing it. The goal is that if you add a new type, then the projects using pattern matching on that type will not compile, and the error will tell you that you are missing a type.
@@andrewshirley9240 There is a great misconception about functional types where many programmers believe they are somehow less powerful than OOP classes. The truth is that those two concepts are orthogonal.
While all FP consumers would fail to compile when a new type variant is added, the same stands for subclasses when the new method is added to the base class.
The reason why FP is so practical today is that business applications require frequent addition of functions, but not new variants of existing types. That is precisely what hurts in object-oriented design.
I think LINQ SelectMany is kind of monad?
IEnumerable with SelectMany and a creation function (e g. ToList) is the monad.
@@zoran-horvat I don't see what ToList has to do with IEnumerable being a monad. You say 'creation function' so I think you must be talking about the unit needed for a monad (since that's how you create a monad from a value), but ToList doesn't serve as a unit in any sense.
@@Robert-yw5ms It does serve as Unit, because it supports wrapping a single object into an object implementing IEnumerable. Other example is the array literal, which is also constructing IEnumerable.
@@Robert-yw5ms Aaargh you are right - I wanted to say new List, which supports the initializer syntax, not ToList.
I wrote 100 comments today, please understand me :)
A piece of a monad: SelectMany is the monadic "bind"
Linq library is Monadic
It is, indeed.
If one feels like writing C# functionally to tee, isn't it better to just go with F#? Golden hammer..? 🤔
Not necessarily, because C# allows you to write part of the domain functionally and the rest object-oriented. It also supports rich procedural syntax, which is useful in performance-critical portions of code.
There are many benefits from using C# in design.
@zoran-horvat
F# allows you to write terse OOP code just like C#. And allows you to write procedural code.
Of course there are some hurdles, especially when you need to cast to interface due to Hindley-Milner type inference in F#, but so many hurdles for FP design exist in C# - primarily OOP oriented, no inbuilt mutability, non-exhaustive pattern matching, lack of native discriminated unions (and standard Optional objects, etc.)
Given that .NET is polyglot environment, why not use F# for functional design and C# for OOP?
@@dusanknezevic9072 Then there is the inverse question: why not use C#?
You cannot mix languages within the same project, so .NET will not help you with that.
consuming c# libraries, whose are a lot of, from within f# sometimes is hard and not worth the effort
@@zoran-horvatnot within the same project yeah but you can have two projects one for each that can work interchangeably (you do have to rebuild for the other project to update but that’s the only issue I know of) however you do need to make sure not to use F# types for stuff the C# project will use or it will get messy but while it can be done I would also not recommend doing so. There’s features in both F# and C# I wish where in the other that would make both a good option for this
I like the Option monad, but I absolutely love the Result monad (because it can provide information about the outcomes of operations).
Hey....perhaps you can do a vid on defining extension methods of `Select` and `SelectMany` so that one can use your Option type with the so-called 'query syntax'??
Option monad is great.
The best implementation IMO is ErrorOr. It's not Result in the strictest sense, but it gives a great failure path, and is really easy for most developers to understand
When i look at these codes I see typescript.
@@linuxon-s3f Well, TypeScript came pretty much last, and it took portions of the syntax that proved well in other languages. C# was in a similar situation back in the time when this syntax was added to it. As one colleague of mine told me when I was learning TypeScript: nowadays you decide whether the arrow is -> or => and that's pretty much it. Everything else is about the same across languages.
ahhh, just use F#!
Nope.
when a new author type is added, the formatting function will need to be redone (add a new Func parameter). If there are too many inherited types, your code will turn into noodles.
Now listen to this - it is your words:
When a new method is added to the base class, all subclasses will need to be redone (add a new method override). If there are too many inherited methods, your code will turn into noodles.
It is genuinely funny how easy it comes to criticize functional design, but fail to see that it is in every respect a mirror image of the object-oriented design. Every strength of one is the weakness of the other, but also - vice versa.
I hope you will give this detail a fair portion of thinking before making another comment.
@@zoran-horvat Just curious how would you handle the match function when you get new NameTypes? f.e. We need to add support for a middle name, maiden name and a pseudonym(alias)
@@mister-yuki-wfh Adding new subtypes breaks all functions in functional programming the same way as adding a new method breaks all subclasses in object-oriented programming.
The reason why functional programming is gaining so much popularity in recent years lies in understanding that we add new functions much more frequently than new variants in discriminated unions. Therefore, while the problem you are referring to does exist, it is significantly less frequent in practice than the corresponding problem in the design of classes.