DEV Community

Discussion on: What are your thoughts on functional programming? In PHP?

Collapse
 
joshualjohnson profile image
Joshua Johnson

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.

Collapse
 
underscorefunk profile image
John Funk

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.

Collapse
 
joshualjohnson profile image
Joshua Johnson

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.

Thread Thread
 
underscorefunk profile image
John Funk

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).

Thread Thread
 
joshualjohnson profile image
Joshua Johnson

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.

Thread Thread
 
underscorefunk profile image
John Funk

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. :)

Collapse
 
underscorefunk profile image
John Funk

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.

Collapse
 
ohryan profile image
Ryan

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.

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)

Collapse
 
joshualjohnson profile image
Joshua Johnson

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.

Thread Thread
 
ohryan profile image
Ryan

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.

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 call isLoggedIn($user)?

Imagine how many places you would need to get the state every time you would want to know if a user is logged in.

Is this where pipelines come in to play?

Thread Thread
 
joshualjohnson profile image
Joshua Johnson

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

$user = User::getUser();
isLoggedIn($user)

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:

$user =
$cookieValue =
$databaseValue =
isLoggedIn($user, $cookieValue, $databaseValue);

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.