Declarative vs Imperative in Functional Programming

แชร์
ฝัง
  • เผยแพร่เมื่อ 7 ก.พ. 2025

ความคิดเห็น • 154

  • @ApprendreSansNecessite
    @ApprendreSansNecessite 2 ปีที่แล้ว +21

    Another thing I wanted to say is that a `for` loop can do anything. When you see it you need to parse the code and you need to mentally jump from place to place where mutations occur.
    When I see `filter` I know what it does and I know I can mentally replace this expression by its result (referential transparency) and carry on reading the next expression. There is actually a lot less cognitive load.
    And you don't need to define the predicate as an anonymous function. Functions can have names and these names carry meaning. The whole point of higher order functions is to be able to pass *functions* to them. If you write the instructions on the `filter` call site, you only partially take advantage of HOF, because the predicate can also be a composition of other functions, with their narrow scope (== small cognitive load).
    You can shove arbitrarily complex predicates in there. Try to do the same in your for loop and see if it's still easy to parse. And if your reaction to this is that you can use a function inside your for loop... well, congratulations, you just implemented filter ;)

    • @DomainObject
      @DomainObject ปีที่แล้ว

      💯 Couldn’t agree more.

  • @wongyok1
    @wongyok1 2 ปีที่แล้ว +36

    The complexity of map/filter/reduce comes from the learning curve, once you have learned it, it has become a part of your long-term memory, you will be able to process them intuitively in no time.
    On the other hand, the complexity of loops, mutations, conditional branches can only be handled by your working memory because there is no abstraction to form patterns

    • @ebrelus7687
      @ebrelus7687 2 ปีที่แล้ว

      This is a helpful tip at my early stage. Thanks a lot!

    • @markhathaway9456
      @markhathaway9456 2 ปีที่แล้ว

      From the imperative world we're used to functions like abs(x), max(x,y), pow(3,2), etc. as relating to data manipulations, but the functional functions like map/filter/reduce relate to lists, searching, selecting, immutability, and those are a bit different, and are often just pre-packaged control flow to eliminate the fine granularity of loop...until or do...loop or for... stuff.

    • @DomainObject
      @DomainObject ปีที่แล้ว

      Bingo. This is why I reach for map/filter/reduce over raw loops almost each and every time.

  • @ABTA
    @ABTA 2 ปีที่แล้ว +8

    I was always confused by both and never really understood it even after writing lots of code in different languages and also reading lots of articles about this. Your example was really good as you said that we don't know how "append" method adds at the end of list and how "filter" function filters the list so in a sense all programming languages are declarative and imperative at the same time. That made my concepts clear.
    Thank you for clear crisp explanation.

  • @WouterSimonsPlus
    @WouterSimonsPlus 2 ปีที่แล้ว +22

    To me these discussions are always somewhat weird. I've always enjoyed finding elegant ways to solve problems and sometimes they end up being more declarative or imperative. I agree with the point that the design of the solution matters more than the language patterns hidden in the functions in most cases. Cognitive load and cyclomatic complexity are super important, but especially cognitive load changes over time when you get more used to particular patterns and styles. It really helps me if a codebase follows one style rather than multiple styles mixed when it comes to reading and understanding it.

    • @N1ghthavvk
      @N1ghthavvk 2 ปีที่แล้ว +1

      Yes, we put focus on doing things the same way in our code base too. Makes it easier to understand, especially when new people join.

    • @dickheadrecs
      @dickheadrecs 2 ปีที่แล้ว

      some codebases are intentionally hostile to onboarding

  • @OLApplin
    @OLApplin 2 ปีที่แล้ว +33

    5:56 Cognitive load is not absolute. Some idea might be more complicated to grasp but once you learned them, it could be easier afterward. For me, the cognitive load to go through what the loop does with every element is higher than just applying a filter.

    • @alexis3170
      @alexis3170 2 ปีที่แล้ว +5

      Agree.
      I'd add that it also depends on the language, a lambda expression inside an Array method is the bread and butter of modern javascript for production code,
      but one could argue a bit less so in Python.

    • @viktorasmickunas2527
      @viktorasmickunas2527 2 ปีที่แล้ว +1

      Exactly

    • @aakarshan4644
      @aakarshan4644 2 ปีที่แล้ว +3

      well that would mean that the cognitive load isn't ever really lost .. its either heavily accumulated at the start or ever-present throughout the development process.

    • @EricKolotyluk
      @EricKolotyluk 2 ปีที่แล้ว +1

      The trick is how to we best introduce new levels of knowledge and understanding? Increasingly, with modern code, there will be more cognitive load to someone who has little or no experience with Functional Programming. This gets worse when you don't know about curried functions, and start reading Scala code that uses Haskell calling conventions. It turns out, there are ways to use curried functions, such as in Javascript, that are actually not too hard to understand, but you have to choose to design it that way... We have to learn write our code for other people to read, and Dave made an excellent point about that.

    • @markhathaway9456
      @markhathaway9456 2 ปีที่แล้ว +1

      @@EricKolotyluk Perhaps Scala shouldn't have gone with the Haskell type hierarchy to avoid being pigeon-holed into Currying.

  • @zkreso
    @zkreso ปีที่แล้ว

    One thing to point out is that the foreach construct is in itself declarative. In the example where word counts are compared, the use of a foreach loop (a more declarative approach) over a regular for loop (a more imperative approach) is reducing the word count by quite a bit. The code can be translated much more naturally to English precisely because we are using a foreach and not a for loop.

  • @davew2040x
    @davew2040x 2 ปีที่แล้ว +3

    Certainly an interesting topic, and one that’s very difficult to discuss effectively in a generalized way. The answer to “which is better” typically comes down to a “I know it when I see it” situation. Ideal declarative scenarios can often be beautiful in their elegance, but there are other concepts that are practically impossible to express in a declarative way, or at least become convoluted and very unintuitive when done so.
    I do like the insight that declarative systems can often *start* beautiful and become ugly and cumbersome as features get added. It is very true to my experience.
    Ultimately there’s one fundamental truth in programming: the most important thing is the ability to break the system down into units that are small enough to describe and comprehend. Everything else is secondary to this.

  • @dexterplameras3249
    @dexterplameras3249 ปีที่แล้ว

    Your comment on reducing cognitive load rang completely true with me. I've found that reducing cognitive load becomes more important when a coder uses multiple programming languages. I used to write Perl only, probably one of the most concisely typed programing languages ever known. The same example in Perl would take only a fraction of the lines compared to writing for other programing languages. After stints in Python, Ruby, Go and then repeat when changing jobs, I found with language specific idioms, I would have to relookup language specific idioms every time I switched between languages (even in the same job). It wasn't an issue when I only wrote Perl and quite frankly it was much quicker to get code out when only using one language. My favourite language now is Go which is all about reducing the cognitive load, however there are certain times in which I wish certain features of Ruby or Perl where in Go because it would reduce the extensively longer amount of code I'm writing in Go which I could do in one of two lines of code, an example of this is error handling. I do agree that reducing cognitive load is very important in writing maintainable code, but I wish that concise syntax would not be traded off for readability (cognitive load) every time. Surely there is a way to have both? I hope that language developers would find ways or alternatives that make idioms such as ternary operators, conditional assignment operators, concise and readable. That would be coding nervana.

  • @esra_erimez
    @esra_erimez 2 ปีที่แล้ว +1

    You said, "error of my ways". However, I beg to differ. You are very deliberate with your choice of words. Your arguments are cogent and well thought out. Your knowledge is steeped in decades of real world experience. I hold your insight in the highest regard, right up there with the likes of Ed Yourdon, Weinberg, Booch, Demarco and Fowler, just to name a few.

  • @AloisMahdal
    @AloisMahdal 2 ปีที่แล้ว +1

    I am usually attracted to something like this:
    def sweet_words(dictionary):
    def is_ok(W):
    return len(W) == 5
    return list(filter(is_ok, dictionary))
    Still pretty declarative without stacking the Jenga tower of composition too high. And separates the overall point of the function (filtering) from definiton of the criterion (five-letter-er).

  • @Dr-Brown
    @Dr-Brown 2 ปีที่แล้ว +11

    declarative_words(Dictionary, Words) :-
    findall(Word,
    (member(Word, Dictionary),
    length(Word, 5)),
    Words).
    Couldn't let this go without the example in the most iconic of the declarative languages: Prolog. Read as "find all the Words such that the Word is a member of the Dictionary and the Word has length 5." How's your cognitive load on it? How Prolog actually executes it depends on which Prolog you're using.

    • @valcron-1000
      @valcron-1000 2 ปีที่แล้ว

      In Haskell using list comprehension: '[word | word

  • @defeqel6537
    @defeqel6537 2 ปีที่แล้ว +2

    Thank you for mentioning the (mental) cost of abstractions!
    I generally use both styles where I feel they make reading the code easier, and I'm not married to any one style, but some pointers:
    - complex (/overly generic) declarative statements should still be abstracted with smaller, better named, methods
    - declarative style becomes much more readable when the language supports piping functions instead of nesting them
    But as you allude, I think, the bigger the picture the more declarative style shines. If the purpose of the software is to present something of a domain language to the user, considering a declarative style can be helpful.

    • @defeqel6537
      @defeqel6537 2 ปีที่แล้ว +1

      e.g. if your declarative example were to read something like:
      return dictionary | filteredBy | (word) => 5 == len(word) | convert_to_list
      it becomes much more readable, at least in my opinion
      then there is API design in general, the example could also read (might be invalid Python, it's been a while):
      return filter(dictionary).by(lambda word: 5 == len(word)).then(convert_to_list)

  • @kebien6020
    @kebien6020 2 ปีที่แล้ว +11

    In terms of cognitive load, I would say it kind of depends. Using the same example of length 5 words, here is the JS version (in FP-style).
    const declarativeWords = wordlist => wordlist.filter(w => w.length === 5)
    I would say that this is not only lower cognitive load, but also that it reads even more like English, "take the wordlist and filter it leaving only the ones with length equal to 5". Here is the Haskell version:
    declarativeWords = filter ((== 5) . length)
    This one just keeps the important bits. "We are filtering for the ones with length equal to 5". All the boilerplate is gone.
    Going back to JS, here is the imperative version:
    function declarativeWords (wordlist) {
    const words = []
    for (const word of words) {
    if (word.length === 5) words.push(word)
    }
    return words
    }
    When I read this code I go like this:
    - Hmm an empty array followed by a for, maybe a map?
    - Let me look if we are pushing inside the look. Ok we are, so a map... wait
    - The push is inside an if, so it's actually a filter.
    - Are we modifying the value? Nope, just a regular filter.
    - So we are filtering for words of length 5.
    - Let me double check we are actually looping over the input array. Yeap, no surprises there.
    - ...Well, if this was a filter why didn't you say so from the start!!!
    The FP-style versions just flat out spell filter, I don't have to look for patterns there. Also, if I'm not interested in the details of the filtering I can bail right away (maybe I'm looking for changes in the shape of the data), the imperative version forces me to understand it fully before looking at something else.
    Another point is the way that you perform changes on these functions. If the next step is to, say, make the words upper case. This is how the imperative version is usually going to be changed:
    function declarativeWords (wordlist) {
    const words = []
    for (const word of words) {
    if (word.length === 5) words.push(word.toUpperCase())
    }
    return words
    }
    With this it looks almost like a filter, but now it's also changing the data, so reading this code for the first time you have to understand it fully to see where the data is changed. Here is the natural change to the FP code:
    const declarativeWords = wordlist =>
    wordlist
    .filter(w => w.length === 5)
    .map(w => w.toUpperCase())
    The 2 parts correspond to 2 separate function calls.
    To me this code is just lower cognitive overhead overall because it's easier to know which parts to ignore when browsing around (browsability is really important because code is read more often than written), and because the patterns are not obscured by loops, assignments, control flow, etc., but rather spelled out in words.
    (To be clear, the imperative code is a bit faster than the FP one, since in the FP version I'm creating more temporary arrays. And in general, to avoid mutating data structures, you'll end up with slower code that does a bunch of extra copying. To me that tends to be a non-issue, since in my experience it is rarely a bottleneck.
    There is also workarounds to these problems, like how Haskell optimizes this using stream fusion or how C++ and Java handle this with "streaming" semantics.
    There is also various techniques that exist for immutable data structures sharing storage when being "modified", which reduces the amount of copying, like how Clojure does it.)

    • @KogiSyl
      @KogiSyl 2 ปีที่แล้ว

      It kind of looks like in this example declarative wins, but you are not comparing apples to apples.
      Now try the same thing when you don't have the "filter" function ready from the beginning, because that is what you started with in the imperative style.
      Try to make the filter function in declarative style without using the built-in ;)
      Now here you have a challenge, don't you? ;)
      Now I will show you a different code in imperative style, using "generators"
      function *imperativeWords(wordlist) {
      for (const word of wordlist) {
      if (word.length === 5) yield word;
      }
      }
      Nice, isn't it?

    • @kebien6020
      @kebien6020 2 ปีที่แล้ว

      @@KogiSyl I had the filter function available in the imperative version. It is built in into the language, just like for loops. It is also available in other languages:
      * filter in Python
      * Stream#filter in Java
      * std::copy_if in C++
      * Iterator#filter in Rust
      * clojure.core/filter in Clojure
      But here is the filter function in FP-style anyways:
      const filter = (arr, fn) => arr.reduce((acc, x) => fn(x) ? [...acc, x] : acc, [])
      Here is a less performant version in pure recursion:
      const filter = (arr, fn) => {
      if (arr.length === 0) return []
      const [head, ...rest] = arr
      return fn(head) ? [head, ...filter(rest, fn)] : filter(rest, fn)
      }
      And here is the version using a loop:
      const filter = (arr, fn) => {
      const acc = []
      for (const x of arr)
      if (fn(x))
      acc.push(x)
      return acc
      }

    • @kebien6020
      @kebien6020 2 ปีที่แล้ว

      ​@@KogiSyl On the generators, I actually like them. They also mix very well with functional programming.
      The thing with generators is that they kinda work like lazy collections, that calculate each value on demand when it's needed. This is nice for performance, and solves the problem I mentioned about temporary arrays being passed around. But in my experience people find it hard to debug, because this "on demand" execution is not intuitive for a lot of people.
      An example to show both the weird execution order and how nice it works with FP:
      function* filter(arr, fn) {
      for (const x of arr)
      if (fn(x))
      yield x
      }
      function* map(arr, fn) {
      for (const x of arr)
      yield fn(x)
      }
      const toArray = iterable => [...iterable]
      const isLen5 = (s) => {
      console.log('isLen5')
      return s.length === 5
      }
      const upper = s => {
      console.log('upper')
      return s.toUpperCase()
      }
      const filtered = filter(['abcde', 'abc', 'abcdef', '12345'], isLen5)
      const result = map(filtered, upper)
      console.log(toArray(result))
      This logs the following:
      isLen5
      upper
      isLen5
      isLen5
      isLen5
      upper
      ['ABCDE', '12345']
      If you make a breakpoint in isLen5, and look at the call stack, you will see that it is being called by filter, which makes sense, but on top of filter is map, and on top of map is toArray. Which looks weird. There doesn't seem to be any function calls in toArray at all, in fact it doesn't seem to be doing anything. But in this example, the one that is actually "requesting" values to be calculated (remember, they are calculated on demand), is toArray.
      If you do a lot of work on lazy iterables the order of execution can get pretty crazy. If you analyze it, it makes sense, but it is pretty different to what we are used to. I find the cost in cognitive load of using generators usually too high to justify, especially when working with more people (so most of the time).
      If you use them a lot, you will invariably hit some harder cases where you have to understand the iterable protocol, the iterator protocol, and all the other nitty gritty details. And these too, are things that usually don't make sense having to teach people.

    • @digitalspecter
      @digitalspecter 2 ปีที่แล้ว

      @@KogiSyl There are languages where for-loop is not a built-in... but filter is. Still, that's a weak point because people know the concept and how it works even if it needs to be implemented. Also, generators can be handy but I dislike using for-loop for non-generic cases because it forces me to "simulate" the loop in my head.

  • @bleki_one
    @bleki_one 2 ปีที่แล้ว +1

    I think the problem is that we, as software developers, like to focus on the "how?" rather than on "why?" and "what?" which really matter in my opinion.
    By saying "how?" I mean what is your IDE? What is you language? Are you using functional/Oop/declarative/imperative style? Are you using dark theme?

  • @osisdie
    @osisdie 2 ปีที่แล้ว

    Great video. Dave is not only talking about their differences but and particularly pointing out scenarios why declarative one can help more due to it could be more generic then results in better abstraction design. While, imperative way is still valuable for a smaller code section, introducing the flows in a method with clarity.

  • @marc-andrebrun8942
    @marc-andrebrun8942 ปีที่แล้ว

    thanks for this smart overview about programming style.
    I'm just an old tinkerer, I started programming in the 80's in Pascal;
    now, I use C as imperative and scheme (racket) as functional, and i don't know python (i feel it boring);
    i think you miss one point between imperative / declarative :
    because most of algorithmes are already available in the core with built-in function, you don't need to write it, so you type less code with declarative;
    you mention that, right, but they're two main advantages :
    there's very much less bugs in scheme than in C and, in 20 ligne of code that you can see on your screen, you have a full picture of a lot of stuff done by your code, instead of C, you have to write so many lines and spent time scrooling to read your code.
    about languages :
    you make exemple with python which seem versatile and able to do everything : imperative, object oriented, and even declarative;
    i don't use it because i feel it messy and it never fire my imagination;
    C and racket seems to be on opposite edges of programming paradigms, and i feel it's worth to practice both : it's a mutual improvment.

  • @Quapcio
    @Quapcio 2 ปีที่แล้ว +4

    If talking about 5-letter-words example in Python - that's why they introduced list comprehension/generator expressions to have english-like code:
    return [word for word in dictionary if len(word)==5]
    Yes, it's syntactic sugar, but "readability counts" like it's stated in Zen of python 😉

    • @gabrielmachado5708
      @gabrielmachado5708 2 ปีที่แล้ว +3

      List comprehensions are cool for those direct cases, but I find it hard to understand when you have more than one loop and more conditions. I can understand better the maps and flatmaps when that's the case.

    • @ewerybody
      @ewerybody 2 ปีที่แล้ว

      @@gabrielmachado5708 True

    • @ovhaag
      @ovhaag 2 ปีที่แล้ว

      This List comprehension is the natural way to do the job in Python. And if you are familiar with the construct, it has least cognitive load either.
      One question remains: Is a List comprehension imperative or declarative? Maybe the answer depends on how the programmer represents the contruct in his mind.

  • @HoD999x
    @HoD999x 2 ปีที่แล้ว +2

    I think of it as a scale. There is no clear separation between imperative and declarative. In practice I mix the two and choose whatever works better (the priorities being:less chances to introduce bugs, coded faster, runs faster)

    • @HoD999x
      @HoD999x 2 ปีที่แล้ว +1

      It's surprising thst I skipped 'readability '. To me that's just so much of a must-have that I didn't mention it

    • @berkes
      @berkes 2 ปีที่แล้ว

      Indeed: it's a spectrum, not binary. You don't "have declarative code", you have "more declarative" and vice versa.

  • @lucassacramento9039
    @lucassacramento9039 ปีที่แล้ว

    I like to think that Imperative vs Declarative Programming are products of Inductive Reasoning vs Deductive Reasoning respectively. In Inductive Reasoning, the thinker starts from specific premises based on observations in order to produce a general conclusion. It’s starting from the complex and trying to make it simpler by generalizing things. A bottom-up approach.
    In Deductive Reasoning, the thinker starts from general premises based on theoretical approaches in order to produce an specific conclusion. It’s starting from the simpler, understanding it in-depth, and then composing their next reasoning with its previous conclusions of that’s “true and accurate”, which creates an end result that’s more complex indeed.
    Inductive thinkers are focused on productivity, impact and profitability. It’s as if they tend to enjoy the end result (see something working and solving the problem) more than anything.
    Deductive thinkers are focused on being immersed in the process, accuracy and understanding.
    Deductive thinkers tend to believe that Inductive Reasoning isn’t logical, since it tends to focus on a reaching a goal instead of understanding the true nature of each premise in-depth (subject). Whereas Inductive thinkers tend to believe that Deductive thinkers aren’t rational, since spending more time on understanding each thing in depth cannot compare to making an impact in the real world (object).
    If you notice, our favorite programming paradigms can tell us a lot of our own personality too. Imperative Programmers (Inductive thinkers) sees the world in similar way too. They value and/or understand hierarchy, requirements, deadlines, organization, etc. more than Declarative, which can also make them to feel more Pride. They also tend to be more successful financially, since the “business world” is rooted on Inductive.
    Declarative Programmers (Deductive Thinkers) tend to see the world not as being full of objects that should respect their hierarchy and so on, instead they see the world as “collecting components and visualizing their internal potential”. In the Declarative Programmer’s mind, if we can make sure that one small thing/component has been understood and tested to be “accurate”, we store it in or own memory as being “true”. Let’s say another day we find something else that’s “true”, we consider that not only both of them are true but also the product created by those two (without any side effects). So when you say that “Declarative Programming is changing the state because it’s creating a new Array”, remember that’s not how it works in our mind. You’re seeing from the perspective of the end result seen in the computer from the product of enforcement towards the machine (telling it what to do), but as the name says, “Declarative” means that from the perspective of the programmer, once something is done IT IS done. If something is not useful anymore, it shouldn’t be just transformed, instead it should be displaced from their current role (the one we know it is true for that occasion) and then start a new reasoning from there, while using the experience of the past state as measurement for a testing that starts inside our heads even before writing a single line of code. That also tells a lot about “Declarative” personality traits. If we find something new that has potential to be “truer” than the past truth, we get immersed into it until we understand it completely (or their innate potentials). That makes us less efficient, productive and unable to commit to a single thing for much longer. On the other way, Declarative/Deduction is far more accurate logically, since it comes from an impersonal point of view, making the “truth being the truth regardless of our personal relationship with it”.
    So in my view (which I could be wrong):
    Imperative Programmers: Inductive, Externally Productive, Profitable, more organized, can be too committed (fanboy/cultist-like), though full of generalizations based on their own personal experiences, which makes them efficient but less logically accurate overall.
    Declarative Programmers: Deductive, Internally Productive, Knowledgeable, more chaotic, difficulty with commitment, though spend too much time experimenting without producing, which makes them less efficient but more logically accurate overall.
    PS: Another thing that just popped out in my mind, is that there’s
    this view that “the more imperative and low level one is programming, the closer one is to talk to the same language as the machine”. From my personal point of view (of someone who thinks far more declaratively than imperatively, which could also be a personal bias), that’s the complete opposite. In my head, the more I have power towards something, the less “equal” I am with that thing. In my opinion, “talking the same language” means taking a small piece that has been tested, processed and confirmed by the machine as being accurate, understanding “how it works” and “why it works”, and then considering it as something untouchable that I should only use it to make safe compositions. If there’s a situation in the future where somehow it’s been proven that it’s not accurate OR there’s something new that is more accurate and works better, it’s like it “invalidates” every single thing I’ve ever produced/concluded that was composed with the past “component”. This often leads me to feel this urge to refactor things (like code) done in the past and update it to my current view of what’s true. Like, if I learn something that could bring some improvement to a code I wrote in the past, I feel the responsibility to update it myself, regardless if it’s doing its job successfully. So my view (from a Declarative Perspective) is that this way of handling logic is what means to “talk with the machine with the same language”.
    Sorry for the long text but I hope I was able to explain it. It took me ages to realize that people actually processed things in their brain in an imperative/OOP-like way (like Gravity Force, a Scalar quantity) instead of processing each thing individually and just visualizing slots where that thing could be used while considering it might bring a different behavior/impact depending on the situation (like Gravitational Field, a Vector quantity).

  • @ClaymorePT
    @ClaymorePT 2 ปีที่แล้ว +1

    The problem with python examples is that sometimes its easy to forget that Python has built-in functions which are in fact, C functions with bindings.
    Taking advantage of built-ins in Python is a smart thing, given that they will run faster than any byte-code function implemented in Python. As so, the imperative function will most likely have a penalty performance.
    It's not just about the clarity. It's also about the developer knowing the tool so that it takes advantage of its features when it is required. Clarity can have a performance cost which will directly translate in higher operational costs.

  • @xybersurfer
    @xybersurfer 2 ปีที่แล้ว +1

    My feeling is that your tests are a form of declarative "programming", especially when they test the full specifications. The problem i see with the functional example is that there are lots of nested brackets when written in that style. Usually there is some kind of "|>" pipeline operator in functional languages, that passes the result from one lambda onto the next, to prevent some of this:
    def declarative_words(dictionary)
    dictionary |> filter (lambda word: 5 == len(word)) |> list |> return
    Now you can see the steps more clearly. You could argue that the requirement for an order makes this imperative. You could also argue that specifications require you to specify an order in some parts. But calling specifications imperative seems strange (they are declarative). The line is very blurry, but maybe it's because i'm considering some *expressions* as imperative as opposed to only *statements*

  • @beowulfsleeps892
    @beowulfsleeps892 2 ปีที่แล้ว

    I can broadly agree that Declarative offers a high level of abstraction but that (paradoxically?) it may a higher cognitive load.
    One thing you didn't touch on (unless I missed it) is a consequence of the difference between Declarative (what) versus Imperative (how) that Declarative languages/styles are claimed to be much more open to parallelisation than imperative.
    make is an interesting example in that its declarative style makes it easy to run parallel build tasks but the build steps are still imperative and the programmer still has to be careful with side-effects and interactions.

  • @johntrevithick5900
    @johntrevithick5900 2 ปีที่แล้ว +1

    Nice! Your point about cognitive load resonates with me.🧠
    When I first started coding in JavaScript (after many years with Java/C++) the similarity of syntax was very confusing. In time, I learned that thinking *declaratively* with JS and *imperatively* with Java made switching between the languages easier. It was (and still is) a 'cleaner' division for me.
    I wonder if the Imperative/Declarative debate isn't in part driven by this combination of similar syntax/different underlying paradigms exhibited by these popular languages. It is of course possible to write JavaScript classes (or Java lambdas) but I for one find the code difficult to read. There is a cognitive switching cost at work.
    At heart, the C/C++ languages are built on imperative ideas. The functional stuff is the syntactic sugar. JavaScript/ECMAScript is built on declarative ideas, and it is the OO stuff that is the syntactic sugar.
    I suggest that mastery of a language involves low-sugar coding.😁

  • @KogiSyl
    @KogiSyl 2 ปีที่แล้ว +3

    Each of both approaches to writing a code have their place where they shine and each has a place were they suck terribly ;)
    I don't like however the fact, that in declarative languages where you use those long chains of functions - you need to read them from the innermost to the outermost which for most programming languages means from right to left - while I am used to reading from left to right, and it gets much more complicated if you don't just have a single chain, the "beginning" will be hidden somewhere in the middle of the code and the control flow and data flow will be scattered all over the code. I don't like when the sequence of my code doesn't reflect control flow or data flow and it's a very big minus for declarative languages. Also, declarative languages suck at handling complex types that have interconnections not structured from top to bottom like a tree. You would need a lot of gymnastics and a ton of code to handle those types in a declarative approach.
    I love however how they handle some of the algorithms.
    When I am choosing my languages, I don't care how easy it is to write "hello world" in them, I am more interested if they won't break or blow up out of proportions when I will try to use multithreading or async or complex types or algorithms, depending on the projected future needs of the project.

  • @ratfuk9340
    @ratfuk9340 9 หลายเดือนก่อน

    I find that after the initial learning curve, there's less cognitive load to read declarative code. Once you know map, fold, reduce, any etc, it'll be easier to parse something like "... any . map f" than the equivalent iterative version. You have to scan through the loop and figure out what it does, as opposed to just seeing what the pipeline is. Both will still require understanding why that is done but at least I don't have to puzzle out the "what" part first.

  • @hughdavidson6748
    @hughdavidson6748 2 ปีที่แล้ว +21

    From my perspective, i’d argue the declarative example you used actually requires fewer language constructs as it’s mostly just function composition. The more imperative version requires you to understand variable assignment, for loops, conditional control flow, list mutability. I think it’s easy with the curse or knowledge to assume these things are trivial and require less cognitive load, but that’s largely a matter of exposure.

    • @ContinuousDelivery
      @ContinuousDelivery  2 ปีที่แล้ว +7

      Yes, but as I said we do know quite a lot of this from English and algebra.

    • @shardator
      @shardator 2 ปีที่แล้ว +9

      This "just function composition" thing is actually quite complicated. When your wife sends you shopping with a shopping list, she wont define it in a declarative way. "Go to the shop, bring theses things", as opposed to some obscure, declarative way.
      I studied declarative programming, and am a happy user of lambdas here and there (C++ mainly), but I still find it much cleaner to write a loop than to write down a nested map/filter/whatever.

    • @TheSirmousavi
      @TheSirmousavi 2 ปีที่แล้ว +10

      @@shardator Does your wife tell you to go to aisle 5 look at the shelves, find this or that, compare the price, put it in the trolley, etc.... or she just gives you the list of things to buy?

    • @bobbycrosby9765
      @bobbycrosby9765 2 ปีที่แล้ว +2

      @@TheSirmousavi realistically, you would only ever do that once in an imperative language. I tend to find that as you get into higher level implementation code, imperative and functional paradigms tend to converge on eachother: functional looks more imperative (pipelines), imperative looks more declarative (tell don't ask).

  • @MikeStock88
    @MikeStock88 2 ปีที่แล้ว +1

    Great video
    I had the class vs function style java script problem today
    Took me ages to even understand how to write the code
    If its easier to read it's better imo. I should be able to read it like English
    I always think of them as organisational patterns, ultimately it doesn't really matter. If you can find it easy and understand it, that's its purpose

  • @alxjones
    @alxjones ปีที่แล้ว

    Functional vs. OO, Declarative vs. Imperative, and language choice are all related, even if not inseparable. It's more like declarative style is the goal, functional is a paradigm that lends itself to a declarative style, and certain languages lend themselves to the functional paradigm. It doesn't mean you can't write declarative code anywhere, but it also doesn't mean that Java 7 or C99 is just as good for a declarative style as Kotlin or Rust.
    To take the simple words example here, a Kotlin version would be `dictionary.filter{ it.length == 5 }`. Less effort for better clarity, because the language is built to do this well. Python is not, so using that language to demonstrate functional or (non-pythonic) declarative style is doing a serious disservice. The pythonic way would be `[word for word in words if len(word) == 5]`. I wouldn't pick it over Kotlin, but I would pick it over `list(filter(lambda ... ))` any day.

  • @jw8573speed
    @jw8573speed ปีที่แล้ว

    I generally prefer declarative with lambda, but it get really messy when one start stacking multiple "map", "filter", and "reduce" in one line.

  • @gabrielmachado5708
    @gabrielmachado5708 2 ปีที่แล้ว +1

    Imperative code is better when you want more control over stuff, and it is indeed easier to understand when you are not very familiar with the concepts, because you can follow the steps.

  • @jesperhallborg7720
    @jesperhallborg7720 2 ปีที่แล้ว +1

    Love the vids, your doing a great job :)

  • @lysun83
    @lysun83 2 ปีที่แล้ว

    The same example made in Java stream it very different. The stream itself is very good approach in dataset manipulation, I started with rxjs it's a bit different but similar.

  • @soberhippie
    @soberhippie 2 ปีที่แล้ว

    First, the Master Yoda style in comparisons is not Pythonic (i.e. 5 == len(word)). As someone who switched from Java to Python a considerable time ago and has recovered, I find the `filter` variant a lot more readable and containing a lot fewer "thought elements". "I have a list. I need to filter it. Here's the filter I want to pass it through (lambda w: len(w) == 5), here's what I'm filtering (words)". If you look from right to left you see the raw collection, the filter and the receptacle that the filtered elements drip into. Whereas with the for-loop, you don't see the intent of the loop until you find the crucial `append(...)` thing somewhere in the middle. At the same time, the for-loop will then get bloated with other transformations to the elements, like, stripping them from whitespaces, checking that there are no umlauts and stuff, so it's intent will shift and it will become even more difficult to reason about. And certainly, as a recovered java developer, I think that those Interfaces, Adapters and the like make it _more_ complicated, not less

  • @KogiSyl
    @KogiSyl 2 ปีที่แล้ว +1

    "Declarative Programming Languages focus on on describing what should be computed - and avoid mentioning how that computation should be performed"
    Well, if you do all your basic functions in imperative style and then use them in declarative way then sure, declarative seems easier - but it is easier only because all the hard work was done earlier!
    Try to make the filter function in declarative style without using built-ins ;)

  • @TheJessejunior
    @TheJessejunior 2 ปีที่แล้ว +2

    your lessons are amazinly profound and clear. tks Dave

  • @aiders4352
    @aiders4352 2 ปีที่แล้ว

    I feel the cognitive load of the functional example here is definitely higher, but I also think other languages allow you to do a better job at expressing things in a functional way. Kotlin for example would be much more expressive, additionally with the help of extension functions:
    fun Iterable.getFiveLetterWords(): List = filter { it.length == 5 }
    @Test
    fun getsFiveLetterWords() {
    listOf("five", "four", "three", "two", "one").getFiveLetterWords() `should be equal to` listOf("three")
    }

  • @m.x.
    @m.x. 2 ปีที่แล้ว +1

    There's not "vs" because you can combine the best out of both approaches. 100% agree with Dave on "less typing != more clarity", less typing is good but there's a point where both factors become inversely proportional and clarity is negatively affected. Clarity refers to readability, which has impact on maintainability and extensibility, and 99% of real-world projects need to be maintained and extended. Thus, programmers should not code thinking about themselves, but rather about other programmers who might end up maintaining and extending their code in the future, and that requires certain level of empathy. I've met several programmers who religiously think declarative is better than imperative based on their limited experience and/or subjective perception, and they all had selfish and arrogant personality, coincidence? I don't think so.

    • @martinbakker7615
      @martinbakker7615 2 ปีที่แล้ว

      Indeed. Code is more often read than written.

  • @Yulrag
    @Yulrag 2 ปีที่แล้ว +2

    Very valuable insight. Thank you Dave.

  • @Vendavalez
    @Vendavalez 2 ปีที่แล้ว +1

    I have come to find that “readable” comes down to personal preference and personal experience. In other words personal biases. It is therefore difficult to have level-headed discussions about how “readable” because even the most intelligent people often have different preferences.
    Even very small differences on the amount of abstraction, let alone the kind, used can cause extreme disagreements. It is similar to the effect most people experience driving on the highway: everyone driving slower than you is a grandma and everyone driving faster than you is a maniac. With the difference that it is not very often that your boss sits next to you while driving and lectures you about how you should drive more like them.
    I think that is where some of the intensity comes from when it comes to these discussions. People don’t often like to be told what to do, specially when it goes agains their preferences. And so people start to get into camps and start to take their preference as the clearly superior way.
    Which is a shame because some of the most productive discussions on coding style happened when everyone came from the angle of “this is something that has helped me and maybe it will help you.”
    Which is probably why I enjoy this channel so much, since that seems to be the tone taken on every discussion. With some very clear personal preferences and biases, yes. But those are always identified and never treated as the “gospel truth.”

    • @ContinuousDelivery
      @ContinuousDelivery  2 ปีที่แล้ว +1

      Certainly it is subjective, but I think I mean something a little more specific when I talk about "readable code". I don't mean can I read it and understand it if I think hard. I can do that with assembler, but assembler isn't really usefully "readable" in the sense that I mean. Rather, can I infer what it is doing without studying hard. Could someone infer what it is doing, even if they don't know much, or anything about code? Are the words that we use for variables and functions relevant to the problem that we are solving, does my code make little, probably slightly weird, but understandable sentences that convey its intent? I strive for that, I don't always make it, but I nearly always try to.

    • @Vendavalez
      @Vendavalez 2 ปีที่แล้ว

      @@ContinuousDelivery oh, most certainly, but that is what the video made me think about. I should have made that clearer. I didn’t feel like I had much to add to the points made in the video, but I am trying to be more interactive with the content I enjoy since I know it helps. Even if it is only with tangental thoughts. I’ll try to keep in mind to make clear that’s what I’m doing.

    • @ContinuousDelivery
      @ContinuousDelivery  2 ปีที่แล้ว +2

      @@Vendavalez Please don't apologise! I agree with your points, just "riffing" on the ideas 😉😎

    • @stefanomontani6008
      @stefanomontani6008 2 ปีที่แล้ว +1

      It seems a little suspect the frequency with which the words "subjective" and "relative" come out in these comments. They seem a too much comfortable argument, if they are. Even the relativity has to be relative, and not absolute. "everyone driving slower than you is a grandma and everyone driving faster than you is a maniac": but probably you will never find someone asserting that who is driving slower than you is a maniac and who is doing it faster than you is a grandma. Declarative code is more concise, imperative code is more redundant, so the latter is more readable. This is objective, not subjective. The so called "subjectivity" comes only after. It is only after you have learned certain things by heart that the declarative code becomes subjectively readable just like, and maybe for someone even more, the imperative version.

  • @mike13891
    @mike13891 2 ปีที่แล้ว

    For the declarative example, couldn't you just split the one-liner into multiple lines to reduce cognitive load (like I did below)? That's what you did with the imperative example. If so, would that mean that the real argument in this video is to not use one-liners?
    def declarative_words(dictionary):
    filtering_function = lambda word: 5 == len(word)
    words = filter(filtering_function, dictionary)
    return list(words)

  • @Sarwaan001
    @Sarwaan001 2 ปีที่แล้ว +1

    Would a series of declarative statements technically be imperative?

  • @kamertonaudiophileplayer847
    @kamertonaudiophileplayer847 2 ปีที่แล้ว +5

    I use a hybrid approach, some very well defined part of programming certainly declarative, but the rest is an imperative.

  • @DryBones111
    @DryBones111 2 ปีที่แล้ว +1

    Does any of this even matter if you extract your logic into well named methods/functions? The calls to subroutines should spell out the how to the containing routine's what. At the end of the day you'll end up with a generally declarative flow tree with imperative leaves which the compiler can actually transform.
    The core of the argument is readability / cognition. Subroutine naming is the big fish.

  • @JanBebendorf
    @JanBebendorf ปีที่แล้ว

    Well functional programming doesn't actually count as declarative programming for me as I still need to tell the system how a certain domain problem needs to be translated into computation steps, it's just a way of making code more readable and reusable. Actual declarative programming for me is just delivering the problem to the system without needing to give it any hint on how to solve the problem. Until a couple years ago it seemed impossible to build a machine that could solve generic problems in such a way but with LLM's like GPT 3/4 it actually becomes somewhat possible. You still need to worry a little bit about building the prompt in a way that is comprehensible and meaningful to the model but it's coming closer and closer to being able to understand human language better than humans actually do and combines it with the largest data source that exists on earth so at some point it will actually be better at solving generic problems than any human brain writing/performing imperative instructions could ever be.

  • @ionutale1950
    @ionutale1950 2 ปีที่แล้ว +1

    this is like slang.
    for natural language, let's say English, younger usually use slang that annoy older gen.
    for programming I think it is the same

  • @inninner
    @inninner 2 ปีที่แล้ว +1

    About immutability: In the functional example, you get a new list (a state change) AFTER your function (whole program) is done, in the imperative example your state changes many times and that could be a problem if later on you add something in between appends

    • @ContinuousDelivery
      @ContinuousDelivery  2 ปีที่แล้ว +1

      Yes, but this wasn't talking about immutability, that is a different discussion I think. Imperative or declarative says nothing about immutability.

  • @scvnthorpe__
    @scvnthorpe__ 2 ปีที่แล้ว

    The way I think of it is that
    Imperative: chop(a) (a itself is chopped)
    Declarative: b = chop(a)
    So one is *doing something* directly, whereas the other is declaring a new value
    At least that's the impression I get

    • @ContinuousDelivery
      @ContinuousDelivery  2 ปีที่แล้ว +2

      No, sorry but that is something else. What you are describing is "immutability" not "declarative programming.

  • @MrAbrazildo
    @MrAbrazildo 2 ปีที่แล้ว

    8:11, I guess by now most of popular declarative languages have this filter concept.
    15:40, to reach this level, the designer must seize all possible combinations from the different features the system can combine. Either this is too simple or, as far as I can see for complex world problems, the design starts to shatter once new conflicting features are being added. i.e. your objects can communicate to each other, when they were text-only objects. Once graphics were added, a new design was required. Nowadays it's an utopia to glue a complete strange thing like this, without needing to break and remodeling the design, at least partially. And this kind of thing uses to leave marks (or should I say scars?).

  • @DuckStorms
    @DuckStorms 2 ปีที่แล้ว +5

    SQL is a clear example of declarative programming instead of imperative. And I would say it’s definitely not just syntactic sugar.

  • @TheGrumpyGameDev
    @TheGrumpyGameDev 2 ปีที่แล้ว +2

    one of the more common PR comments that I make these days goes something like "I have to mentally compile this code in order to understand what it does.... make it so I don't have to."
    we often forget the LANGUAGE part of "Programming Language". languages communicate thoughts and ideas. computers don't have ideas or thoughts. humans do. the fact that our programming languages reduce down to computer instructions is useful, but not the main point of the exercise.

  • @hungariannerd8445
    @hungariannerd8445 2 ปีที่แล้ว

    Great video I learned a lot

  • @coliv2
    @coliv2 2 หลายเดือนก่อน

    return [x for x in dictionary if len(x)==5]

  • @KogiSyl
    @KogiSyl 2 ปีที่แล้ว

    The example was wrong, it assumed the prior existence of "filter" function in declarative language. Try to make a "filter" function in declarative language without a built-in then compare it with imperative code. Now here you have a headache, right? How to make a filter without using a ready filter?
    Now I will show you a different code in imperative style, using "generators"
    function *imperativeWords(wordlist) {
    for (const word of wordlist) {
    if (word.length === 5) yield word;
    }
    }
    Nice, isn't it? Pretty, simple, understandable from first glance. Generators can do all the things that functional code can do (and a ton of things that functional programming struggles with) in a simpler fashion ;) And they keep the control flow and data flow much more visible than functional programming.

  • @MrAbrazildo
    @MrAbrazildo 2 ปีที่แล้ว

    I guess by now everybody understood TDD is good. You should now become more technical. Let's say I have a f() that makes several steps to reach a goal. It has several steps because they are private to it, they would not make sense outside of it - _it could be prone to errors if called outside_ . And that f() also updates variables in other classes, because otherwise this should be scheduled to do later, and before some certain other actions take place - _and would be prone to errors too, if this schedule fails to complete or, in worse cases, fails to follow a certain order too_ . By what I understood so far, TDD would demand that a f(), which would be written like that, be splitted in several small f()s. So, to avoid a disaster, I think 2 possible solutions:
    a) Each f() like this should become a class, with all those private steps being private f()s from it. Tests would have plenty of access to those f()s, most of them not tagged as 'const'. Fortunately, tests are small, and compile-time prone to be implemented. So, tests are unlikely to cause bugs - and easy to fix if that happens.
    Pros: *each test would be executed only 1 time* .
    Cons: *kind of a "risky" design* .
    b) Any f() like this stays the same, but its steps could become lambdas, in order to be tested, and this would be made inside that "big f()", each time anyone calls it. To avoid repeated tests execution (hence consuming performance), they could only communicate in case of failing. Since they are compile-time (according to your examples), the optimizer could solve them, realizing that they work, meaning they would do nothing, and then it could decide to eliminate them at compile-time, "because they are useless" (unreachable code). For the tests that accuse an error, the programmer should fix them right away, in order to them become potentially "useless" too - thus eliminated too.
    Pros: *design continues to be as safe as before, despite tests intrusion* .
    Cons: *tests would be processed several times, unless the optimizer decides to wipe them out* .

    • @ContinuousDelivery
      @ContinuousDelivery  2 ปีที่แล้ว +3

      My recommendation is in two parts, focus on the externally visible outcome and never break encapsulation for a test. So if your function has lots of parts, test how that matters to a user of this code. If it doesn't, don't implement it that way! Splitting into smaller function can be fine if it helps readability, but don't expose those functions or test them, other than via the public interfaces that expose the behaviour that they represent. The fact that your public function is composed of many, smaller, private functions is implementation detail that your tests should not care about. There are three types of test, focus on those and test for the outcomes. th-cam.com/video/W40mpZP9xQQ/w-d-xo.html

  • @EricKolotyluk
    @EricKolotyluk 2 ปีที่แล้ว +1

    Thank you for using that all-important phrase "cognitive load." Years ago I overheard someone remark out loud "Fucking UI! Don't make me think." While he was referring to the User Interface or User Experience, it applies equally well to the Developer Experience. These days I am passionate about improving the Developer Experience and especially passionate about Reducing Cognitive Load to improve that experience.
    Personally, I think we will always have Declarative and Imperative styles... they are not different paradigms. My sense is that it requires more cognitive load to design and implement a good Declarative framework, but that it results in less cognitive load to use a good Declarative Framework.

  • @markhathaway9456
    @markhathaway9456 2 ปีที่แล้ว

    Just as we use functions to ensure things like mathematical correctness, using functions thus improves "clarity" over any home-made code to do detailed things. So, clarity and correctness may provide both quality, brevity, proper degree of abstraction, and speed of work.

  • @selocan469
    @selocan469 ปีที่แล้ว

    I made research on both and I think that;
    - comment on this video is right on the spot, where it is stated "Not a paradigm shift but syntactic sugar"
    - Any general purpose language which is assumed imperative can bu used as declarative one so to speak. You just write or acquire a well written high level API to do a specific job and suddenly you have your "eureka" moment. In the example given here, you need a collection API that provides high level services such as sort, filter whatever.
    From my point of view this total nonsense. Calling high level API as the flashy paradigm shifting name "declarative" and portraying "non FP languages" as low level code enforcing environments. It is the reinvention of the wheel in my opinion. This is basically using high level interfaces in an easier manner and stylish manner. I am sure FP community state C# to be considered an imperative language since it is an OOP language but what about the collection modification capabilities provided with "LINQ", what??? the language suddenly became declarative. How is this possible, unbelievable. Pffft!
    Yeah let me explain it to you then. C# language support both "delegate (function pointer in ancient C language)" and "anonymous function with lambda declaration operator(=>)". Well without "lambda declaration operator(=>)" anonymous support it would be just a little bit less syntactic sugary

  • @jethrolarson
    @jethrolarson 2 ปีที่แล้ว +1

    I dislike the idea that all turing complete languages are equivalent. Sure from a computer science perspective its true but it's like saying all artistic media is the same because they all can be used to represent any idea.
    You can create a castle out of Legos or yarn and your choice matters

  • @arieljuod
    @arieljuod 2 ปีที่แล้ว +1

    Isn't `words.append(word)` actually a declarative statement? an imperative statement would be something like: get the length of the list, put the word at length+1, right?

  • @calvinsadewa4752
    @calvinsadewa4752 2 ปีที่แล้ว

    Good content! I have a tiny bone to pick though:
    On 5:37, on Comparing Natural Language mapping of Imperative vs Declarative version of take word of 5 length,
    I think the choice of language used affect the easiness of mapping, in this case python is mostly focused on imperative style readability.
    For example, writing Declarative version on SQL would be much easier to be mapped to natural language:
    SELECT word FROM dictionary WHERE LEN(word) = 5
    which would easily be mapped to: Take word from dictionary which length is 5

  • @kayakMike1000
    @kayakMike1000 2 ปีที่แล้ว

    Just going to feed the algorithm. And you know, Linode is pretty durn good cloud provider. I will click those links after the show!
    Hmmm... i think i am more of an imperative programmer...

  • @hfspace
    @hfspace 2 ปีที่แล้ว

    i think what really leads to a higher cognitive load is quite subjektive and depends in case of functional programming on how you are used to think in terms of combinating operators to achieve your goal. This in fact is a way to think that has to be trained. The advantage of operator combination, at least if you try to use them in a way that is side effect free, reduces the need to think of all side effects and since all the operators should be thoroughly tested, it should give you more security that your code does not contain bugs (though you still have to make sure, that it does what you intend it to do, but that is more a problem of understanding the operators. that part might be a trade off to imperative programming)

  • @Techiesse
    @Techiesse 2 ปีที่แล้ว +2

    Saying functional programming is the same as declarative programming is a mistake in my opinion.
    Tldr: In my opinion, Dave here only shows his bias for imperative programming because it's what he's comfortable with. If his background was lambda calculus and fp languages he would think the other way around.
    And the example is far from ideal. Functional programming is all about abstraction that's correct, and how far you abstract is a design decision pretty much how much you show implementation details also is.
    Trying to argue which syntax is preferable here will always bring the observer bias to the table. For me having studied and used fp for some years now I always prefer the filter version when available. Had he created a proper named function instead of using a lambda the benefit would be more evident even in python.
    But for those who fell comfortable with structured programming giving it up for function composition will be a hard time.

    • @ContinuousDelivery
      @ContinuousDelivery  2 ปีที่แล้ว +2

      I don't think I said "functional == declarative" in fact I think I said the opposite, they are independent. 😎
      The rest I agree with, except all programming, at least all good programming, is about abstraction, not just functional. Does functional programming allow me to do things I couldn't otherwise do - no. Does it make some of those things easier, yes, sometimes.

    • @Techiesse
      @Techiesse 2 ปีที่แล้ว

      @@ContinuousDelivery I think you didn't actually say FP == declarative. But the example used for declarative is a functional style and I got the feeling they were used as synonyms throughout the video. In contrast, DDD talks about declarative design using (old) Java as an example (so imperative). Had you used that approach + the specification pattern (also in DDD) in the example and I think the declarative vs imperative would be clearer. But I don't really know if that was you primary intention and I guess the conclusions would be different.

    • @ewerybody
      @ewerybody 2 ปีที่แล้ว

      "In my opinion," he literally says this himself! 7:20

    • @ContinuousDelivery
      @ContinuousDelivery  2 ปีที่แล้ว

      @@Techiesse I think my video says the same as DDD, or something very similar 😉

  • @calvinsadewa4752
    @calvinsadewa4752 2 ปีที่แล้ว

    What about ease-of-optimizability? For me it's one of the defining difference between between Imperative vs Declarative (in concrete term, PL/SQL vs SQL). Declarative style is easier to be optimized (less code change) compared to Imperative style.
    After thinking about it a bit, one of Big reason ease-of-optimizability is harder in imperative style is because imperative style is highly dependent on mutating program state, which give blocker on ability for compiler/planner/optimizer to do optimization while still maintaining correctness.
    For example, in Declarative style, if you do two filter on list/data, compiler/planner might combine these two filter (aka compose) into a single filter and execute it in a single loop.

    • @calvinsadewa4752
      @calvinsadewa4752 2 ปีที่แล้ว

      This issue of ease-of-optimizability might have impact on modularity of code. For imperative style, to have multiple condition/filter to be executed performantly, you would need write/adjust code to execute it all in a single loop. Making the loop as a central point of logic aggregation.
      that being said, i think modularity is more important that optimizing code for most of the time, so most of the time you should pursue better modularity rather than worrying about performance.

  • @yapdog
    @yapdog 2 ปีที่แล้ว +5

    Yep, I'm in total agreement with you that language is less important than design. Great observation.

  • @mattongbp
    @mattongbp 2 ปีที่แล้ว

    Since restful & graphql had long defined all the data function...Why is there is still a need of Imperative programming language??

    • @ContinuousDelivery
      @ContinuousDelivery  2 ปีที่แล้ว +2

      Lot's of reasons, but one of them is that sometimes code needs to be fast, an declarative implementations are usually a lot slower, at the limits of performance.

    • @stefanomontani6008
      @stefanomontani6008 2 ปีที่แล้ว +1

      @Continuous Delivery So damn slow (as I learned after an overly strong injection of MS Blazor...) that right now I would like to run away from everything declarative and go back to imperative web ui development. I want back fast code, easy debug and full control on the code of my apps. I found this video because a was almost desperatly searching a voice not enthusiastical about declarative programming, a thing which today seems more and more difficult.

  • @gardnerjens
    @gardnerjens 2 ปีที่แล้ว +1

    i would say .append is declarative feature nested inside imperative

  • @ParkourGrip
    @ParkourGrip 2 ปีที่แล้ว

    I just want to add a side-note to the example.
    If we realized that the list of words was so massive that it would make sense to use multi-threaded processing to do the filtering faster.
    The imperative solution would require a lot more change to make it work multi-threaded.
    I'm not exactly sure if Python has something similar but in Rust for example all that is necessary to change the declarative solution to use multi-threading is instead of using a normal iterator we use something that's called a "parallel iterator". (This is 1 line of code.)
    These declarative solutions are pretty powerful at abstracting parallel processing.
    And this is not only limited to multi-threading, one could imagine a iterator object that runs the declared operation on an entire cluster of computers.

    • @TinBryn
      @TinBryn 2 ปีที่แล้ว

      I think that point about changing from a normal iterator to a parallel iterator hints at a more general concept. That is that the "declarative" style is more composable, you have a set of simple pieces that can be plugged together to make something more substantial.

    • @ContinuousDelivery
      @ContinuousDelivery  2 ปีที่แล้ว +1

      Except unfortunately that doesn't work in practice. The overhead of doing the work to automatically split the problem up into parallel steps, then merging the results back together at the end results in an orders of magnitude reduction in performance for even two threads, and the costs increase exponentially as you add more threads. One of my colleagues did an experiment to measure this, demonstrating that claims for "auto-parallelisation" in languages don't hold up. Let's imagine we are going to increment a number 500 million times, here is how long it takes in milliseconds on an Intel processor a few generations old:
      Single thread 300
      Single thread with lock 10,000
      Two threads with lock 224,000
      Even if you do lock-free concurrent programming:
      Single thread with CAS (Compare And Swap) 5,700
      Two threads with CAS 30,000
      So for the best case, ANY form of concurrent execution is 100 times slow, so you'd need more than 100 threads, but then the costs go up again! This is an unsolvable problem. Look at Amdahl's law.

    • @ParkourGrip
      @ParkourGrip 2 ปีที่แล้ว

      Not sure about that.
      I used that parallel iterator to search a block of memory for all memory addresses that contain patterns like for example "40 57 48 83 EC ?? C6 81 ?? ?? ?? ?? 00 48 8B F9".
      The parallel iterator was 5x faster then a standard one on a ryzon 5900x.
      Of-course that the benefits for parallel processing will highly depend on the problem at hand.
      Some problems will work well some will not.
      All I know is that I changed one line of code for a 5x improvement and that improvement would probably not be worth the investigation effort of implementing the algorithm for parallel processing in a imperative way.
      Changing 1 line of code, testing if it's faster, reverting back if not is worth it if you ask me.

    • @bobbycrosby9765
      @bobbycrosby9765 2 ปีที่แล้ว +1

      ​@@ContinuousDelivery yeah, trying to perform a bunch of operations that each takes half a nanosecond isn't gonna be amenable to locks or CAS.
      It also doesn't have to be automatically. In Clojure, for example, you can ask it to do a map in parallel by changing the call to pmap. You don't have to do anything special to run an experiment.
      Elixir has a pretty cool module called Flow for this. It largely mirrors the Stream module. So you just replace your Stream.map and Stream.Filter with Flow.map and Flow.filter. You have to pipe it through a Flow.partition ahead of time though, which will configure worker procs based upon the number of cores your machine has.
      This really is no different than a pool of worker threads, but from my testing the worker thread solution is gonna look way different than the single threaded imperative solution. In contrast, these parallel solutions look almost exactly the same.

  • @qj0n
    @qj0n 2 ปีที่แล้ว

    I think in this imperative example we need to consider also the cognitive load required to understand the result from steps to be made. While in imperative code we are explicit about the steps, it's not always clear, what will be the result. In declarative code we are explicit about the result we want to achieve, but it's not clear what steps will be made. Of course in this particular example a developer would immediately recognize a pattern and know this is filtering. But in case of e.g. matching items from two collections, Join method (or equivalent) will be probably understood quicker than nested for loops or a loop and a dictionary
    Unless we need to take special care about performance, hiding the exact implementation often reduces the cognitive load, as long as we just accept that we don't know, what is happening below

  • @KulaGGin
    @KulaGGin 2 ปีที่แล้ว +1

    first xD
    Got recommended this vid by TH-cam.

    • @yapdog
      @yapdog 2 ปีที่แล้ว

      As did I in early 2021 (or was it in 2020?). Since then, this channel has grown from just a few thousand at that time, to over 106K. Even if you don't _think_ you need the info from this channel right now, it's a good idea to subscribe. You never know what the future may hold.

    • @ParkourGrip
      @ParkourGrip 2 ปีที่แล้ว

      Glad to see you are alive. :)

  • @arraymac227
    @arraymac227 2 ปีที่แล้ว

    What are the opcodes in a Lisp machine?

    • @ContinuousDelivery
      @ContinuousDelivery  2 ปีที่แล้ว +1

      They are the imperative code that LISP ultimately gets translated into. They are what makes programs capable of being run on a computer.

    • @arraymac227
      @arraymac227 2 ปีที่แล้ว

      @@ContinuousDelivery Given that I have seen an entire Lisp interpreter written with a single page of Lisp, I am not convinced that Lisp, beyond that interpreter, needs to be translated into something else.

    • @ContinuousDelivery
      @ContinuousDelivery  2 ปีที่แล้ว +1

      @@arraymac227 Well I am afraid that you are certainly wrong. How does the processor in your computer interpret Lisp - it doesn't Lisp is translated, ultimately, into a series of machine code instructions that the processor that you work on uses. This is simply how computers work. There were attempts in the past to optimise processors for certain languages, including Lisp I seem to recall, but that doesn't change the fact that you need to change stuff into a collection of 0's and 1's to instruct the computer.

    • @arraymac227
      @arraymac227 2 ปีที่แล้ว

      @@ContinuousDelivery So, I am correct, once the one page is translated, every computation works at a higher level. All the machine code does is manipulate that one core structure.

  • @jkone27
    @jkone27 2 ปีที่แล้ว +1

    For is imperative, for each is declarative, as it uses enumerable abstraction over indexes , so your statement is wrong

  • @am3mptoskatharos105
    @am3mptoskatharos105 2 ปีที่แล้ว

    Interesting video.
    In the first example of code with the lambda, a list comprehension would have been a better example for a declarative method, no?

  • @ewerybody
    @ewerybody 2 ปีที่แล้ว

    though ☝ ... I wouldn't dare to name something "dictionary" in Python that is clearly of type list :D

  • @EmNudge
    @EmNudge 2 ปีที่แล้ว +2

    I'd like to argue the opposite position, if I may.
    I think you rightly defined the problems with your own argument with the prefix of "it's possible".
    Your argument at the beginning as I understood it is that declarative code requires abstractions and abstractions are harder to reason about. It's a solid argument, but the examples weren't very supportive of this argument.
    From a performance standpoint, imperative code often models how computers actually work and so functional/declarative code CAN be a lot less performent. If we want speed, we probably want something closer to assembly or at least how computers actually work (C/C++, Rust, Fortran, etc).
    HOWEVER, in the beginning of the video, you mentioned how declarative code is harder to reason about and we write code for humans first. This I do not agree with. Imperative code itself requires similar abstractions. How am I to know what `ArrayList.append` does under the hood? Perhaps there is hidden behavior!
    The difference is that you're familiar with Java's and C's APIs. If you were not, things would be similarly confusing. It isn't that functional programming paradigms don't introduce novel concepts, but we have to assume people coding in both are familiar with both.
    Once we've established that, declarative code is MUCH easier to reason about. This is because declarative code has the benefit of declaring intent. I know what the programmer MEANT to do. I can quickly scan the code and see if the intent aligns with the implementation. I do not get this benefit in imperative language. We require a plethora of comments to solve for this. In more declarative code blocks, the code is the comment.
    I'd like to write a lot more on this, but I'm going to need a video at least as long as the one you posted to make a sufficient response.

    • @martinbakker7615
      @martinbakker7615 2 ปีที่แล้ว

      If one needs comments to explain the code I would suggest to read Clean code by uncle Bob. Comments indicate bad code.

  • @digitalspecter
    @digitalspecter 2 ปีที่แล้ว +1

    1. Take an imperative language to demonstrate imperative vs. declarative
    2. Complain about unfamiliarity and language quirks
    3. Say that language isn't that important anyway
    4. Profit?

  • @VictorMenge
    @VictorMenge 2 ปีที่แล้ว +5

    Normally a fan of the videos here, but the example used was pretty bad and I thought the way it was approached was incredibly biased towards the imperative style. By using C# or JavaScript (or any other implementation of the filter that makes the use of the dot notation) the use of the filter function would have been not only more readable but would make it immediately obvious why its so much better than the imperative version. The python functions used in the way they were are terrible to read as opposed to using the dot notation or the pipe operator.
    I also completely disagree with the cognitive load being an issue. Do we really want to lower the bar so much to the point understanding what a filter function does counts as a negative towards something? We're aren't asking juniors to know how to use monads on their first day.

    • @EmNudge
      @EmNudge 2 ปีที่แล้ว

      I'm not sure I agree with your tone, but I definitely agree with the sentiment.
      If we consider understanding Java's `ArrayList.append` mandatory then the same should be assumed for `filter` and `map`. They don't need to be re-learned on each use. There is an initial hurdle and then after the declarative code becomes much much simpler to read.
      "I don't know what filter does therefore it's more complicated" to me sounds as good of an argument as "I don't know what a switch statement is, therefore Java is more complicated"

  • @yurivendruscolo7676
    @yurivendruscolo7676 2 ปีที่แล้ว +1

    this video seems to be only concerned about Python, where declarative and functional programming are not valued or the objective, rendering the syntax and semantics inappropriate. Most languages that have a more functional, less imperative focus and approach will be more expensive than Python when using a functional style. Take clojure for example with (map inc [1 2 3]) or (remove even? (range 10)). there is no noise, only exactly the required information to perform the operations, and there is no ambiguity on which is the iteration operation, it is just a function, not a "template" to fill in, no syntax (like Python's for, in, indentation).
    even Python actually has a better syntax for this: [transformation(x) if condition(x) for x in some_xs].

  • @Immudzen
    @Immudzen 2 ปีที่แล้ว

    I would have used return [word for word in dictionary if len(word) == 5] I find it simpler than both of those and easy to explain.

  • @ohwow2074
    @ohwow2074 2 ปีที่แล้ว

    I think that if you had written that example with C++'s new range adaptors then you would see that how declarative beats imperative in most of the cases. In C++20 you can write a few lines to handle a task of filtering a range with pipe operator. Try to implement the whole pipeline yourself and it will kill your productivity.

  • @adambickford8720
    @adambickford8720 2 ปีที่แล้ว

    The problem with the imperative version is it allows arbitrary complexity far too easily. In practice i haven't seen the typical map/filter/reduce type functions end up quite as complex as some of the multiple nested looping structures I've seen where every iteration sets booleans at various scopes to control the flow of the whole thing.
    For non blocking contexts writing imperative code can quickly get very complex without specific language support. For the functional version, its more an implementation detail.

  • @arraymac227
    @arraymac227 2 ปีที่แล้ว

    (5=≢) beats (lambda word: 5 = len(word))

  • @jeanvillete
    @jeanvillete 2 ปีที่แล้ว

    Dave, I respect your opinion, but I don't think you brought a good example.
    Most developers working on this called imperative styles, don't make good use of returning new filtered/mapped data from a method, also they passes objects to nested functions/methods that have their state changed on calling functions/methods, making the code hard to be read and debuged... Becoming a code impossible to deal with parellelization.
    I see that declarative and functional programming has standardized how to deal with structures specially around collections... how to filter, transform/map and collect... and how important immutable is (with hash code and equals) for structures and parellelization.
    Concurrency and thread safe data structures has been a very complex subject for a long time, but nowadays in case a Jr developer follows some few guidances around immutable and functional programming, this developer is probably getting success on his/her project.

  • @bobbycrosby9765
    @bobbycrosby9765 2 ปีที่แล้ว

    I pick languages more for the other features they provide, rather than declarative vs imperative. That said, there's lots of "funny", non imperative languages that offer some pretty unique features, so if someone decides to stick to imperative languages it can really restrict their world view.

  • @imel96
    @imel96 2 ปีที่แล้ว

    The arguments was based on the assumption that people use functional programming to write generic code. That's not really it. The main goal of functional programming is to use mathematical abstraction (higher order logic) into software development so we can create proof for things that we can't test exhaustively. See also formal methods, formal proof.

  • @richardhaw9757
    @richardhaw9757 2 ปีที่แล้ว +1

    declarative === job_security

  • @donniesmith512
    @donniesmith512 4 หลายเดือนก่อน

    It seems like all this talk with no meaning. You have no good points.

  • @jacobshore
    @jacobshore 2 ปีที่แล้ว

    So... not the point here at all, but list comprehensions are considered more pythonic than using filter. 😁

  • @ZonkethBKFST
    @ZonkethBKFST 2 ปีที่แล้ว

    You should make a hot dog.exe shortcut that when you open it on your computer it automatically Cooks a hot dog in your microwave.

  • @jethrolarson
    @jethrolarson 2 ปีที่แล้ว

    filter5LetterWords = filter(hasLength(5))

  • @ApprendreSansNecessite
    @ApprendreSansNecessite 2 ปีที่แล้ว

    I had the same feeling in your previous video: you say things concerning one or the other that could be said of both, for example you claim that `imperative_words` is composed of English words, but so is `declarative_words` (what is not understandable about "filter"?).
    I also notice a lack of consistency: you speak of algebra here to describe `imperative_words` but in your previous video you claimed that FP was about math, which made it difficult to reason about, while OO was about modelling, and that this difference made OO more natural to us humans who like to classify things (which I find odd provided that a Square cannot be a Rectangle in OO because of state, which is a problem that doesn't exist in FP where types are used casually to model the problem domain). When did algebra stopped being maths and became easy to reason about?
    You do admit you have a bias, but it doesn't cast away the fact that you make arguments on the basis of familiarity more than simplicity.
    I also want to point out that your indentation is more readable for `imperative_words` and you chose a language in which filter does not return a list. In JS `dictionary.filter(word => word.length == 5)` is all you need to write. There is value in using iterators when you chain list transforming functions, but the language, or a library, should provide facilities to use functions like `filter` alone. This argument is about API anyway, it is not conceptual.

  • @MuratKeremOzcan
    @MuratKeremOzcan 2 ปีที่แล้ว

    You have to study FP for about 6 months until it sinks in. It is an investment. After that, a fair evaluation is possible. I have not many that have put in the time, and went back to imperative programming.

  • @adicide9070
    @adicide9070 2 ปีที่แล้ว

    so there, tdd == declarative programming :D what wizardry :D

  • @xtinctspecies
    @xtinctspecies 2 ปีที่แล้ว

    For once I can’t like a video on this channel. Using Python to prove this is probably not for the best.