Golang Functional Programming (and why you should NOT do it)

Поділитися
Вставка
  • Опубліковано 6 лип 2024
  • Golang Functional Programming (and why you should NOT do it)
    Did you know we already have functional programming support in Golang? In this video, I'll talk about how you can start writing functional code in Go and some benchmarks on functional Go.
    Should you do it? Watch and let's find out. Enjoy!
    --
    Golang Dojo is all about becoming Golang Ninjas together. You can expect all kinds of Golang tutorials, news, tips & tricks, and my daily struggles as a Golang developer. Make sure to subscribe if you look forward to such content!
    Get Your Golang Cheat Sheet! - golangdojo.com/cheatsheet
    Git repos & notes - golangdojo.com/resources
    Golang Releases - • Golang 1.18 Overview (...
    Golang Informative - • How much do Golang dev...
    --
    Timestamps
    0:00 Intro
    1:08 Filter method
    2:05 Filter positive function
    6:30 Stream struct
    10:43 Benchmarking
    12:29 Outro
    --
    #golang #goprogramming #golangdojo
  • Наука та технологія

КОМЕНТАРІ • 36

  • @GolangDojo
    @GolangDojo  2 роки тому +1

    📝Get your *FREE Golang Cheat Sheet* -
    golangdojo.com/cheatsheet
    PS - Video idea accredited to Amir from our secret Discord server. Find out how to join by signing up for our newsletter!

    • @heliumhydride
      @heliumhydride 2 роки тому

      HELP NEEDED: I have tried multiple emails and I haven't received my cheatsheet yet

  • @michalbotor
    @michalbotor 2 роки тому +22

    i would say that allowing != supporting
    from what I know, in Java, stream methods are smart: you tell them to filter this, map this, reduce that, but they do absolutely nothing... until you tell them to fire at the end.
    only then your whole chained instruction is being read an analysed and compiled to an optimized version like the one that you showed in Go in the end.
    that is the whole idea: you tell me what you want, and let me worry how we will get there as fast and as efficient as possible.
    functional programming can be unbelievably productive, safe, and fast too, but it needs to be natively rooted into the language itself, otherwise it becomes useless.
    but maybe that is ok, not every language has to be functional, and go can keep embracing its imperative nature of explicit looping and branching. i still love it! 💙

  • @cookie_of_nine
    @cookie_of_nine 2 роки тому +35

    I have two comments here, first is that like others have suggested, there is a difference when running two filter operations than just a single operation that does both checks. Here are results of doing so, where a single filter is nearly as fast as a single loop:
    BenchmarkFPTwoCalls-8 794659 1440 ns/op 128 B/op 5 allocs/op
    BenchmarkFPOneCall-8 5266573 215.9 ns/op 8 B/op 1 allocs/op
    BenchmarkTwoLoops-8 1000000 1291 ns/op 128 B/op 5 allocs/op
    BenchmarkOneLoop-8 7161225 149.1 ns/op 8 B/op 1 allocs/op
    I'm running this on a fairly slow ARM chip (a chromeOS tablet) so the numbers are different but the like the original 2-filter / 2-loop runs, the 1-filter/1-loop versions are similar. I suspect one difference in both cases is less the generics, and more the fact that function calls (which are likely not inlined) are slower than comparison instructions, although still quite competitive.
    Second comment:
    A big difference between 2 -> 1 steps is likely the memory use (as shown by the columns added by --benchmem), as the two-pass versions must (re)allocate memory several times because they need to grow slices several times (mostly during the isPositive step). Different data, especially one where the final results have significantly more than a single result may show less of an advantage. Here is the results with an input array of a million random integers:
    BenchmarkFPTwoCalls-8 9 134851111 ns/op 62761522 B/op 73 allocs/op
    BenchmarkFPOneCall-8 25 50334985 ns/op 21083401 B/op 35 allocs/op
    BenchmarkTwoLoops-8 14 85377324 ns/op 62761466 B/op 73 allocs/op
    BenchmarkOneLoop-8 31 40581509 ns/op 21083400 B/op 35 allocs/op
    The benefit of one step now is now much closer to a 2x speedup, which does line up with the allocations. If you were to have Filter pre-allocate the output slice so it only needs a single allocation, you may have wasted space in the slice, but actually less garbage due to not throwing away the old slices, and everything speeds up ~2x again.
    BenchmarkFPTwoCalls-8 14 87367445 ns/op 16007175 B/op 2 allocs/op
    BenchmarkFPOneCall-8 48 26948692 ns/op 8003588 B/op 1 allocs/op
    BenchmarkTwoLoops-8 45 28888468 ns/op 16007172 B/op 2 allocs/op
    BenchmarkOneLoop-8 68 17966153 ns/op 8003588 B/op 1 allocs/op
    Essentially, be careful with benchmarks and what you are actually measuring, as here a non-trivial portion of the time (half of it or more) was simply from allocations / GC on top of of the choice of filtering method.

  • @dominikvrbic7199
    @dominikvrbic7199 2 роки тому +17

    One question why did you not benchmark a isEvenAndPositive solution to have a more closer test to your one loop solution?

  • @gerardgauthier4876
    @gerardgauthier4876 2 роки тому +2

    I'd like you to dig a little deeper into GoLang's type system. Here's something that's achievable in GoLang's type system but I found this out by trial and error and not by reading any docs. Basically I created a type that allows me to create a function which can return the current answer and a function closure which can be called to return the next value and a function closure... This is a basic example of lazily evaluating a fibonacci sequence. This is where the type system really gets interesting.
    package main
    import (
    "fmt"
    )
    type Func func() (int, int, Func)
    func (f Func) call() (int, int, Func) {
    return f()
    }
    func fib_aux(x int) int {
    if x == 0 {
    return 0
    } else if x == 1 {
    return 1
    } else {
    return fib_aux(x-1) + fib_aux(x-2)
    }
    }
    func fib(x int) (int, int, Func) {
    return x, fib_aux(x), Func(func() (int, int, Func) { return fib(x + 1) })
    }
    func main() {
    d1, n1, f1 := fib(15)
    d2, n2, f2 := f1.call()
    d3, n3, _ := f2.call()
    fmt.Println(d1, n1)
    fmt.Println(d2, n2)
    fmt.Println(d3, n3)
    }

  • @CriPPle358
    @CriPPle358 2 роки тому +2

    This feels fundamentally wrong, a true stream would be much faster. This doesn't operate in the same way the Java streams API does, this has lots of blocking and waiting for things to happen, in order to create something like this you should implement with channels chaining the functions together so you can actually have a single stream of data flowing through all the methods, having loops and waiting for one section to finish before starting the next is missing the point of what a stream is.
    Also Golang doesn't need lambda functions as it has first class functions, lambda is a bit of a hack in Java to help achieve a similar goal

  • @gerardgauthier4876
    @gerardgauthier4876 2 роки тому +3

    With generics and built-in support for higher order functions, you should be able to make a workable Optional[T any] type.
    Actually I just made a lazy generic seq with GoLang.

  • @SeaRich
    @SeaRich 2 роки тому +1

    js is full of those array methods. But for rang iterator is good in go. I’am from JS and was curious of those things.

  • @TheTotalGeek
    @TheTotalGeek 2 роки тому +3

    I’m a new subscriber and I must say you are doing a great job with your demonstrations. Thank you very much 😃

  • @hanwang7382
    @hanwang7382 11 місяців тому

    It looks to me the video should've been titled "Golang Functional Programming (how and WHEN you should not do it).😀It is a very nice feature combined with generics as long as you are not constrained by performance requirements.

  • @evertvankammen9588
    @evertvankammen9588 Рік тому +1

    Well even in functional languages you have to think things over. Its logical that if you loop through an array twice it would take longer then doing it once. Create a funtion that does 2 things at the same time in fp would also go faster. This not a good example.

  • @kamaleizhang
    @kamaleizhang Рік тому

    awesome

  • @thestemgamer3346
    @thestemgamer3346 2 роки тому +19

    this just sounds like a reason not to use Go

    • @michaelmroz7433
      @michaelmroz7433 Рік тому +6

      Golang designers have worked hard to create a language with the largest possible number of reason not to use it.

    • @user-br3hw1us7k
      @user-br3hw1us7k Рік тому +1

      @@michaelmroz7433 ahah indeed

    • @evertvankammen9588
      @evertvankammen9588 Рік тому +5

      You can create this example in any language. Bad programming is not the fault of the language

    • @Alexey-gp7vc
      @Alexey-gp7vc Рік тому

      @@michaelmroz7433 🔥

  • @thachnnguyen
    @thachnnguyen 2 роки тому +2

    Wrong comparison with the last benchmark. One can easily write a function that does it (isPositiveEven()), looping through the slice only once.

  • @nabbikill
    @nabbikill 2 роки тому

    lambda functions are indeed in go.

    • @nabbikill
      @nabbikill 2 роки тому

      example here:
      func run[T any](a T, f func(T) T) T {
      return f(a)
      }
      func main() {
      fmt.Printf("%d
      ", run(4, func(i int) int { return i }))
      }

  • @kevintanudjaja7597
    @kevintanudjaja7597 Рік тому

    your implementation don't match java/c# implementation. stream is not supposed to do multiple loops, it's lazy calculation, the function will be called when / at the iteration time.

  • @swagatochatterjee7104
    @swagatochatterjee7104 2 роки тому +6

    Functional programming isn't just about using hifgher order function! It's also about using design patterns like Monoids, Monads, Functors, Applicatives and so on. If you add them properly, say state monad, it can eliminate if err != nil statements completely from your program. That's where the power of functional programming lies. Is correct and readable code, better than code with little marginal performance hit? Absolutely!! Because code is written once, and read a million times. One optimization of the compiler down the line, these problems would be non existent. Heck go is even inspired by functional patterns such as error as value, or channels.

  • @hellelo.5840
    @hellelo.5840 Рік тому +1

    Go lang designers : let's make something people are not familiar with, weird and make them cringe, but we will work hard to make the language very fast just to trap them and torture them, they will probably make a a youtube channel about our language.😂

    • @nandomax3
      @nandomax3 9 місяців тому

      Sounds like you're new at programming, have you ever heard about C? 😅

  • @dibyojyotibhattacherjee4279
    @dibyojyotibhattacherjee4279 2 роки тому +3

    Golang wants you to be simple, no complicated sh*t, just be the simplest you can be ..... ☮

    • @losiu998
      @losiu998 2 роки тому +7

      filter is not simple?

    • @Alexey-gp7vc
      @Alexey-gp7vc Рік тому +2

      Map, filter and even reduce easier/simper/readable than loop.
      Many things are much simpler than imperative code with mutable state.

  • @MarcosVMSoares
    @MarcosVMSoares 2 роки тому +6

    Unfortunately. Funcional make more simple and safe

    • @MarcosVMSoares
      @MarcosVMSoares 2 роки тому +1

      @ThatGuyJamal I'm saying this after years of OOP and 2 year of Elixir to see how different and easy is functional and how much problem OOP create to itself to try to solve with some crazy technique that don't need in a functional paradigm

    • @KManAbout
      @KManAbout 2 роки тому +1

      @@MarcosVMSoares this is not OOP v functional this is iterative v functional.

    • @miniappletheapple
      @miniappletheapple 2 роки тому +1

      totally agree, it's crazy that golang dev need to write the same thing over and over, in elixir, you can just Enum.filter(&(&1 > 0 and is_even(&1)))

    • @miniappletheapple
      @miniappletheapple 2 роки тому

      @ThatGuyJamal Writing the same thing over and over is pretty bad for productivity, i think golang team can make a optimazated inline function.

    • @nandomax3
      @nandomax3 9 місяців тому

      var filteredList [ ]int
      for _,value:= range(list) {
      if (value > 0 && value % 0 == 0 {
      filteredList = append(filteredList , value)
      }