A friend of mine introduced me to the concept of functional programming this weekend. As someone who's been writing procedural PHP since before PHP supported objects and then gradually transitioned to an OOP-based mindset, the concept of functional programming is kind of blowing my mind.
For those of you unfamiliar with functional programming in PHP...
@davidbhayes ' video on banishing loops with functional programming is a good primary on the basic concept.
If you are more familiar with functional programming in PHP...
I have some question:
- what are some of the main benefits?
- is it mostly about simplifying loops?
- any examples of projects that make good use of functional programming? github links?
- what are the main drawbacks? gotchas?
- Is OOP on its way out?
I haven't been this intrigued by a programming concept in a while. Super curiosu what the dev.to community thinks!
Top comments (27)
The concept of functional programming definitely helps with code maintainability. But, in my opinion, it's like TDD in PHP. We all talk about how great it would be to get to TDD, but don't have any practical experience in coding TDD.
I think sometimes we place too much focus on wanting to code everything "the right way" that we get caught up in paradigms that don't necessarily add any value to the code we are writing. PHP OOP is not on its way out, and I think we will be back to that as the primary paradigm when we find that coding in the functional paradigm is harder than coding in the OOP paradigm.
For the record though (and I've only really been doing this in JS so far).... TDD is really lovely. It sucks when you have to mock objects and deal with cluster effs like WordPress... but refactoring a system with good test coverage is a total joy.
... but oddly, I don't TDD so much with PHP because of the required mocking. So I guess what I'm thinking here is... if we use simple data and functions, it's easier for us to do TDD... and making the better path the easier path is probably not a bad thing.
I LOVED TDD when I first learned about it. I implemented it everywhere I could! Later, it got to the point where the amount of time I was taking to write code to test my code was more time than to write the actual code. I lost the value in doing the testing to begin with.
Think about it this way...time is something you can't buy back. At some point you have to evaluate how much time you are spending on the things you are doing. When you're spending more time to write tests than to write code, you have to compare that to the amount of time you would be debugging code to solve issues or add capabilities.
For me, going TDD, or even unit testing all together got to the point where it wasn't worth the value of effort I was putting in. That is why I went to automated functional testing.
I found that if you place your focus on testing your public facing capabilities, then you can catch more bugs with less amount of code. Which is the reason I spent time building out FireTest ua1.us/projects/firetest/
Now all of my efforts are focused on exercising the functionality and not on testing each individual piece of code that implements that functionality. And when a bug is reported, I write a test that will replicate that bug and include it in my test suite to ensure that bug never happens again.
I can see how that'd be the case. For me, the thing I like about TDD is that I can clearly outline my expectation of how code should work so that it feels logically sound. And then, if I need to refactor it, I can confirm that everything is still ok. I would never want to refactor a big project that was only tested from public facing stuff. I think that if you need to mock stuff TDD can be slow and a pain. Almost all of my tests are expect().toBe();... there's almost nothing to writing them. I keep it deliberately simple because if it's hard to test, I'm probably writing brittle code. (Not to say you are or were either. Just something that I noticed about my own coding practice).
Nice! Different stuff works for different people. I find no value in writing individual units. Never have I ever caught a bug because a unit test failed. I did catch a bug because my functional test failed though.
Even recently. My test was written in a way such that I expected that if the user tried to access a class that didn't exist within a DI container, that the code would throw an exception. Turns out that the code was written such that if the DI container didn't contain the class it would throw an exception in php 7.1, but not 7.3. That is a bug that I wouldn't have been able to catch if I mocked everything and spent time writing individual unit tests.
Totally agree. Different strokes.
And I hear ya about the need for other tests and assurances too. I wouldn't ever think, "I have unit tests, so everything is perfect."
And I've written loads of code that isn't tested at all and had zero problems.
I can say this much. I DEFINITELY dislike TDD when working with OOP. :)
What downsides of FP do you think will make it harder that OOP? (I don't disagree, I just don't know enough about FP to be able to think of any)
Going full FP within a UI application or REST application becomes pretty difficult at the framework layer. I think this works great for business logic, but when it comes to persisting states and maintaining data structures, by definition, you cannot go functional because it breaks the first rule of functional. FP written code requires that you have inputs and outputs. Nothing outside of that.
So think about a function isLoggedIn($loggedIn). You'll have to continually pass in $loggedIn everywhere. To write isLoggedIn() functionally, you would need to pass in the state and have the method determine if you were logged in based on the state. Imagine how many places you would need to get the state every time you would want to know if a user is logged in.
You lose the abstraction of OOP.
Isn't this kind of moot though? I mean, everywhere that you're calling
isLoggedIn()
you should have access to the state. Right? Is it really worse to callisLoggedIn($user)
?Is this where pipelines come in to play?
So, OOP architecture wants us to abstract isLoggedIn() away from the rest of the application. That way we don't have to write redundant code like
To me, it is much more efficient for me to go to one place to understand where the user is logged in. Rather than having to resolve a bunch of dependencies before I can know if my user is logged in. Example:
What if the system were setup in a way where the user being logged in needed more data than just the user object. For example, they also needed a cookie and a line in the database that says they are logged in. To keep my isLoggedIn() function in the FP paradigm I would then need to do something like this:
Now instead of just getting a user, I need to get the cookie and the db value. ALL just so I can write my functions in the FP paradigm.
We definitely switch gears quickly. I'm not saying so much that FP is "the right way" but that I've personally found having pure functions and coding in a functional style solves a lot of problems for me. And that when applications get huge and complicated, having things split out nicely and not contingent on object state, a lot easier to reason about.
I still really like objects as a way to structure my code, but then I write methods in a functional (for the most part) style.
You definitely can write PHP in a functional style. It’s missing some of the niceties in syntax of a functional language though.
Mostly you’re just writing code to minimize the amount that you mutate data.
If you’re writing in a functional style in PHP it can still be useful to use OO concepts like classes as, essentially, tiny namespaces where you can determine the level of privacy of methods. Interfaces and abstracts can also be pretty useful to define custom types.
Such as?
Pipeline and pattern matching. There’s probably some more syntactic sugar I’m not thinking of right now.
Basically it just results in “uglier” code.
You could probably approximate pipeline with some method chaining if you didn’t want to nest array functions as arguments to other array functions.
You can simulate pipeline type stuff with array reduce and unary functions.
What I really miss is built in currying/partial application, less verbose anonymous function syntax, and auto closures.
Oh, and auto return statements.
When you get used to writing JS and other FP stuff like (x,y)=> x + y and you can do (x) => (y) => x + y, it's really nice.
Pretty sure you can curry in PHP?
Edit: never mind, read your post wrong.
All good. :D I'm no FP expert and welcome corrections. Who knows... maybe my comments will yield some answers and it'll turn out that what I didn't think you could do easily, you can.
These are some nice to have features in functional programming languages.
Function can be passed to other functions and returned as values. In short functions should be treated as values.
closures the inner function can use the variables of outer function regardless of where it is running.
Map, filter, reduce like functions.
pipeline operator.
Unless I'm misunderstanding the terms, I believe PHP has all of these features except a pipeline operator.
It's a different language, but check out the ramda library for examples of really awesome functional programming. The documentation is great. Chris Okahravi has a superb video tutorial series on every single function in ramda too. —> youtube.com/playlist?list=PLrhzvIc...
If you REALLY want to get into it, check out Prof. Frisby's Mostly Adequate Guide to Functional Programming github.com/MostlyAdequate/mostly-a...
+1 for Frisby’s
.... I was setting up a new project in PHP storm, and when setting the language level for the CLI interpreter I noticed the option to set PHP 7.4, which has "short closures" and arrow functions!!!!!!!!!!! :D :D :D :D :D
wiki.php.net/rfc/short_closures
"Variable binding
The position of this RFC is that the shorthand syntax is to allow anonymous functions to be used as easily as possible. Therefore, rather than requiring individual variables be bound to the closure through the use ($x) syntax, instead all variables used in the body of the anonymous function will automatically be bound to the anonymous function closure from the defining scope."
Oh happy day!!!
22 Yes, 30 No. I don't think this is happening?
I forgot to post the later one.
wiki.php.net/rfc/arrow_functions
The linked RFC was reissued as the above.
.... edit... turns out this one was also reissued as the following.
wiki.php.net/rfc/arrow_functions_v2
51 yes, 8 no
I'm fascinated by FP and I maintain several PHP codebases. I'd love PHP to be more functional than it is.
What PHP has:
What PHP lacks:
require
/include
the right file all presuppose you're going to do OOP).:D I’m so happy to hear that you’re excited about this!!!
I've written about it, so instead of just copy and paste a whole article, I'll share this link, these are my thoughts about FP in PHP :
medium.com/swlh/functional-program...