Prime Reacts: From C to C++ to Rust to Haskell

Поделиться
HTML-код
  • Опубликовано: 11 янв 2025

Комментарии • 1,2 тыс.

  • @BenVisness
    @BenVisness Год назад +392

    7:47 “it avoids the possibility of off-by-one errors”, he says while adding 1 to the end of his range to make it inclusive

    • @dahahaka
      @dahahaka 7 месяцев назад +5

      The previous code has multiple places where off by one errors can stack and even cancel each other out due to < and

    • @tzint56
      @tzint56 7 месяцев назад +13

      Us Haskell giganerds avoid the possibility of off-by-one errors by not having iteration at all, and instead optimizing the language for recursion. Yes, that means no while loops, no for loops, no flow control made for a toddler. Off by one errors are straight up not possible since there's no mutability. index++ can't be an iterative step--It mutates a variable, and that is not allowed.
      Instead, we use function guards and define a condition for when recursion should stop. If going across a list, this is when the head of the list is empty. And yes, I can use C, Java, etc etc etc as well. I actually don't work in Haskell. But it has taught me to be a better programmer overall.
      On the surface this leads to memory usage concerns, but in practice the compiler will compose your functions together such that your entire program is more analogous to an end-to-end stream of data.

    • @DiVoidationStudio
      @DiVoidationStudio 6 месяцев назад

      ​@@tzint56nah/

    • @oioio-yb9dw
      @oioio-yb9dw 4 месяца назад +1

      ​@dahahaka I just have a question; if you say to the language to do something and there is a chance it will not do it well, then why keep using said language over and over? It doesn’t make sense to me.

    • @NubeBuster
      @NubeBuster 2 месяца назад

      At that moment I thought this guy must be joking right

  • @richardfairthorne7021
    @richardfairthorne7021 Год назад +2088

    The original C was readable by an infant. I need an encyclopedia of the history of programming methodologies to understand his C++ refactor.

    • @richardfairthorne7021
      @richardfairthorne7021 Год назад +83

      Also, you can use explicit returns, and still end your block with an implicit return.

    • @marcossidoruk8033
      @marcossidoruk8033 Год назад +261

      I don't understand how on earth can anyone think any of the (modern) C++ versions is better, all the advancements in modern text editors and LSPs to make typing faster and for some goddamn reason some people are obsessed with typing less even if that means writing the most cryptic expressions known to man.
      The C version is not only orders of magnitude more readable but also more versatile, you can tweak it here or there if the requirements change, however the C++ ...
      Also the C version has no function calls and thus translated almost 1 to 1 to an equivalent assembly and thus the compiler may be able to optimize it much better, notice the C code had the least instructions in O2 and then in O3 the most probably due to loop unrolling. If I had to take a guess I would say the C code is noticeably faster than the rest. Both in O2 and O3.

    • @julealgon
      @julealgon Год назад +44

      At the end of the day, the C++ version is less error-prone though. That should be incentive enough to learn it.

    • @marcossidoruk8033
      @marcossidoruk8033 Год назад +210

      @@julealgon How on earth is that less error prone??????
      It seriously boggles my mind. The rust version I can understand but the C++???
      The original C code is as simple as it gets and it is much more explicit in every aspect and on top of that it is orders of magnitude easier to tweak, how in all hell is that more error prone than a single Statement that uses layers upon layers of abstraction that you have to remember specifically how every piece works including the return types and corresponding operator overloads for those types. Absolute insanity, what has happened to software development?
      And then what if you realised you had to handle a handful special cases? The C version you just add a couple ifs inside the loop or whatever and you are basically done, you can make little adjustments here or there no problem, the C++ version on the other hand is painfully specific and you would probably have to rewrite it just to make a minor modification.
      Would you rather maintain a 250 lines file that uses a different std::nonsense function every single line complete with overloaded operators everywhere or a 1000 or heck, even 2000 line C file that does exactly the same in the most explicit, simple way possible? I would take the C codebase any time.

    • @newyorthtimes4496
      @newyorthtimes4496 Год назад +81

      @@marcossidoruk8033 one thousand percent agree. Reading that refractored code gave me brain aneurism of the highest order.

  • @radioJim
    @radioJim 7 месяцев назад +82

    This is the guy Torvalds was talking about when he said "if the choice of C were to do nothing but keep the C++ programmers out, that in itself would be a huge reason to use C."

  • @feschber
    @feschber Год назад +1280

    You don't have to debug Haskell because either
    a) you didn't use it in the first place
    b) you git everything right because you wrote it in Haskell and you are a genius

    • @ThePrimeTimeagen
      @ThePrimeTimeagen  Год назад +347

      these are facts

    • @taurasandaras4699
      @taurasandaras4699 Год назад +1

      1 reply but top comment?

    • @noxabellus
      @noxabellus Год назад +13

      Nah actually Haskell is pretty easy to write but debugging it is impossible

    • @VojtěchJavora
      @VojtěchJavora Год назад +10

      ​@@noxabellusyou use REPL for debugging.

    • @laughingvampire7555
      @laughingvampire7555 Год назад +6

      you only get that with JSDL unless you break JDSL and you make Tom angry

  • @jon9103
    @jon9103 Год назад +90

    7:50 "avoids off by one errors"
    Shows a loop that requires a + 1 to avoid being off by one. 😂

  • @k98killer
    @k98killer Год назад +579

    That c++ refactor was the most ridiculous and hilarious thing I've seen in a while. I burst into laughter at the exact same time Primagen did.

    • @lostsauce0
      @lostsauce0 Год назад +7

      "burst" 😂
      There's an old article by Burst lib showing how to refactor a function that calculates the distance between two points to be generic. It's absolute insanity

    • @abhaysingh-lg2cr
      @abhaysingh-lg2cr Год назад

      literally you! 😳

    • @random6033
      @random6033 Год назад +12

      Also the entire thing is kind of pointless cuz.... MATH
      This is the objectively correct version:
      int calculate(int bottom, int top)
      {
      bottom += bottom & 1;
      top -= top & 1;
      if (bottom > top)
      return 0;
      return (bottom + top) * ((top - bottom) / 2 + 1) / 2;
      }

    • @xkali8119
      @xkali8119 Год назад +11

      @@random6033 sorry, but this version is rather cryptic, even if it's correct. I would rather keep the for loop.

    • @dimit3356
      @dimit3356 11 месяцев назад +4

      @@random6033 I compiled your code and his code and after removing the comments and empty lines yours was 105 lines long and his was only 102 long. so hardly no difference. I decided to use my twisted edrich mind and write it in my way i got for the following code only 99 lines of assembly:
      int calculate(int bottom, int top){
      int sum = 0;
      m1:
      if(bottom

  • @uzbekistanplaystaion4BIOScrek
    @uzbekistanplaystaion4BIOScrek Год назад +316

    what i've learned: C is evergreen, can look really nice and is super readable, but will spontaneously transmute into a nuclear bomb if you look away for an instant. rust is the new kid on the block with fancy hair, but is still just C's cousin on mood stabilisers. haskell is an eldritch horror that will annihilate your mind but in return allows you to astrally project your code into a pocket dimension were it runs optimally. c++ is a hulking, grotesque abomination enslaved by sadists to run the entire world at our peril and would benefit from being taken round the back of the shed and shot squarely in the base of the skull.

    • @Naa-ee7nq
      @Naa-ee7nq 10 месяцев назад

      the problem with C++ is people like the guy who "refactored" the original C code
      if you use C++ sensibly, it's pretty great, but that usually requires a strict team-wide discipline
      namespaces are very useful, you can hack namespaces in C with struct and headers but it's not the same
      const ref& is extremely useful and allows for a lot of flexibility
      every other feature or library is to be used strategically and not "because it's cool" - the more features of a language you use the bigger the barrier you make for readability and maintainability - templates, std and boost features are to be used very strategically, with the exception of idiomatic things that will be commonplace anyway (like, say, vector stuff, ranges, safe strings etc etc)
      a massive problem with C++ is that when you have to use someone else's code it's often completely alien to your internal practices, but this happens with C as well just not as much
      generally you don't want to work with other people's code in C++ if it's not a readily usable library that you don't need to touch and that you can trust with your life that it works as is (namely std, or boost)

  • @Euphorya
    @Euphorya Год назад +662

    Haskell does require you to pretty much throw away everything you know about imperative programming. It's a completely different paradigm, but once you do learn how it works the code here is very readable. I recommend learning a bit of Haskell just because it requires you to think so differently about solutions.

    • @marcs9451
      @marcs9451 Год назад +63

      Also causes your solutions to be painfully slow. Computers are fundamentally imperative, if your functional language doesn't guarantee tail cal optimazation (only one I know of is Scheme) it quickly becomes a stack destroying monster that also ruins cache coherence. Pure functional languages manage to always do the worst case scenario for the CPU.

    • @havocindustries3078
      @havocindustries3078 Год назад

      ​@@marcs9451I feel like you don't get the idea behind Haskell and company. The language is a tool that is supposed to help you write readable and maintainable code. Code is a medium to communicate with other developers. That a computer can understand the code you've written is merely a side product. Therefore it is better to write succinct code than fast code. The compiler's job is to produce fast code in more cases than a developer could do and still be correct. GHC is doing impressive work in that regard. Scala is available and production ready doing similar and it allows you to enforce TCO with @tailcall. There's a bunch of languages that try to push generally available optimizations. Rust is yet another example. For example, the optimisation behind option are available to all data structures of similar form. Looking at the current state of application development, it's green threads aka async / await all over the place. Your declared worst case scenario is the current state of every application in any language anyway. So why does it matter? A ton of programs transfer a few bytes between a client and a database. It's not like they actually need to have tremendous performance.

    • @thomassynths
      @thomassynths Год назад +59

      @@marcs9451 To be fair (and yes I know this is not pedantic since languages aren't the same as their implementations), but Haskell typically outperforms Python which is arguably more imperative than functional.

    • @mskiptr
      @mskiptr Год назад +37

      @@thomassynths It's usually said to be in the same ballpark as Java or C#. Not really that surprising given it's compiled and they are JITed, all of them have a runtime system and all do GC.
      It _can_ perform terribly ofc. If you use data structures not fit for the job, implement a slow algorithm or your code doesn't play well with the GC or laziness.

    • @hardknockscoc
      @hardknockscoc Год назад +46

      ​@@marcs9451 I can't see my reply anymore. Maybe it got removed or maybe youtube is just having a fit. I'll just reiterate: functional languages are more open to optimization than imperative ones. And if you benchmark them, they perform incredibly fast compared to imperative. Just look at how Ocaml performs against Java. On top of this, the abstraction in which programmers think, doesn't need to concern itself with the underlying machine. That's literally the whole point of high level software. There's a reason compilers exist. Also Scheme is obviously not the only language with TCO lol. That's most functional languages. There are tons of compiler optimizations that a) improve cache locality and b) reduce stack usage. Rust is proof that you can have tons of functional stuff in a language while still keeping it fast. And Rust doesn't even have a runtime/GC. Just look at the logos crate: github.com/maciejhirsz/logos. This crate generates faster lexers than you could ever hope to write by hand and all you have to do is specify regexes for your tokens. You don't need to concern yourself with the algorithm used for lexing.

  • @TheEyalYemini
    @TheEyalYemini 10 месяцев назад +25

    This how you make code unreadable

  • @tokiomutex4148
    @tokiomutex4148 Год назад +423

    It's amazing how C++ allows you to turn straightforward programs into something very close to APL programs.

    • @v01d_r34l1ty
      @v01d_r34l1ty Год назад

      C++ is a double edged sword, and the further you run down the blade, the sharper it gets. You can either leave legacy code and cut yourself with hours of painful debugging, or you can use modern features and cut yourself with hours of documentation review.

    • @xBZZZZyt
      @xBZZZZyt Год назад +4

      why youtube doesn't show reply?

    • @tokiomutex4148
      @tokiomutex4148 Год назад +7

      @@xBZZZZyt it's broken

    • @AVX512
      @AVX512 Год назад +27

      it's amazing how bad programmers write bad C++

    • @jjtt
      @jjtt Год назад +6

      The name iota literally comes from APL, so yeah

  • @blarghblargh
    @blarghblargh Год назад +209

    "readability is a function of experience" this is the truest thing I've heard this week. how have I been programming for 30+ years and never, ever heard any dev say this? I say it all the time, and everyone just smiles and nods when I do, and acts like I need to get back to my fucking nursing home.

    • @marcossidoruk8033
      @marcossidoruk8033 Год назад +34

      Readability is a function of experience divided by how much your language sucks sqared.
      The C version is readable by an infant with neurological disorders, the Rust version is kinda nice, you just need to explain 2 things and it makes sense. The C++ version just doesn't make any sense, yes if you have experience you can read it but you shouldn't to when you could have just written it in C or in Rust and any random joe would have been able to read it.

    • @blarghblargh
      @blarghblargh Год назад +7

      @@marcossidoruk8033 "yes if you have experience you can read it" - this is all programming. that was the point I was agreeing with.
      don't shave yaks. get used to things that make you uncomfortable. eventually it'll all become easy.

    • @marcossidoruk8033
      @marcossidoruk8033 Год назад +20

      @@blarghblargh You didn't get the point at all.
      My whole point is that the C version is so much more explicit you barely need any experience or anything at all to understand it, its beautifully dumb code, compared with the C++ version is much more clear to a greater number of people and doesn't gatekeep people for being unnecessarily complicated and language specific, it is objectively better code. Hence why I said "it is a function of experience divided by how much your language sucks squared", the whole point is that experience is not the only relevant factor here.
      Saying as a response to this "thats all of programming" is a remarkably stupid answer. Its like saying "suffering is all of life", yes but does that mean that the ammount of suffering doesn't matter? Same thing here, with arbitrary experience you can understand anything, with arbitrary experience you can even understand languages like brainfuck or malbolge, yet nobody does that because why would you.
      For some reason people don't apply this logic to (modern) C++ and end up writing utterly pointless unmaintainable code like this just because it makes them feel smarter.

    • @blarghblargh
      @blarghblargh Год назад

      @@marcossidoruk8033 "a remarkably stupid answer". keep up this junior mindset and you'll never grow out of it.
      I never said C++ doesn't suck. I said it's all easy once you have experience. git gud, kid. and keep your negativity to yourself.

    • @notapplicable7292
      @notapplicable7292 Год назад +9

      > Writes a 10k line method
      > "readability is a function of experience"
      > okay grandpa

  • @maninalift
    @maninalift Год назад +159

    "iota avoids off by one errors.... Oh, by the way, don't forget to add one to top if you want an inclusive range"
    I like the code report channel but he's definitely more into arcane programming aesthetics than pragmatism.
    No idea why he decides to compare number of assembly instructions produced, its just as easy to benchmark the run time.

    • @yjlom
      @yjlom Год назад +12

      well code report just tries to write everything like it's APL

    • @TheMyHalo
      @TheMyHalo Год назад +10

      "iota avoids off by one errors.... Oh, by the way, don't forget to add one to top if you want an inclusive range"
      thats because every range is exclusive, every single one. Its not a hard thing to remember to youre always working on [a;b[

    • @isodoubIet
      @isodoubIet Год назад +8

      "iota avoids off by one errors.... Oh, by the way, don't forget to add one to top if you want an inclusive range""
      How is that not an improvement over the original where you had to notice that the range was inclusive by looking at a single character?

    • @voskresenie-
      @voskresenie- 3 месяца назад

      run time can be affected by tons of things other than the program itself. that's why when you run it 100 times, you'll get 100 different results. machine instructions is a more reliable comparison.

    • @maninalift
      @maninalift 3 месяца назад

      @@voskresenie- reliable in the sense that it's more likely to return the same result each time (even then, it will vary) but not a reliable indicator of run time performance.

  • @ThePrimeTimeagen
    @ThePrimeTimeagen  Год назад +9

    Watch the original: ruclips.net/video/wGCWlI4A5z4/видео.html
    Amazing author: www.youtube.com/@code_report

  • @micc1211
    @micc1211 Год назад +25

    Because he uses assembly size as his metric, O3 in C and C++ optimizes for speed, the compiler will unroll your loop and inline aggressively if it thinks it can make it faster and won't really care about the size of the code. The reason adding the various library functions reduces code size in O3 is likely because it adds a lot of things that have to happen to now work with arrays instead of just a few stack variables and therefore can't unroll your loop to make it all that much faster. While I can't say for sure, this might be slower when not threaded. So who knows how the speed is actually affected, you would need to actually benchmark it to know.

  • @grim.reaper
    @grim.reaper Год назад +368

    Prime: “c++ Longest way to say the shortest thing”
    Java: hold my beer!

    • @ThePrimeTimeagen
      @ThePrimeTimeagen  Год назад +68

      This is true

    • @joshix833
      @joshix833 Год назад

      With java it would be something like the follwowing:
      int calculate(int bottom, int top) {
      if (bottom i % 2 == 0).sum();
      }

    • @2mbst1
      @2mbst1 Год назад +195

      More like: Hold my FermentedAlcoholicBeverageBrewedFromMaltAndFlavoredWithHops.

    • @grim.reaper
      @grim.reaper Год назад +7

      @@2mbst1 🤣🤣🤣

    • @johnhilts5980
      @johnhilts5980 Год назад +57

      ​@NETSPLIT you forgot to add "Factory"

  • @aarorissanen930
    @aarorissanen930 Год назад +52

    9:25 As a C++ dev; What the actual fuck is happening?

    • @sillymesilly
      @sillymesilly 6 месяцев назад +4

      This is why C devs should of stayed and never migrated to C++

    • @linuxguy1199
      @linuxguy1199 27 дней назад

      ​​@@sillymesillyWhat? As a C developer we would never come up with such a descriptive name like std::views::iota, we'd call it orgextb

  • @brady1123
    @brady1123 Год назад +103

    That C++ calculate function is an abomination.
    Here is a non-nested version that doesn't hurt my brain (and doesn't have an early return even though it really should):
    int calculate(int bottom, int top) {
    int acc = 0;
    for (int val = bottom; val

    • @Evan490BC
      @Evan490BC Год назад +6

      If you are going to use a God damned C loop, at least use a range-based one...

    • @bloody_albatross
      @bloody_albatross Год назад +3

      What happens if top is the maximum integer? Like: calculate(INT_MAX, INT_MAX)

    • @catcatcatcatcatcatcatcatcatca
      @catcatcatcatcatcatcatcatcatca Год назад +3

      @@bloody_albatrossINT_MAX,INT_MAX should return 0.
      It just.. Doesn’t return.
      Im not sure if INT_MAX,INT_MAX should actually return zero or just throw, because whoever called this probably doesn’t actually want to know the sum of even numbers in the range of one odd number.

    • @bloody_albatross
      @bloody_albatross Год назад

      @@catcatcatcatcatcatcatcatcatca It was a rhetorical question hinting at a bug. ;)

    • @zytr0x108
      @zytr0x108 Год назад

      @@Evan490BCno

  • @david.cr96
    @david.cr96 Год назад +11

    When he mentions that he would need to relearn programming to understand Haskell: well, indeed you kinda do, and that is actually the point. Not a big fan myself of functional programming but one must recognize it is such an interesting paradigm and worth knowing (specially its theoretical foundation).

  • @remboldt03
    @remboldt03 11 месяцев назад +7

    The C code was much more readable then the C++ one

  • @boody8844
    @boody8844 Год назад +49

    As a javascript developer I have never used rust before and I have gotta say, I understood the rust version of the sum function really quickly by just looking at it for 30 seconds. I've got to learn rust, such a neat looking language!

    • @dealloc
      @dealloc Год назад +15

      sum is also way more descriptive than "accumulate". Accumulation could be done in a number of different ways. Sum does what it says on the tin. Add up and get a result.

    • @thekwoka4707
      @thekwoka4707 Год назад +5

      Definitely. Just made sense right away while the cpp and Haskell stuff was like "wtf?"

    • @DBGabriele
      @DBGabriele Год назад +1

      ​@@dealloc This is because `accumulate` is a function that iterates over any kind of iterator, rather than being a method (of a integer vector).

    • @dealloc
      @dealloc Год назад +1

      @@DBGabriele That doesn't explain why there isn't a sum method in C++ standard library (partial_sum is the closest but requires you to pass a range). Rust also has "accumulate" in forms of fold and reduce for more generalized accumulation (reduction).

    • @DBGabriele
      @DBGabriele Год назад +2

      @dealloc as already said, std::accumulate is "generic" while sum is specific.
      Since there is accumulate in std, sum is unnecessary and redundant.

  • @thomassynths
    @thomassynths Год назад +144

    To make the Haskell code more explicit, here it is:
    calculate :: Int -> (Int -> Int)
    calculate = \bottom -> \top -> sum (filter (\i -> even i) [bottom..top])
    This is literally equivalent to the code in the video because all functions in Haskell are implicitly curried. I also removed the dollar operator and made the even check more obvious that it is a function used for a callback.
    Anyway one major reason high-order functions are is not the Hellish Nightmare they are in JS is because all variables in Haskell are immutable and all functions are pure. (Haskell is able to model state and impurity through its type system and some primitive language-given types such as IO and ST.)

    • @guisande
      @guisande Год назад +27

      Another way to read it is just:
      Normal way:
      calculate bottom top = sum $ filter even [bottom..top]
      Way of reading:
      calculate(bottom, top) { return sum(filter(even, [bottom..top])) }
      The dollar sign is just a way of removing parentheses. So, instead of: sum (filter even [0..10]) you can write: sum $ filter even [0..10]
      Edit: I'm mostly writing this for prime

    • @biskitpagla
      @biskitpagla Год назад +5

      that, and the fact that the standard library is practically nonexistent in js in terms of data structures and algorithms.

    • @DryBones111
      @DryBones111 Год назад +9

      @@guisande Yep, to read it imperatively you go right to left. But it takes on a declarative meaning when read left to right, which is why the apply function ($) has its operands in that specific order.
      The declarative way of reading it is how Primeagen read it: "The sum of a filter of even elements from a range from bottom to top."
      The inclusive range is also a declarative approach to ranges since its essentially set notation from mathematics. The idea of the top of a range of integers being non-inclusive is something that's left over from an imperative loop approach using i < top.

    • @isodoubIet
      @isodoubIet Год назад +1

      @@DryBones111 " The idea of the top of a range of integers being non-inclusive is something that's left over from an imperative loop approach using i < top."
      Nope. Ranges being non-inclusive is the correct choice for any programming context because 1. the difference between the endpoints equals the length and 2. non-inclusive ranges compose together much more nicely since with two adjacent ranges the endpoint of the first is the first element of the second. Haskell is using the wrong convention.

    • @ДаниилРабинович-б9п
      @ДаниилРабинович-б9п Год назад +2

      @@isodoubIet nah, the correct choice is to have both with a syntax like `a..=b` and `a..

  • @Gabriel-wq4ln
    @Gabriel-wq4ln Год назад +101

    I can't believe there was a clean C++ program with LITERALLY a "for" and an "if" and the dude turned it into a program with functions which you gotta search what the parameters mean, passed functions as arguments to the said functions, declared a namespace (which you also gotta see what exactly it is) and brought a lot of new syntax that only works on the last versions of C++. It also takes 10x more to understand the "upgraded" version.
    Btw, love your videos, Prime
    (curly braces on new line lol)
    {
    }

    • @jasonwhite5578
      @jasonwhite5578 Год назад +27

      preach it - overengineering at it's best

    • @thomasziereis330
      @thomasziereis330 Год назад +13

      What do you mean the code was absolute garbage and the final c++ result was readable like a comment it literally written: sum all numbers from top to bottom that are filtered to be even. You can understand this code in like 5 seconds where you would still be figuring out the control flow of the original example.
      Ofc its not as beautiful als rust but its basically readable the same way

    • @Gabriel-wq4ln
      @Gabriel-wq4ln Год назад +29

      @@thomasziereis330 I mean, if you prefer it, alright. I still think the original one is cleaner. What about having to check what tf the iota function does?

    • @isodoubIet
      @isodoubIet Год назад +2

      @@Gabriel-wq4ln Iota has been an algorithm in the standard library for ages, every c++ programmer should already know it

    • @mrcrackerist
      @mrcrackerist Год назад +17

      @@thomasziereis330 it took me less then a second to understand the C code

  • @ionized8744
    @ionized8744 Год назад +194

    Considering how much you love rust I really suggest at least giving Haskell a chance. On it's own it's not the most useful language but it is really elegant and fun when written properly. And for some use cases, such as compilers, it is crazy good

    • @ea_naseer
      @ea_naseer Год назад +30

      Rust language creator even says the syntax is ML inspired

    • @Boxing_Gamer
      @Boxing_Gamer Год назад +10

      Haskell is brilliant but there's some annoying stuff in it. For example all the strange characters you can define to mean what you want. Good look understanding someone who went crazy using monads and the entire hierarchy of category theory using symbols and one liners. This is why it will never be really popular. I feel like they need to simplify it a bit, at least the syntax. Also there are annoying thing like name clashes when importing other Haskell files. Rust is easy and efficient to use which means a lot.

    • @romannasuti25
      @romannasuti25 Год назад +4

      @@Boxing_GamerI feel like Haskell is properly a research lang, perfect for exploring programming languages at depth but not for actually programming. If you need something practical right now that feels the same, use OCaml for a pure experience or Rust if you’re a performance junkie. If Unison ever gets off the ground, that’s much closer to pure Haskell with a lot of QoL improvements.

    • @Boxing_Gamer
      @Boxing_Gamer Год назад +1

      @@romannasuti25 I'm not sure, I think the ecosystem, compiler and package manager are very mature. Why not make projects with it? I know there are companies out there who use Haskell.

  • @seancpp
    @seancpp Год назад +20

    Wait, you *don't* like seeing 2 or 3 ternary operators nested together with a bunch of inline lambda expressions? Log debugging is in the past, just guess the problem correctly the first time

  • @vnshngpnt
    @vnshngpnt Год назад +21

    30:39 It's semantically equivalent. Yes syntax is different, but semantically you're just composing functions. And yeah, as other commenters said, Haskell can be weird, but it can teach you *a lot* about programming and give you many "WHOA" moments, once you get through initial learning curve.

  • @willful759
    @willful759 Год назад +49

    Expanding a bit on the haskell syntax, indeed haskell is a language that requires you to think very differently about programming, and as such, its syntax tends to look ugly, but once you start using it and understanding it, you can see that it is actually quite clean
    in haskell, functions are king, and as such, the syntax is worked around clean composition and use of functions ( to various degrees of success )
    ignoring type signatures, functions are defined by naming them first, then listing the parameters
    calculate top bottom
    then, to apply it, you do the same
    calculate 1 100
    this does cause a problem, filter takes a function to use as a filter, and the list you're gonna filter, if you tried to just list all of the arguments
    sum filter even list
    the parser thinks you're trying to apply sum to three arguments, in c-like terms: sum(filter, even, list)
    to solve this, you could just use parentheses
    sum (filter even list)
    but we also have the $ operator, which is simple function application (f $ x = f (x))
    sum $ filter even list
    since $ is an operator, the parser now knows that filter is a function, and even and list are the arguments, and now it type checks neatly
    this does map directly to the rust solution:
    [bottom..top] = (bottom..=top)
    filter even [bottom..top] = (bottom..=top).filter(even)
    sum $ filter even [bottom..top] = (bottom..=top).filter(even).sum()
    as an extra note, the type system in haskell also tends to have an intimidating syntax, but is is because the type system is very rich and expressive
    for example, epressing filter in rust would look something like this (ignoring whatever complications might arise from lifetimes and such)
    fn filter (f: T -> bool, vect: Vect) -> Vect { … }
    while in haskell it would be:
    filter :: (t -> Bool) -> [t] -> [t]
    filter f list = ...
    (yes there is a reason for the arrows but this comment is very, very long as is)
    which one you prefer is up to you, but imo when you move functions left and right, haskell's syntax is quite ergonomic, while the parentheses of c-like languages gets in the way

    • @LtdJorge
      @LtdJorge Год назад +2

      The rust function would actually be:
      fn filter (func: fn(T) -> bool, vec: Vec) -> Vec{}
      :)

  • @somosworld
    @somosworld Год назад +4

    there's a big difference between higher ordered functions in haskell and javascript. one has a world-class compiler the other is a dumpster fire. debugging haskell is a wet dream. there's no state everything is pure. wake up.

  • @cas1652
    @cas1652 Год назад +59

    Kotlin is also very nice:
    fun calc(bottom :Int, top: Int) = (bottom..top).filter { it % 2 == 0 }.sum()
    Differences to Rust:
    Return type is inferred and you can leave out the braces if your function is just one statement
    'it' is always the name of the first unnamed lambda parameter

    • @mv2e19
      @mv2e19 Год назад +6

      Agreed! Very similar to the Rust solution. Though, I usually explicitly declare the return type for readability

    • @kyay10
      @kyay10 Год назад +13

      `it` is not for the first argument of a lambda, it's for the *only* argument of a lambda

    • @JanuszKrysztofiak
      @JanuszKrysztofiak Год назад +6

      Kotlin's syntax for lambdas is pretty clever, given how it eases creation of quasi DSLs.

  • @mpogrzebski
    @mpogrzebski Год назад +96

    Love the steady stream of new videos on this channel ❤️

  • @heron619
    @heron619 Год назад +58

    I just learned the term "React Andys" from chat and I'm gonna use it to harass Theo and his audience!

  • @NithinJune
    @NithinJune 10 месяцев назад +2

    40:30 if you’re curious he uses powerpoint
    it’s confirmed

  • @trondenver5017
    @trondenver5017 Год назад +12

    O3 inlines more functions into the call site, which increases instruction count but *almost always speeds execution

  • @NithinJune
    @NithinJune 10 месяцев назад +2

    20:18 someone in chat said “how do we not have is_even [the node module] in rust” 😂😂😂

  • @Parker8752
    @Parker8752 Год назад +16

    So, Haskell can be a bit of a brain bender.
    Every function in Haskell takes one parameter and returns one parameter. A function which takes more than one parameter is effectively a function which takes in one parameter, and returns a function which takes in all the other parameters (this happens recursively). So to use the example in the video, Int -> Int -> Int means that it takes an integer and returns a function that takes an integer and returns an integer.
    Higher order functions in Haskell are pretty nice for the simple reason that in order to deal with mutable state, you need to use monads (basically a means of passing the current state as a function parameter, and having the new state be returned). This means that if there's a bug involving mutable state, you automatically know which subset of functions it must be in, because you had to declare the use of the monad; much like how unsafe in rust reduces the surface area for memory or concurrency bugs.

  • @AndrewBrownK
    @AndrewBrownK Год назад +13

    The Haskell code is beautiful, I wish you could appreciate it 😅

  • @ColorfirePluma
    @ColorfirePluma Год назад +40

    Honestly, the most cursed thing about the C++ example is the fact that *in C++20* they didn't even have a ranges overload for accumulate... or a general fold function

    • @Evan490BC
      @Evan490BC Год назад +3

      Ranges are not done yet.

    • @isodoubIet
      @isodoubIet Год назад +6

      The committee forces a 3-year cadence for language releases because apparently they're afraid of repeating the success of C++11

    • @JanuszKrysztofiak
      @JanuszKrysztofiak Год назад +5

      It's moving a bit slowly: design by the comittee and then waiting until tooling actually implements now. It's 2023 and C++20 is still not fully implemented. For instance, the compilers mostly support modules but the build systems are far behind. The barebone support for coroutines is there, but, again, the library support is off and one doesn't want to use it without libraries, because it is quite low-level and meant for... library creators implementing useful, easy-to-use stuff on top of it. One single thing about C++ I hate most is building. Whereas elsewhere it is matter of specific dependencies, in C++ it is a game of makefile generators on top of makefile generators trying to guess where dependencies are, these can from the OS or some package manager or manual inclusion.

    • @au._.79
      @au._.79 Год назад +1

      C++23 has std::ranges::fold algorithms

  • @bloody_albatross
    @bloody_albatross Год назад +15

    About the C++ code: The initial for loop was perfectly fine! Easy to understand, easy to optimize by the compiler.
    Anyway, there is a problem with the C and C++ versions. The problem is when top is the maximum integer value. Assuming it is really running for that long the C version will overflow and never stop. Always cycling all the numbers. The C++ version starts by adding 1 to top and as such will immediately overflow and immediately stop (will do one loop if bottom is the minimum integer value). In reality that case might not happen, but maybe it can happen if user control the input and that situation would cause some security issue or something. So depending on your context it might be a good idea to handle that case. Hope I got that all right in my head.
    Rust ranges seem to be handling that case correctly when testing it on Rust Playground.
    And the difference between the Rust and Haskell version is just methods Vs functions. The -> syntax for the function type is maybe a bit weird when you see it the first time. In Haskell all functions are curried. Meaning if you pass in less parameters than possible you just get a function (closure) back that takes in the rest of the parameters. The -> basically means returning. As such the calculate function is a function that takes in an Int and returns a function that takes in another Int and returns an Int. A bit weird, yes, but handy if you like this high level stuff. E.g. IIRC you can define sum like this: sum = foldr (+) 0
    Note that there is no parameter in the definition of sum because foldr takes 3 parameters, but I only pass 2 here. Meaning there is one remaining parameter: The list of numbers to sum. The product function would be: product = foldr (*) 1
    foldr is usually called reduce in other languages. There is also foldl. It's about from what side the list is processed. Since addition and multiplication are commutative that is not important here, though I think foldr has better performance in Haskell because of lazy evaluation. Haven't touched Haskell since university, which was more than a decade ago.
    Btw. IIRC Haskell and currying is both named after the same person: Haskell Curry. So of course everything is curried in Haskell! XD

  • @kametsu.
    @kametsu. 6 месяцев назад +3

    I watched this video year ago and thought "how would anyone on earth prefer his last C++ refactor over starting code" and now rewatching it after doing C++ and a lot of declarative programming stuff in Haskell/OCaml/Elixir and overall applying more declarative style of code for example when using Python I now find that version a lot more understandable and readable.

  • @haskellelephant
    @haskellelephant Год назад +15

    In haskell the right to left reading is because of function application. It's like doing f(g(x)), the g function happens first.

    • @fsharplove
      @fsharplove 9 месяцев назад

      @@nisonaticF# version (you can use a List instead of Seq):
      [bottom..top] |> Seq.filter(fun i -> i%2 = 0) |> Seq.sum

  • @Jplaysterraria
    @Jplaysterraria Год назад +11

    The big problem with `else` and `else if` statements is that the condition to execute the code block is not at the start of the statement like for `if`s, so you have to mentally juggle with negating the `if` condition and applying the extra conditions from the `else if`.
    tl;dr: else's have code which is run on an implicit condition, unlike `if`s where the condition is explicit.

  • @igrb
    @igrb Год назад +196

    As the official intern (and future CEO) of The Startup™I'm legally obligated to say I'm loving this channel. A chance to watch something I missed from my genius CEO??? Perfect way to try harder.

    • @ThePrimeTimeagen
      @ThePrimeTimeagen  Год назад +50

      LETS GO!!
      the CEO is going to put a good word in for you with Karen R.

    • @rumplstiltztinkerstein
      @rumplstiltztinkerstein Год назад +8

      I'm praying for your promotion to CEO intern of the startup soon Igor

  • @yaksher
    @yaksher Год назад +3

    What the assembly comparison tells is that the early things all compile just fine and the compiler understands exactly what is going on, so when you compile with -O2, it can make things tiny, and when you compile with -O3, it can unroll the fuck out of everything and makes things ~blazingly fast.~ On the other hand, the fancier stuff where there's less of a difference, the compiler no longer understands what is going on and so it can't make the optimizations you would want out of it.

  • @sortof3337
    @sortof3337 Год назад +22

    i love haskel. It was one of the first langues i was taught. I am math person and it just makes sense to me. haskell programs read like proofs. i love rust too, been learning it for 6 years now lol. But I write cpp code for my telecom job, I must say that cpp code was just pp move. I work with deveopers that do this in real life, it just looks downright obscure and ugly to me. It is a nightmare to onboard new people if anyone in team write codes like that and doesnt write comments. that stood was awful as well. lol.

    • @isodoubIet
      @isodoubIet Год назад +2

      "that stood was awful as well."
      Extremely common pronunciation in the community, probably the most common. From your post it seems like you write C++ as C with classes. You should stop resisting the coworkers who want you to do better.

    • @gristlelollygag
      @gristlelollygag 11 месяцев назад

      nah@@isodoubIet

    • @iqwit
      @iqwit 8 месяцев назад

      ​@@isodoubIetSay what you want about C, but you don't need to look through 5 million std functions documentation just to understand 1 code snippet

  • @realmimak
    @realmimak Год назад +8

    37:16 a call to a library function itself adds an overhead of a few instructions, like stack push/pops, call/jump instructions, returns etc. which can vary based on the calling convention

  • @gogl0l386
    @gogl0l386 Год назад +23

    This has been an emotional roller coaster as a mathematician (so haskell feels the most natural for me).
    Like first he gets mad of the type declaration thingy and I'm like "wtf programmers don't know currying?" but then he gets it an appreciates it which is nice.
    BUT THEN HE SAYS "I wOulD hAndS dOwN haTE debUGGinG haskell".
    Bruh everything is pure functions and type-safe. It is literally the debugging dream.

    • @nan0s500
      @nan0s500 Год назад +5

      And we all know you cannot have bugs in pure, type safe functions.

    • @gogl0l386
      @gogl0l386 Год назад +3

      @@nan0s500 Mu point is that if you want to debug then a pure type safe function is the dream as you can just test all values you wanna test without having to think about anything else than it should return the right values and you can trace back exactly the source of the error as there are no global variables that you need to keep in mind.

    • @nan0s500
      @nan0s500 Год назад

      @@gogl0l386 Fair enough

    • @psd993
      @psd993 8 месяцев назад

      @@gogl0l386 As he said, you'll have to print everything to see where the bug is. And that is probably what's annoying about it.

  • @markosth09
    @markosth09 Год назад +98

    For those wondering iota is a term borrowed from APL, where there is a glyph represented by iota which gives an exclusive range from 1 to n. It is used like ⍳5 and this would give the sequence 1 2 3 4. It is represented by iota because the proper name for this is interval or index generator, with iota being the equivalent to the English letter i in Greek

    • @poppyjepson8906
      @poppyjepson8906 Год назад

      wow cool reference, what is this a marvel crossover or something? xD
      (why cant cunts just name it "range" or "inclusive range" or at least std::aids::inclrange)

    • @mskiptr
      @mskiptr Год назад +23

      This is cursed, but I guess I see why code_report thought it's so intuitive and readable

    • @markosth09
      @markosth09 Год назад +19

      Also code_report does a lot of APL(his favorite language), so this all seems very intuitive

    • @sebastiangudino9377
      @sebastiangudino9377 Год назад +11

      I mean, in the context of APL iota is a beautiful operator. It's just kinda bloated for C++. But the fact that it is even there just show some capabilities to perform declarative programing in C++. That's IMO kinda nifty

    • @isodoubIet
      @isodoubIet Год назад +5

      No idea why people keep copying syntax from esolangs but w/e

  • @benjaminlieser8148
    @benjaminlieser8148 Год назад +3

    There is actually a second "bug" connected to inclusive ranges.
    for(int i = bottom; i

  • @Yupppi
    @Yupppi Год назад +2

    I see the thumbnail and know it's Conor Hoekstra. His algorithm videos are just satisfying as hell. His convention talks are also just intense. And guy who does APL for fun must be pretty serious about languages. Sean Parent started it all, really in 2013. Then Baccara with 105 algorithms in 2018, and since then Hoekstra.
    They've been going on about how the STL has had algorithms that do things faster, more describingly and with less room for error since 98 and people still in 23 act like you spelled Voldemort if they see a single algorithm instead of bunch of indexed nested for loops. The message being: it's been 10-20 years, it's time you got more familiar with the STL algorithms so you didn't need to be a shocked assembly coder because it's not what you're used to. It's not at all that bad if you spend a little time to know the algorithms and becomes much more readable. Similar to the way Rust is often written with multiple methods ending up with something descriptive and pretty neat. Scary at first when you don't know it, but then rather satisfying.
    But I loved the whole chat going "this is not readable, it literally says accumulate, filter even while the previous code said for for if for which was painstakingly obvious to me what it was doing, personally I love messing up my for loops for trivial details". Like people don't get shocked when they see something like .reverse() or pow(a, b) or sqrt(). Or for god's sake a function call someone custom made that's all new to them. I would argue though that his reasoning is pretty solid about the bug, it's not just unclear intention if you write two loops contradicting in behavior, it's something you didn't pay attention to and make consistent. But I surrender that it's not guaranteed it was meant to do what he concludes.
    Basically every speaker in C++ says stud and it's weird. And I also wish everything convenient in C++ wasn't std::blahblah::yup::right::plus. For some reason they went with "iota" when they could've called it something like "range". It's literally just generating the range for you to iterate over. The beauty of Rust really is that no legacy to carry over and no need to follow ISO standard, it could just make this one so nice. But as long as you have to deal with C++, it's kinda nice to have some of the neat features implemented less nicely in C++ than not at all.
    Personally I'm still trying to understand the need for (|w| w.operation) and understand which and when does mutate the original and what makes a copy or something so I could intuitively write it correctly. And at some point I questioned why you have to always do the |w| manually when you don't have an option to not do it and it could just be done for you by the compiler, but I assume there's a good reason and exceptions. But Rust really doesn't allow you to do stupid things like manually configure iteration loops like you'd intuitively do, it allows you to do simpler and better things instead. Like I was really lost with rust vectors like vec.iter().map(|v| *v * *v).collect() or something, but it was also beautiful how .collect() just deduced the correct return type despite same syntax, different types. The closures were just unfamiliar and I didn't see the reason, but that's a lot of things with Rust: they're new and confusing because your previous language intuition might lead you astray if you don't read how to use the features. But the collect() with ints and strings, it just worked and once you got it into your head that with vector you use .iter() and you have to .map() it, you can even throw a function call inside the .map(). It was so nice even if a bit weird at first. I also saw something about enums in Rust and how they're rather different and powerful, almost like classes, but I haven't still really understood them.
    But people should be really scared if they don't like seeing this, Hoekstra is a research scientist at Nvidia. His work is quite literally in all of our computers sooner or later. You explained very well why the algorithms exist and are used, and what's the caveat of using them sometimes. Particularly on the Rust example about iterators.

  • @mskiptr
    @mskiptr Год назад +107

    I would actually enjoy seeing Primeagen trying to learn Haskell. Or well, something smaller like Elm or Idris.
    (Both are basically Haskell but done from scratch, with the focus placed on a slightly different use)

    • @ccgarciab
      @ccgarciab Год назад +1

      I've done some Haskell, but I have only heard about Idris. Isn't it harder with the dependent types?

    • @mskiptr
      @mskiptr Год назад +3

      ​@@ccgarciab I'd say that they make the type system way simpler. Or maybe sleeker would be a better word.
      Tho its learning resources are sparse and usually assume you already know Haskell.
      I guess I would say that while some Idris features are more advanced and harder then what Haskell provides within the language, there are many topics in Haskell that are way more theory-heavy than what Idris adds. Still, these topics usually apply to Idris too.
      And finally, Idris tooling is basically limited to the compiler|interpreter and some editor integration. Though this includes being able able to write code semi-automatically (with a couple shortcuts to generate partial definitions, case split, search for a correctly-typed implementation, etc.)

    • @ccgarciab
      @ccgarciab Год назад +1

      @@mskiptr That sounds promising, maybe I should put Idris closer to the top of my todo pile

    • @mskiptr
      @mskiptr Год назад +1

      @@ccgarciab Btw, one pretty nice introduction to the language is a series of four lectures from 2017. You can find it by looking up "OPLSS Idris".

    • @anj000
      @anj000 Год назад

      I would like to see Prolog

  • @NithinJune
    @NithinJune 10 месяцев назад +1

    9:30 i think in cpp23 you can get rid of the empty capture group

  • @HelloThere-xs8ss
    @HelloThere-xs8ss Год назад +19

    The comment section is always like having -Wall -Werror flags on.

  • @andrez76
    @andrez76 Год назад +9

    Ah, C++; the language everyone loves to hate. True, there are a lot of things that come out of the standards commitee that make you go "what?!". On the other hand, it's ~sad~ funny to see how little people know about the language before criticizing it.

  • @noxagonal
    @noxagonal Год назад +8

    I'm mostly with you on this. I really like C++ though. I don't care about iota or filter much. I do like lambdas and some of the helper functions if they make the code more clear. I don't like operator overloading when it changes meaning depending on the context, it's fine if it's consistent across C++. I guess it's good we're all a little different, find the flaws in each other. XD

  • @simonachtnich4417
    @simonachtnich4417 Год назад +3

    You could flip the first if statement and return early in the case (top

  • @gtgunar
    @gtgunar Год назад +5

    You can manually reason about what constitutes the count of evens in a range, and find out that you do not need loops, just a few rithmetic steps. You calculate the half of the length of the even aligned subrange, and then correct it with the endpoints. Simple constant time goodness.
    int calculateCompact(int bottom, int top)
    {return !(bottom%2) + top/2-bottom/2;}

  • @linuxguy1199
    @linuxguy1199 27 дней назад

    37:18 The C compiler likely either A) inlined the for loop, B) replaced the inside of the for loop with some mess of SIMD instructions, or C) done both.

  • @Chalisque
    @Chalisque Год назад +21

    The Int -> Int -> Int stuff makes more sense when you learn currying. If we had, say
    myMultiply x y = x * y
    then
    myMultiply x
    would give you a function that took one parameter, y, and multiplied it by x. That is, it is equivalent to
    \y myMultiply x y
    In e.g. Python, we have to write something like
    lambda y: myMultiply(x,y)
    It comes from the maths roots of Haskell. Things like if f:X→Y is a function and g:Y→Z is a function, then (g.f) is a function (g.f)X→Z where (g.f)(x) = g(f(x)). Haskell is a bit of a 'programming language for mathematicians' at times.

  • @noredine
    @noredine 9 месяцев назад +3

    He took the C code and made it into a tangled ball of words

  • @Acetyl53
    @Acetyl53 Год назад +12

    You can't always just rename to cpp. Things like no implicit cast from void* breaks malloc, realloc, etc. There are some other oddities (with function pointers iirc) as well, but that's the primary one.

    • @QuantenMagier
      @QuantenMagier 8 месяцев назад

      Yeah they fucked up the C++ standard and compilers not to be synced any more with the C standard, but in former times every C code was also C++ code and C++ was just compiled to C before being compiled to assembly.

  • @oct_nate
    @oct_nate 3 месяца назад +2

    8:31 it's iota because it's actually atoi backwards 🤔

  • @lucaslugao
    @lucaslugao Год назад +3

    God, that C code could have been so much simpler:
    int calculate (int bottom, int top){
    Int sum = 0;
    for(int i = bottom; i

    • @mortred4144
      @mortred4144 2 месяца назад

      I agree, but I also think sum += (i % 2 == 0) ? i : 0; is more readable to sum += (i % 2 == 0) * i; In either case I wonder if the compiler recognizes adding 0 (in the first case) and multiplying by zero (in the second case) and optimizes these instead off actually performing these operations

  • @MAcDaTHo
    @MAcDaTHo 5 месяцев назад

    @8:08 "Iota is the smallest character, I think in greek"
    Alpha: "Am I a joke to you?"

  • @kellybmackenzie
    @kellybmackenzie Год назад +3

    I love Haskell so much, you should really give it a chance!! It's such a beautiful language

  • @RootsterAnon
    @RootsterAnon Год назад +2

    Heyyyyyyy this was my "Prime should react" video suggestion! I'm so happy that this was on yt now since I missed the stream due to other stuff I had to do. Awesome, had a blast watching it!

  • @RuslanKovtun
    @RuslanKovtun Год назад +4

    Comparing number of asm instruction is as meaningful as comparing number of lines in indian code to number of lines in "normal" code.

  • @GamingBlake2002
    @GamingBlake2002 Год назад +1

    3:28 This code doesn't hurt to read because putting curly braces on a new line every time is inherently bad. This is the convention I adhere to because it creates a nice visual distinction between a line that defines a block and the block itself. The issue is when people follow it religiously; ignoring cases when it makes the actual code too sparse. Although I follow this convention, I would still write that particular function like this:
    int calculate(int bottom, int top)
    {
    if (top

  • @moodynoob
    @moodynoob Год назад +5

    This brought back trauma of writing Haskell applications back in uni. Rust really looks like a practical implementation of functional concepts

    • @dancan4949
      @dancan4949 9 месяцев назад +1

      Haskell is a purely functional language while most programming languages are just imperative with some functional features. The thing with such a language is that it basically never sees any practical use outside of academia where it is used as a proof of concept. It is a very beatiful language however.

  • @bravegrumpy689
    @bravegrumpy689 10 месяцев назад +1

    So the original video used syntax explicitly meant to be language agnostic.
    All Turing complete languages have control flow, functions, and loops.
    While the code happened to have been C, the original code snippet is also runnable Java code.
    So the advice in the original video was helpful for me when thinking about my spaghetti-code python script.
    It also works for refactoring React components. And for simplifying my MATLAB scripts. (Data-science/ChemE background)
    Or for finding a bug in Julia. Or for simplifying and refactoring CSS.
    The refactor using rust or cpp specific features does not do as good of job getting the core programming concepts across as the code becomes unreadable by somebody using an unrelated language. Or by somebody using a DSL.
    Most reasonably modern programming languages have inspiration from C. So C can often look like pseudo-code, and therefore can be used as an example to talk about a more fundamental concept. While refactoring using modern language specific tricks is cool, I think it misses the point and reduces the audience that can accept the lessons about a happy path, early returns, and using functions when a script gets too messy. The original video is closer to a moderate and useful distillation of the lessons learned from clean-code.

  • @T33K3SS3LCH3N
    @T33K3SS3LCH3N Год назад +3

    Me at university: I love C++, it gives me so much control! Sure some concepts like pointers are hard for some people, but they're fundamental to programming.
    Me seeing actual C++ production code: Yeah no I'll never use this language again wtf

    • @everynametaken
      @everynametaken Год назад +1

      C is amazing, C with classes is also amazing. The things people turned C with classes into... Ye gawds.

  • @LoZander
    @LoZander Год назад +1

    Readable really is a case of what you're used to looking at. I'm fairly used to mapping and filtering over collections in the kind of manner shown around 18:31, so to me it is pretty readable. I find it nice to look at. I would prefer the return early approach to the branching too, but the rest reads easily for me. I read the accumulate body something like this: ACCUMULATE over a RANGE from bottom to top + 1, and then keep the entries that are even.

  • @pranavbadrinathan6693
    @pranavbadrinathan6693 Год назад +8

    I'm learning Racket here at University, a functional language similar to Haskell, and debugging is a nightmare. Recursion is such a prominent feature of a functional language, and it is very efficient, but very also hard to debug. We have a stepper to step through the code and it always explodes in lines of code being executed on each recursive call, making it hard to read and actually debug lol.

    • @geoffl
      @geoffl Год назад +1

      learn how to do it by hand, it will help you _see_ what's going on

    • @pranavbadrinathan6693
      @pranavbadrinathan6693 Год назад

      @@geoffl Alright I'll give it a shot. Anything to make it easier. Thanks.

    • @rogergalindo7318
      @rogergalindo7318 Год назад +2

      dont know about racket, but in my experience with haskell, the type system really helps, and though at the beginning it was very difficult, later it isn’t an issue
      that is, with modularity and clarity of what your functions are doing in your loops

    • @ipodtouch470
      @ipodtouch470 Год назад

      Racket is damn hard to debug just because of all the parenthesis 😂

    • @jacksonmagas9698
      @jacksonmagas9698 Год назад

      northeastern student?

  • @kuhluhOG
    @kuhluhOG Год назад

    8:40 For those asking for why it's called iota:
    In the ALP programming language, the function ⍳ (literally just the Greek iota character) has the same behaviour of the iota function in the standard library.

  • @jasonwhite5578
    @jasonwhite5578 Год назад +4

    On the C++ example, am I the only one who thinks that the original for loop usage was hands down the most readable? (@6:17) Every single refactor after that using the "new/fresh/modernized" features of C++ were just completely overengineering a very simple piece of code. For loops are so simple, why overcomplicate them, he probably made the code slower too, I'd bet.

    • @xBZZZZyt
      @xBZZZZyt Год назад +2

      no, you are not the only one, the other one is me
      original didn't use any functions (good because no need to search internet)

  • @charlesd4572
    @charlesd4572 Год назад +1

    Optimisation typically will produce more instructions when dealing with loops (unrolling - essentially reduces the number of cmp and jmp instructions by making explicit instruction repetitions with the incremented pointer position or register[] value).
    If you're using library functions your assembly will be smaller as the looping is now reduced to a single call instruction (plus a few mov instructions for arguments) because the the loop work is done elsewhere - unless the functions are inlining the assembly.
    So the number of lines of assembly does not map to performance for a single function or executable. Furthermore, with CISC (e.g. x86) you can use the same complex instructions such as mul (multiply) and how you supplying the source and destination data can affect how well this performs. With modern CPUs how you supply data to certain instructions can affect how CPU graphs your instructions and this can affect execution speed. So again compilers may use a more verbose set of instructions because it helps the CPU firmware map your code more efficiently.
    How do I know this - because I code in C! Grrrrrrrr..........................

  • @braincruser
    @braincruser Год назад +3

    13:30 Imagine you having to teach a junior programmer to read this code.

  • @spwim
    @spwim Год назад +2

    More assembly could lead to faster code actually.
    Say you have a slower general way of solving an algorithm that is quite slow,
    you could branch/match/switch depending on the actual input at runtime and choose an optimised algorithm based on the actual input.
    of course every branch needs it's own code leading to more lines but in the end you'll be executing quicker.

  • @banzobotic
    @banzobotic Год назад +20

    Looking at the generated assembly code he shows, there are some interesting observations I made.
    Due to the way he used godbolt the compiled rust code didn't even include the calculate function since the results were able to be calculated at compile time.
    The C and C++ code also calculated the result at compile time, but still included the calculate function.
    O3 produced more instructions than O2 because it made an attempt to vectorise the code.
    Using std::accumulate prevented the C++ compiler precomputing the result of calculate(5, 12) when using O2, although it still managed it when using O3.
    Trying to compare the speed of languages by looking at the number of generated instructions is flaky at best, but even worse when the code examples used can be trivially precomputed.

  • @asdfghyter
    @asdfghyter Год назад

    7:57 I don't know for sure the origin, but my guess for the horrible reason why it's called iota is that the name is borrowed from the write-only programming language APL, where each operation is a single symbol and which used tons of archaic symbols, including much of the greek alphabet. The greek letter iota was used to generate a range of numbers.
    and since APL code is infamous for being difficult to understand, the C++ people didn't want to be any worse, so they took inspiration from APL's function names to help making C++ as incomprehensible as possible

  • @rogergalindo7318
    @rogergalindo7318 Год назад +37

    once you learn haskell, the code reads almost like poetry, though, the problem is learning haskell… haha gotta say, it’s a bit difficult, but it expands your mind like you never thought it would

    • @xBZZZZyt
      @xBZZZZyt Год назад +9

      is poetry readable?

    • @phazechange3345
      @phazechange3345 Год назад +6

      Learning Haskell to expand your mind is like diving in to flat earth communities to expand your mind. Like, yeah, you learned new things, but those things are all useless.

    • @enzoqueijao
      @enzoqueijao Год назад

      ​@@phazechange3345 What about the Rust code that uses the same functional programming principles as Haskell?

    • @phazechange3345
      @phazechange3345 Год назад

      @@enzoqueijao I'd suggest that the rust implementation demonstrates that the very few loosely inspired by functional programming concepts are not functional in nature, they're just more often found in FP (ptooey) due to the earlier industry forming around OO (ptooey).
      One might view compiler level support for tagged unions (Algebraic Data Types) as an obvious step forward from C upon acknowledging that OO (ptooey) is bad, and the same goes for support for functions as first class citizens.
      Rust also borrows concepts more frequently found in OO (ptooey), such as tight grouping of state and behaviour, as well as their version of encapsulation.

    • @perigord6281
      @perigord6281 Год назад

      Learning Haskell makes Rust feel that much more clunky lol. Especially when it comes to state and error handling. Though Rust is a lower level language so that is to be expected.

  • @fg786
    @fg786 26 дней назад

    20:00 Not at all. I mean you have to kind of guess that bottom..=top is a range of numbers. No idea what |e| means specifically but you get the meaning, because the range gets filtered with %2 and summed up.
    26:00 wouldn't you just write (bottom..top) to recreate the initial code? Or will this then cause an error as it's not handled by the "range object"

  • @marlls1989
    @marlls1989 Год назад +5

    I learnt Haskell before learning rust and I don’t regret anything

    • @olaniyanayodele5986
      @olaniyanayodele5986 Год назад

      Do you recommend the same or just going for rust directly?

    • @marlls1989
      @marlls1989 Год назад +3

      @@olaniyanayodele5986 depends on your inclination, I don’t think the order alters the product here. Maybe learning rust first might be less daunting and them later learning Haskell as a step up on your game

    • @marlls1989
      @marlls1989 Год назад +3

      @@olaniyanayodele5986 I learnt Haskell as a summer project on the first year of my masters. After getting annoyed with Python’s type system.

  • @MikkoRantalainen
    @MikkoRantalainen 8 месяцев назад

    25:09 I think this is just another example why early return is so good. It declares an edge case and clearly implements special handling for that.

  • @justgame5508
    @justgame5508 Год назад +4

    Don’t program in rust (want to learn) I’m a C# dev and that rust code is 100% readable. We have things in C# virtually the same so that helps, but any developer should be able to look at that and understand what it’s doing regardless if their primary language

    • @rusi6219
      @rusi6219 9 месяцев назад

      Then what's the point of rustranies pushing Rust when it's just a copy-paste C#

  • @martijn3151
    @martijn3151 Год назад +2

    Tl; dr; good to see that a lot of this stuff is just syntactic sugar. Modern compilers are usually much smarter than their users and can optimize out a lot of unnecessary branching, so you can focus on writing readable and maintainable code. Putting this whole function in a single line of C++ code, make my stomach hurt.

  • @fredbcruz
    @fredbcruz Год назад +3

    I'm so happy that except from types and the range rust's example is pretty much js TODAY

  • @MikkoRantalainen
    @MikkoRantalainen 8 месяцев назад

    19:50 100% agree for explicit return! I even write the keyword return for my lambda functions just to make it extra clear what's happening.

  • @דניאלאביב-ו6ת
    @דניאלאביב-ו6ת 8 месяцев назад +3

    // arithmetic serie approach
    fn calculate(bottom: i32, top: i32) -> i32 {
    let first = bottom + (bottom % 2);
    let last = top - (top % 2);
    let total_elements = (last - first + 2) / 2;
    return ((first + last) * total_elements) / 2;
    }

    • @oliverglier8605
      @oliverglier8605 6 месяцев назад

      Thanks. I can understand this was an educational example, but it distracted me throughout the entire video that the code was crazy from the start. Also I wonder if we have lost all common knowledge of the theory and history of our profession?

    • @tbkih
      @tbkih 2 месяца назад

      Dammn finally someone posted this

  • @yash1152
    @yash1152 Год назад

    30:07 32:00 that top definition is for "haskell currying". ur instinct of "Int, Int -> Int" requires the fxn to get 2 ints right there.
    But with this currying, u can supply one int, and the other can be say a list of ints, and this fxn would apply calculate over each element in that list iirc.
    there is computerphile's video on haskell currying.

  • @torphedo6286
    @torphedo6286 Год назад +4

    I use newline braces in all of my indented code longer than 2 lines. But if it's 1 or 2 lines of indented code, I'll do a same line curly brace.

    • @isodoubIet
      @isodoubIet Год назад +1

      Honestly just pick one and stick with it. Ideally, use an autoformatter so you don't even have to think about it.

  • @ivlis.w
    @ivlis.w 4 месяца назад

    17:47
    I totally support that statement
    "Readability is a function of experience"
    After learning about those C++ modern features myself, I could say they are indeed not that bad
    Of course you'd need an explanation the first time you see them, but they can be pretty declarative and easy to use if you already know the language
    But still, I should say that it would be *higly* helpful to not try to do a one liner with them
    You know, for any person reading or debugging your code, like please was a ternary really needed there?
    That's exactly the reason why everyone complains when interacting with any existing C++ code

  • @metalhead2476
    @metalhead2476 9 месяцев назад +5

    The C version is by far the easiest to read and fix.

  • @cosmq
    @cosmq Год назад +2

    ya know what? just do this :
    ```
    int calculate(int bottom, int top){
    int sum = 0;
    for(int i = bottom; i top ? 0 :
    ((bottom & 1 ? 0 : bottom) + calculate(bottom - 1, top)));
    }
    ```

  • @arjix8738
    @arjix8738 Год назад +4

    i think the iota comes from the fact that most range loops use i as the variable, for i in range(...), and i is basically iota

    • @xBZZZZyt
      @xBZZZZyt Год назад

      what about the "ota"?

    • @trenwar
      @trenwar 8 месяцев назад

      ​@@xBZZZZyt iota means range, idk what ota means but there's that

  • @dubble_g
    @dubble_g Год назад +1

    imagine me starting uni with 0 programming skills has to do a course in C and Haskell...now in my 5th semester i havent re-done the haskell exam and i am scared because it is probably on paper too... imagine coding haskell on paper to pass a university course

  • @lpanebr
    @lpanebr Год назад +9

    I feel that Code aesthetics target audience is less experienced.

    • @biskitpagla
      @biskitpagla Год назад +5

      True. He also focuses on relatively more oo and less multiparadigm languages and design patterns afaik.

  • @joshuakb2
    @joshuakb2 Год назад +1

    In the Haskell code, that first line "calculate :: Int -> Int -> Int" means "calculate will be a function that takes an integer and returns a function which takes an integer and returns an integer."
    In Haskell, all functions take exactly 1 argument and return exactly 1 value. "calculate bottom top = ..." is really shorthand for "calculate = \bottom -> \top -> ..."

    • @itsacorporatething
      @itsacorporatething Год назад +4

      You could say calculate :: (int, int) -> int, but that would be against the grain.

  • @Gornius
    @Gornius Год назад +20

    That C -> CPP transition was HIDEOUS... No wonders how I almost abandon programming career because I thought everything more complex than simple loops looked like CPP. At this point I am pretty sure CPP people do these things to make their code so obscure that only they can understand what's going on, thus securing their jobs.

    • @voidwalker7774
      @voidwalker7774 Год назад +5

      you would not believe how often i actually observed this behavior in real life

    • @myxail0
      @myxail0 Год назад +4

      But it's basically the same as rust version, just poorer syntax

  • @crispi_shots
    @crispi_shots 10 месяцев назад +1

    24:50 that’s wrong, func is calculating even sum between a range
    C++ solution without the new stuff:
    int calc(int b, int t){
    if(b>=t) return 0;
    int sum = 0;
    for(;b

  • @kipchickensout
    @kipchickensout Год назад +6

    new line braces are more readable and widely used in C#, i see it as a positive thing

  • @Multifire
    @Multifire 8 месяцев назад +2

    You need to be a freaking NASA scientist to understand that C++ refactor, I bet it's slower too.