It's frustrating that we still can't do "from (x, y) in ..." or "let (x, y) = ...". This could also be extended by defining Deconstruct extension methods.
Mind = blown. I haven't used LINQ comprehensions in at least 5-6 years. Thanks for showing us its power 😁 Will this still work with an EF Core query though? I guess not, because the entire query needs to be converted to SQL?
This won't work with EF Core precisely for the reasons you stated. This coding method is reserved for functional models where you already possess objects and then start calling functions that may return monads, such as validation, lookup, all kinds of transforms with a negative branch, even tasks.
I already have used EF Core with Linq and the result was a dynamic SQL on the fly. The base example in EF Core with log enabled you can see the SQL generated.
From other videos i learned from your great practical experience... but this one... This one bend my mind literally. I have quite huge sql background but i never get in linq compenheshions although should be more familiar to me i always prefered chained version. But you made me look at them from diffrent perspective. Fun fact.... I was given book about linq before i know c#. I loved idea instantly, maybe because i dreamed about simillar scripting for file dataabses but less declarative than sql
Ok the last part is incredibile. Gonna explore it more. And overall, im thinking that what im working on would benefit from this coding style. I have an in memory rappresentation of xml and i need to transform these tag and attributes in various objects. I have all these nested calls to extensions methods i ve made to map these collections of objects to each others. For example in one case i have xml nodes and each node have attributes and each attribute maybe have a value maybe not. If not then i need to either let it empty or take a value from some defaults which are also in a list of their own that are related to the node itself. The linq comprehension syntax would simplify this from what im seeing.
Regarding your Option implementation, I wrote something very similar, but instead I created an IOption interface with two concrete implementations (Some, None) and a Match extension method that allows us to treat it as a sort-of discriminated union via pattern matching. I think that it is very clear when looking at the code, but I worry if it’s maybe not as performant as the suggested implementation.
one suggestion from the trenches of using an option type in production for many years: Always make it a value type (i.e. struct) so that you avoid the pitfalls of the option itself being null! A 'default' option should be a 'none.' There are also performance considerations as well, but the correctness is the main reason i'd argue for this.
@@PlerbyMcFlerb That makes sense. Some of my earlier implementations of the option were value types. This one was not meant to be really used, so I avoided all complications and focused on LINQ alone. However, it is not the "it could be null" argument in my opinion. If you catch a programmer setting an option to null or default, just hit them hard and they will learn. I mean... null? That's my attitude.
These are rookie comprehensions. Most seasoned engineers in Python write this in 2 seconds and deliver list comprehension-driven applications to production with average algorithmic complexity of O(n!). Also, might as well use F# at this point :) These lambdas sure make DynamicPGO work really hard devirtualizing them...
"There is no way to know, whether the value is there or not..." - this is where convenience is the king. There is Option.is_some() in rust, Optional.isPresent() in Java, even isJust :: Maybe in Haskell. Moreover, in C# you can have bool IsSome() and bool IsSome(Func predicate) overloads. IsSome is indispensable when you need an early return.
@@zoran-horvat that depends entirely on surrounding code. Might be for unit testing: Assert.That(maybeResult.IsSome); Or an early return: var maybeResult = GetSomeData(); if (!maybeResult.IsSome()) return; // Remaining method body ... At the end of the day, those monads, options, results, etc. are just ways to express knowledge about something. And sometimes I'm interested not in data itself, but whether the data exists at all. There is also almost always a T Option.Map(Func onSome, Func onNone) or similar in all the implementations, exactly for unwrapping the maybes. As I'said, in this case practicality beats the mathematically correct form =)
@@zoran-horvat int id = 3; String query = "SELECT name FROM cars WHERE id = ?"; Optional res = jdbi.withHandle(handle -> handle.select(query, id) .mapTo(String.class) .findOne()); res.ifPresentOrElse(System.out::println, () -> System.out.println("N/A"));
@@janbodnar7815 That was not the use of IsPresent. Pattern matching is the intended use of monads, just as you did. P.S. There is a simpler form of your expression, where the optional string is first reduced to N/A if none and then printed unconditionally.
@@jonasbarka It is called query syntax indeed, but it is also referred to as comprehension. The term comes from functional programming, most notably from its similarity to list comprehensions. Now, since comprehensions are an essential tool in functional programming, and some LINQ query expressions do precisely that, you will often hear functional practitioners using the term LINQ comprehensions for that form. Neither term works against the other. They both exist, but with slightly different intentions. For example, you would hardly ever think of using grouping or joining in a LINQ comprehension - what would the meaning of that be? Comprehensions solely depend on Select and SelectMany as the source of items.
I have two questions: 1) what would be printed in case first "from" returns none? 2) what would be printed in case first "from" returns a book but second "from" returns None? Great video as usual, Zoran!
In both cases the overall result is None. You can see it from the corresponding chained expression with SelectMany and IEnumerable data - nothing executes when the previous stage ended up with an empty sequence. LINQ comprehensions implement railway-oriented programming out of the box. Every stage that can produce a negative result has the power to move execution to the error track and everything beyond that point is skipped. Doing ROP on your own usually takes 2-3 times more typing.
@@isodoubIet Query syntax is alien to C#. Programmers normally abandon it as soon as they see how to use normal methods. I've never seen it used regularly, except in functional programming.