My own style has evolved as I've learned over the years. I use classes less and make them smaller. I use enums and ADTs a lot more these days. But I rarely go whole-hog functional unless it is in tests.
Smalltalk the proverbial Object Oriented language already had first-class functions a.k.a lambdas (called BlockClosures in Smalltalk). So it's not like you should choose between OOP-classes OR lambdas. Having them both is better than having just one of them.
Ironically, Smalltalk syntax for lambdas/closures remains my favorite. No “trailing closure” hack that doesn’t scale and looks ambiguous (looking at you swift). No one line limitation (looking at you Python). No ambiguity between function/method bodies and closures (looking at all of you members of the curly brace Algol descendents). No “you have to reference all of the arguments or you can’t use it” (looking at you Elixir).
There were two capital ironies with Smalltalk’s free functions (closures). Smalltalk USED them. It may have been “objects all the way down”, bit they drank closures the whole way. Both the extensive class library and your own code. I coded and invoked more closures in Smalltalk for a given unit of functionality than I have in any other language to date.
What’s even more ironic to me is that closures/functions in most compiled languages are a semantic facade. You see them in your mental model, but the language/library doesn’t model them for you to interact with. In Smalltalk, you could send messages to closures, AND you could add your own.
I have always found it ironic that in Smalltalk, the “only objects ultimate”, I was actually exposed to closures/functional patterns more than I have been in many other systems that supposedly were more about just that.
Another innovation of Smalltalk which may not be familiar to everybody is that all control structures in Smalltalk are implemented by passing closure-objects as arguments to methods like ifTrue:ifFalse .
"… compiler optimises ifTrue:ifFalse: and friends using special opcodes, so as to avoid having to create closures…"
https://stackoverflow.com/questions/32662354/optimising-iftr...
It's like saying that functional languages implement tail-recursion as iteration in order to "get rid of" recursion.
But the programmer can still think in terms of recursive calls in their program, and can reason about the correctness of the program by assuming it works by recursion.
Except when they do!
When they expect to be able to change those methods, because they have been led to believe they are just methods.
g := [:y :z |
|f|
f := [:x | y * x].
f
].
(g value:3 value:2) value:4.
(g value:2 value:3) value:4.
https://wiki.c2.com/?SmalltalkBlocksAndClosuresSomehow, like always, progress is full of left turns, and we could have been much better if the IT world wasn't busy reinventing them.