Simon, you need better examples. I do like to think that I'm functionally curious, but when I see an example that does validation by newing regex instances to check alphanumerics, I can't help but wonder. Why multiple? Why not one? Why are they not cached? Why are there so many unnecessary allocations? Are FP people in C# just don't know how to code anything more demanding that a win forms app and are akin to snake oil salesman riding the wave of human ignorance? I go be confused now.
I've watched and listened to this several times over several months. I like it a lot. But I've only just picked up that your Maybe example has Just and Nothing as decendant types. 😅 Now I'm trying to think what implications this has got for functions taking and/or returning a Maybe type. Variance will come into it, right?
I really enjoyed this talk, I really loved linq in c# the most and once I discovered that this style can be basically extended to anything I got hooked!
Looking forward to your book in November. Been using functional c# for over a year, it certainly makes thing a bit easier, sometimes more than just a bit
52:59 I'm sorry Simon, that's *not* a Monad. That's a Functor; what you're doing there is not actually `Bind` but `Map`. If it was actually `Bind` then the signature would be `Identity Bind(this Identity @this, Func f)`. Note the difference in the second type of the `Func` argument.
you're not wrong. I've been aiming to keep things simpler though, for the sake of easy adoption by folks less used to FP. I'm taking what I believe to be a pragmatic approach, rather than one that sticks purely to the theory of the Functional paradigm
Interesting talk! Personally, I do feel that mixed-paradigm programming offers a lot of benefits over "pure" OOP or FP - not just in terms of flexibity, but potentially also intelligibility. There are things FP does better (safety, conciseness), and there are things OOP does better (dependency-injection is simpler than reader-monads, and controlled actual state is far more intelligible than using state-monads). Also, FP in a language which does not support something like the "do"-notation or "native" higher-kinded types (which aren't widespread at all) can get very ugly and very long very quickly, negating a lot of the benefits. Even in powerful and beautiful type-systems like those of Scala and TypeScript, the implementations e.g. for monad-transformers are quite involved and somewhat obscure. As for difficulty - I'm a huge fan of Scala 3 and ZIO (an effect-system), as it manages to remove almost all of the category-theoretical jargon and obscure concepts that (understandably) constitute such a high barrier to entry for functional programming, and hinders even many talented and experienced functional programmers in attaining deeper theoretical understanding. As someone who graduated in formal logic and philosophy of science, I think category theory is amazing - wonderfully expressive and universal. It can even replace set theory as a basis for the conceptualization and formalization of mathematics, and has wide-ranging applications beyond. The Curry-Howard-Lambek correspondence between logic/proof calculi, type-theory and category-theory is one of the most fascinating topics I've encountered. But even after a such a graduate degree and over a decade of experience in programming, I'm no expert in category-theory - quite a few things still elude me. I think this exemplifies why we cannot expect programmers to really "get" FP in the way it is usually presented. And unless you already planned on hiring only people with graduate degrees in formal logic or higher mathematics, trying to build a business on pure functional programming places you at a significant disadvantage in recruiting. That's why I am so enamored with ZIO, and why I applaud talks like these going a different route and making FP more accessible. Two minor points: Technically, the example below Church's image doesn't present a particular function - but instead a statement of how to generally derive the result of applying an argument to a function. (beta reduction). One might argue that it still describes a left-total and right-unique relation, and thus a function... and that is true, but it exists on a meta-level. Also while we usually (understably and with some reason) associate currying with Haskell Curry - the concept was defined and discussed a little earlier by Moses Schönfinkel, who rarely gets the credit he deserves - and not only because nobody wants to talk about "Schönfinkling" :) Another name worth looking up in this context is "Stephen Cole Kleene", from whom we get the Kleene-star "*" (and its rigorous definition), which appears in regex, glob and many other places.
Hi Simon! (from the transit lounge at Frankfurt Airport 😉) This actually makes me enthusiastic about learning C#. I'd previously thought of it as something to learn for strategic employability purposes but now it looks fun.
First of all, thanks for the great talk (and book) What I'm wondering is why aren't you reusing all of your complex null-checking logic from the Bind function in ToMaybe as well? That way you don't have to return Maybe from your data access layer and instead it's converted automatically. I'm thinking something like this: public static Maybe ToMaybe(this T @this) { return @this switch { T s when !EqualityComparer.Default.Equals(s, default) => new Just(s), T s when s.GetType().GetGenericArguments()[0].IsPrimitive => new Just(s), _ => new Nothing(), }; }
Procedural is like 1) Take banana from basket A, 2) It's a first banana, 3) Put banana in a basket B, 4) Take another banana from Basket A 5) it's a second banana 6) repeat it until basket A is empty. It's easy, doesn't require much thinking. Functional declarative approach might be harder to express, but it wins in so many ways.
I want a parser combinator for the nino. we just need a compiler option that makes immutability the default. even in OO, immutability is correct. there are very few exceptions, like caching and pooling, where mutability is either simpler, more efficient, or required.
@@simonpainter2242 agreed. I think they're doing a great job. Reading through the github issues gives a sense of how much thought they put into changes.
Simple is hard, complex is easy. That is to say, with respect to creating any system, creating a system marked by simplicity requires more creative work than a system marked by complexity. To purposely create a complete system with a limited concept count requires more imagination than creating a system with the (often detrimental) freedom to introduce new concepts (and the associated increased connectivity) at will.
Год назад+2
Nice start and good explanations but then it goes to 'strange' functions such as Alt and Fork which I'm not sure a lot of people would understand. Imho it makes functional code read like a math book. Very very concise but you miss one small part and it all just falls apart. I rather prefer hybrid approach.
I tend to agree. Part of the problem is that this is too large a topic to fit in a 45m lecture, which is why I have to make this a bit of a whistle stop tour. Hopefully folks will pick out the bits they like and use those
Immutability leads to more stable code, and more predicable, testable results. If there are any trade-offs, they aren't significant. FP _can_ be slightly worse in terms of memory allocation, but the difference is typically insignificant compared - say - to the effects of I/O operations.
Concurrency and referential transparency. The former a lot more than the latter. It's the reason why Java got lambdas and C# got LINQ. Mutable data and concurrent code is a recipe for disaster.
just show me how we can debug when we need to. I want to know what will happen in each step to investigate some weird results? how to do that if we chain things like this?
There are two options - split each piece of the chain out, so that you have a separate variable to examine for each step, or introduce a concept called either "Tee" or "Tap", which is like the "Bind", but it's read-only - it takes an Action instead of a Func and returns the original object back again. You can use it to insert log file calls within the chain. These days, VS2022 does also allow the debugger to step _inside_ the arrow function, so even without these steps you should be OK.
The thumbnail says "Senior Developer at Müller Dairy." For a second, I was thinking, "Boy. That's a very high tech dairy company he works for. Does he program milking machines?"
I’m from Java world! Despite that I’d like to work with C#, thanks for these kind of content, functional paradigm has always caught my curiosity however I won’t go too deep in the rabbit hole because it becomes so math stuff and bored and there’s no job offers for fancy languages such as F#
Why you using Maybe monada in c#? You can use nullable types and writing like void PrintWebSite(Person? person) { Console.WriteLine(person?.Company?.WebSite?.ToUpper() ?? "NOTHING"); }
Strictly speaking, a Person class would already _be_ nullable, so the question mark there isn’t needed. What I’m aiming to do with the monad is avoid the need for any null checking or exception handling, and just define a single flow of operations. It can remove an awful lot of boiler plate necessary in OOP
Yeah, in general its a good idea to be as declarative as possible, so I agree with most of what you are saying. But it does not need to be necessarily all about extension methods, you can also write just normal methods and give them proper naming to describe what they do... And btw you could have left the jokes away, its distracting.
There are all sorts of ways of doing this. I'm using Extension Methods to try and replicate the sorts of things languages like F# can do "out of the box". I'm not going to criticise anyone for just sticking with LINQ or whatever, though. Whatever works for you is fine. I _could_ have left the jokes out. But I didn't :p Apologies if my sense of humour isn't to your taste. FP is a very dry topic, and I thought a little injection of silliness might make it easier to take in.
Jump to 15:00 and realize why this kind of programming is unfeasible in the majority of what we code. Now jump to 19:32 and listen to what he says. This not programming. You won't be a good programmer programming like this.
Skill issue. Any infeasibility for csharp using functional isn't from what functional programming is trying to do, it's from how cumbersome, absurd and verbose csharp is. His slide is overly terse at 15:00, it almost gives the impression that you cannot perform those operations with fp. That's simply untrue, even in Haskell. There is definitely a protective layer around IO, but it's impossible to write any real program without some kind of side effect. No one actually claims it is. What is the claim is that stateless pure functions as the main core of your application are glorious, and absolutely make it easier to reason about, while you keep the impure functions at the edge of the system where they can't screw things up very badly. You keep them in the grip of a monad or something, so that they can't infect the core with their chaos. It's better that way. Genuinely. As far as 19:32 not being "real" programming... Well it certainly works, is predictable, composable, clear to read what's happening, and gets the job done, so I can only assume your definition of real means.. For loops? Are you missing seeing some sort of IImplementsAbstractFactoryManagerServiceHelperDecoratorBaseClassPattern or something? Real programming is the code that does what you need it to do. You can do it in many ways. Most of those ways used by most people are horrible, and lead to crappy, bug ridden, slow systems that are so dizzying in their layers upon layers of convoluted misdirected nonsense that it takes a sadistic level of grit to grok even one small chunk of the system, much less have a real holistic understanding of it from start to finish. It's infeasable to expect an OO codebase to be something to have real knowledge of, because so much of it so obsessed with encapsulation that the combinatorial explosion of in flight mutable variables becomes impossible for a human being to track at scale. Then you add async and generics and the whole thing goes kaboom. Then after half a million lines they ask for a rewrite in 5 years, because no one can confidently make changes to the system anymore, it's become such a mess. If that's what real programming is, I don't want anything to do with it. I'll take my simplicity and elegance from composable expressive pieces, thank you very much.
5:37 “select is the last thing to run… with declarative we are not concerned with the order of operations” whoever put SELECT first in an SQL statement was an idiot, and therefore SELECT is a terrible example of declarative programming… or a good example of how it can be bad.
I’m honestly not sure why the select was put first. There may have been a good reason for it, I don’t know. That’s something I might look into one day!
I think extension methods have their place & can be very useful tools when used well. I draw the line at putting business logic in them though, which is why I kept them super generic here
@@simonpainter2242 I think having business logic as composed of a pipeline of stateless functions that take parameters for particulars and can be arranged however the business logic needs to transform data is super powerful. I say make them small and build em like Lego. And.. Use a Hindly Milner type inferred Lang. Like fsharp or Ocaml.
This is a good example of a bad lecture. Little is explained, but random examples are shown that are confusing and incomprehensible in the short term. The formatting and naming of the examples is so confusing that they are difficult to read. sit down, F- p.s. Since an international audience is being addressed, most of whom do not speak native English, it would be desirable to speak in high-level language clearly and without the local dialect.
I’m very sorry to hear you didn’t like the talk. It is a bit of a whistle stop tour of the field, because there simply isn’t enough time to go into everything in as much detail as I’d like. I could easily talk for a whole session just on discriminated unions! I’m not sure what you mean about the “local dialect”. Do you mean my accent? I’m afraid I’m stuck with it, and unable to do much with it. Consequence of coming from where I do, I’m afraid…
International audience? It's a talk done by a Brit at a conference in London. The examples were also quite simple to follow and his accent is just about as clear as can be. If your English is too poor to keep up, that's a personal problem.
Hi. I'm the person that gave this talk. Thanks for watching! I'll try and keep an eye on the comments thread here, but please keep it polite :)
UwU
Did you get your free yoghurt and buttermilk from Müller? 🙂
@@forestmanzpediaat this point, when it does arrive, it'll have fermented into hyperyoghurt.
Simon, you need better examples.
I do like to think that I'm functionally curious, but when I see an example that does validation by newing regex instances to check alphanumerics, I can't help but wonder. Why multiple? Why not one? Why are they not cached? Why are there so many unnecessary allocations? Are FP people in C# just don't know how to code anything more demanding that a win forms app and are akin to snake oil salesman riding the wave of human ignorance?
I go be confused now.
I've watched and listened to this several times over several months. I like it a lot. But I've only just picked up that your Maybe example has Just and Nothing as decendant types. 😅
Now I'm trying to think what implications this has got for functions taking and/or returning a Maybe type.
Variance will come into it, right?
Honestly this error-free and obvious-values approach is a breath of fresh air for someone deep into C++ OOP.
Thanks! I hope I was of some use to you, then!
I really enjoyed this talk, I really loved linq in c# the most and once I discovered that this style can be basically extended to anything I got hooked!
Glad to hear it :)
Looking forward to your book in November. Been using functional c# for over a year, it certainly makes thing a bit easier, sometimes more than just a bit
Thanks! I hope you enjoy it!
52:59 I'm sorry Simon, that's *not* a Monad. That's a Functor; what you're doing there is not actually `Bind` but `Map`. If it was actually `Bind` then the signature would be `Identity Bind(this Identity @this, Func f)`. Note the difference in the second type of the `Func` argument.
you're not wrong. I've been aiming to keep things simpler though, for the sake of easy adoption by folks less used to FP. I'm taking what I believe to be a pragmatic approach, rather than one that sticks purely to the theory of the Functional paradigm
Referential transparency makes FP more testable than any other concept.I like ur presentation.Thanks for sharing..
You’re very welcome! Thanks!
Interesting talk! Personally, I do feel that mixed-paradigm programming offers a lot of benefits over "pure" OOP or FP - not just in terms of flexibity, but potentially also intelligibility.
There are things FP does better (safety, conciseness), and there are things OOP does better (dependency-injection is simpler than reader-monads, and controlled actual state is far more intelligible than using state-monads). Also, FP in a language which does not support something like the "do"-notation or "native" higher-kinded types (which aren't widespread at all) can get very ugly and very long very quickly, negating a lot of the benefits. Even in powerful and beautiful type-systems like those of Scala and TypeScript, the implementations e.g. for monad-transformers are quite involved and somewhat obscure.
As for difficulty - I'm a huge fan of Scala 3 and ZIO (an effect-system), as it manages to remove almost all of the category-theoretical jargon and obscure concepts that (understandably) constitute such a high barrier to entry for functional programming, and hinders even many talented and experienced functional programmers in attaining deeper theoretical understanding.
As someone who graduated in formal logic and philosophy of science, I think category theory is amazing - wonderfully expressive and universal. It can even replace set theory as a basis for the conceptualization and formalization of mathematics, and has wide-ranging applications beyond. The Curry-Howard-Lambek correspondence between logic/proof calculi, type-theory and category-theory is one of the most fascinating topics I've encountered. But even after a such a graduate degree and over a decade of experience in programming, I'm no expert in category-theory - quite a few things still elude me.
I think this exemplifies why we cannot expect programmers to really "get" FP in the way it is usually presented. And unless you already planned on hiring only people with graduate degrees in formal logic or higher mathematics, trying to build a business on pure functional programming places you at a significant disadvantage in recruiting.
That's why I am so enamored with ZIO, and why I applaud talks like these going a different route and making FP more accessible.
Two minor points: Technically, the example below Church's image doesn't present a particular function - but instead a statement of how to generally derive the result of applying an argument to a function. (beta reduction). One might argue that it still describes a left-total and right-unique relation, and thus a function... and that is true, but it exists on a meta-level.
Also while we usually (understably and with some reason) associate currying with Haskell Curry - the concept was defined and discussed a little earlier by Moses Schönfinkel, who rarely gets the credit he deserves - and not only because nobody wants to talk about "Schönfinkling" :)
Another name worth looking up in this context is "Stephen Cole Kleene", from whom we get the Kleene-star "*" (and its rigorous definition), which appears in regex, glob and many other places.
I agree ,controlled state management is good in some domain such as e-commerce.
Great Talk! Very informative. Thanks
Thanks!!
Hi Simon! (from the transit lounge at Frankfurt Airport 😉) This actually makes me enthusiastic about learning C#. I'd previously thought of it as something to learn for strategic employability purposes but now it looks fun.
Thanks :) I've certainly been enjoying coding this way for the last few years!
Great talk! I enjoyed it and learned things. Thank you!
no, thank _you_ for watching & your kind words :)
I enjoyed this talk. Thanks Simon!
Thanks, that's very kind of you to say! :)
This is amazing.Thanks for your talk and looking forward to check out your book.
Thank you, that's so kind of you :)
First of all, thanks for the great talk (and book)
What I'm wondering is why aren't you reusing all of your complex null-checking logic from the Bind function in ToMaybe as well?
That way you don't have to return Maybe from your data access layer and instead it's converted automatically.
I'm thinking something like this:
public static Maybe ToMaybe(this T @this)
{
return @this switch
{
T s when
!EqualityComparer.Default.Equals(s, default) =>
new Just(s),
T s when
s.GetType().GetGenericArguments()[0].IsPrimitive =>
new Just(s),
_ => new Nothing(),
};
}
That's actually a very good idea!!!
Procedural is like 1) Take banana from basket A, 2) It's a first banana, 3) Put banana in a basket B, 4) Take another banana from Basket A 5) it's a second banana 6) repeat it until basket A is empty. It's easy, doesn't require much thinking. Functional declarative approach might be harder to express, but it wins in so many ways.
I want a parser combinator for the nino.
we just need a compiler option that makes immutability the default. even in OO, immutability is correct. there are very few exceptions, like caching and pooling, where mutability is either simpler, more efficient, or required.
I'd love that too, but I don't think it's likely. I think the C# team are very concerned with backwards compatibility.
@@simonpainter2242 agreed. I think they're doing a great job. Reading through the github issues gives a sense of how much thought they put into changes.
I'm re-watching to figure out what I meant by nino
@@7th_CAV_Trooper it's a National Insurance Number. We all have one for work & tax purposes in the uk. They're a combination of alphanumerics
@@simonpainter2242 worked out well because I enjoyed the presentation as much the second time.
Simple is hard, complex is easy. That is to say, with respect to creating any system, creating a system marked by simplicity requires more creative work than a system marked by complexity. To purposely create a complete system with a limited concept count requires more imagination than creating a system with the (often detrimental) freedom to introduce new concepts (and the associated increased connectivity) at will.
Nice start and good explanations but then it goes to 'strange' functions such as Alt and Fork which I'm not sure a lot of people would understand. Imho it makes functional code read like a math book. Very very concise but you miss one small part and it all just falls apart. I rather prefer hybrid approach.
I tend to agree. Part of the problem is that this is too large a topic to fit in a 45m lecture, which is why I have to make this a bit of a whistle stop tour. Hopefully folks will pick out the bits they like and use those
35:10 wait… why do we want immutability? Are there scenarios we need to be aware of where we are making trade offs for something else? Eg performance?
Immutability leads to more stable code, and more predicable, testable results. If there are any trade-offs, they aren't significant. FP _can_ be slightly worse in terms of memory allocation, but the difference is typically insignificant compared - say - to the effects of I/O operations.
Concurrency and referential transparency. The former a lot more than the latter. It's the reason why Java got lambdas and C# got LINQ. Mutable data and concurrent code is a recipe for disaster.
just show me how we can debug when we need to. I want to know what will happen in each step to investigate some weird results? how to do that if we chain things like this?
There are two options - split each piece of the chain out, so that you have a separate variable to examine for each step, or introduce a concept called either "Tee" or "Tap", which is like the "Bind", but it's read-only - it takes an Action instead of a Func and returns the original object back again. You can use it to insert log file calls within the chain. These days, VS2022 does also allow the debugger to step _inside_ the arrow function, so even without these steps you should be OK.
NIce explanation of Monads
Thanks!
The thumbnail says "Senior Developer at Müller Dairy." For a second, I was thinking, "Boy. That's a very high tech dairy company he works for. Does he program milking machines?"
lol, no. Thankfully those machines largely look after themselves! I mostly do logistics applications
My interest in this went up exponentially when Doctor Who was brought in 😅
I have a hard time not including it! Check out my book for even more DW references
I’m from Java world! Despite that I’d like to work with C#, thanks for these kind of content, functional paradigm has always caught my curiosity however I won’t go too deep in the rabbit hole because it becomes so math stuff and bored and there’s no job offers for fancy languages such as F#
it doesn't have to become too maths heavy. The beauty of C# is that you can use as little or as much FP as you want
Why you using Maybe monada in c#?
You can use nullable types and writing like
void PrintWebSite(Person? person)
{
Console.WriteLine(person?.Company?.WebSite?.ToUpper() ?? "NOTHING");
}
Strictly speaking, a Person class would already _be_ nullable, so the question mark there isn’t needed. What I’m aiming to do with the monad is avoid the need for any null checking or exception handling, and just define a single flow of operations. It can remove an awful lot of boiler plate necessary in OOP
Terrible. Please don't research functional styles if you're still writing junior-level code like that.
Yeah, in general its a good idea to be as declarative as possible, so I agree with most of what you are saying. But it does not need to be necessarily all about extension methods, you can also write just normal methods and give them proper naming to describe what they do... And btw you could have left the jokes away, its distracting.
There are all sorts of ways of doing this. I'm using Extension Methods to try and replicate the sorts of things languages like F# can do "out of the box". I'm not going to criticise anyone for just sticking with LINQ or whatever, though. Whatever works for you is fine.
I _could_ have left the jokes out. But I didn't :p Apologies if my sense of humour isn't to your taste. FP is a very dry topic, and I thought a little injection of silliness might make it easier to take in.
Jump to 15:00 and realize why this kind of programming is unfeasible in the majority of what we code. Now jump to 19:32 and listen to what he says. This not programming. You won't be a good programmer programming like this.
good joke 😂
Skill issue. Any infeasibility for csharp using functional isn't from what functional programming is trying to do, it's from how cumbersome, absurd and verbose csharp is. His slide is overly terse at 15:00, it almost gives the impression that you cannot perform those operations with fp. That's simply untrue, even in Haskell. There is definitely a protective layer around IO, but it's impossible to write any real program without some kind of side effect.
No one actually claims it is. What is the claim is that stateless pure functions as the main core of your application are glorious, and absolutely make it easier to reason about, while you keep the impure functions at the edge of the system where they can't screw things up very badly.
You keep them in the grip of a monad or something, so that they can't infect the core with their chaos. It's better that way. Genuinely.
As far as 19:32 not being "real" programming... Well it certainly works, is predictable, composable, clear to read what's happening, and gets the job done, so I can only assume your definition of real means.. For loops? Are you missing seeing some sort of IImplementsAbstractFactoryManagerServiceHelperDecoratorBaseClassPattern or something?
Real programming is the code that does what you need it to do.
You can do it in many ways.
Most of those ways used by most people are horrible, and lead to crappy, bug ridden, slow systems that are so dizzying in their layers upon layers of convoluted misdirected nonsense that it takes a sadistic level of grit to grok even one small chunk of the system, much less have a real holistic understanding of it from start to finish. It's infeasable to expect an OO codebase to be something to have real knowledge of, because so much of it so obsessed with encapsulation that the combinatorial explosion of in flight mutable variables becomes impossible for a human being to track at scale. Then you add async and generics and the whole thing goes kaboom. Then after half a million lines they ask for a rewrite in 5 years, because no one can confidently make changes to the system anymore, it's become such a mess.
If that's what real programming is, I don't want anything to do with it.
I'll take my simplicity and elegance from composable expressive pieces, thank you very much.
5:37 “select is the last thing to run… with declarative we are not concerned with the order of operations” whoever put SELECT first in an SQL statement was an idiot, and therefore SELECT is a terrible example of declarative programming… or a good example of how it can be bad.
I’m honestly not sure why the select was put first. There may have been a good reason for it, I don’t know. That’s something I might look into one day!
simply WITH cte AS (select * from table1 join table2 on m = n where x = y) SELECT a, b, c FROM cte
Too many extentions is not cool , and extentions for t, make you see it for all types when you coding.
I think extension methods have their place & can be very useful tools when used well. I draw the line at putting business logic in them though, which is why I kept them super generic here
@@simonpainter2242 I think having business logic as composed of a pipeline of stateless functions that take parameters for particulars and can be arranged however the business logic needs to transform data is super powerful. I say make them small and build em like Lego. And.. Use a Hindly Milner type inferred Lang. Like fsharp or Ocaml.
This is a good example of a bad lecture. Little is explained, but random examples are shown that are confusing and incomprehensible in the short term. The formatting and naming of the examples is so confusing that they are difficult to read. sit down, F-
p.s. Since an international audience is being addressed, most of whom do not speak native English, it would be desirable to speak in high-level language clearly and without the local dialect.
I’m very sorry to hear you didn’t like the talk. It is a bit of a whistle stop tour of the field, because there simply isn’t enough time to go into everything in as much detail as I’d like. I could easily talk for a whole session just on discriminated unions!
I’m not sure what you mean about the “local dialect”. Do you mean my accent? I’m afraid I’m stuck with it, and unable to do much with it. Consequence of coming from where I do, I’m afraid…
International audience? It's a talk done by a Brit at a conference in London. The examples were also quite simple to follow and his accent is just about as clear as can be. If your English is too poor to keep up, that's a personal problem.
I've turned on the autogenerated subtitles for a few minutes and they've been spot-on, if you're not used to an English midlands(?) accent
@@RoamingAdhocrat Glad to hear it was possible to understand my "difficult" accent. I am indeed from the Midlands - Shropshire, to be specific.