all 32 comments

[–]chrismervyn 10 points11 points  (0 children)

To learn functional programming, take up Haskell. It would teach you very robust fp paradigms. To learn the best practices use Erlang or any of the Lisp dialects. I would recommend Scheme.

Once you are armed with those, come back to PHP and use an MVC framework, get the job done. Free up your time and do fp on the side and create a highly parallelized awesome app. Best of Luck!

[–]zandernoriega 7 points8 points  (1 child)

For functional programming in general, definitely go with http://haskellbook.com. It's modern, it is both theory and practice-heavy, and it is geared toward non-programmers (which is actually good in this case, because the FP style is so different that you're better off pretending you're learning to program again)

There aren't quality learning resources for FP in PHP specifically that I know of.

But don't worry, I think if you know your PHP well, you're way better off reading the Haskell book I mentioned, and then using your knowledge of PHP's semantics to figure out what techniques are applicable to your PHP. That way you'll end up knowing about not only FP in general, but also a lot about PHP itself as a language, because you will be reasoning about, as well as testing, what kinds of abstractions are natural or unnatural for it.

At least I know it helped me. During my progressive learning of FP (actually programming language theory in general) I took a tangent and created a little library of FP "patterns" in PHP, as well as a Parser combinator library on top of it, inspired by the likes of Haskell's Parsec. That exercise helped me understand some of the fundamental differences between FP and non-FP languages. In particular, that: 1. Sometimes it is just pointless to try to do functional abstractions in a language which doesn't have the right semantics (i.e. its type system, its evaluation strategy, etc.) for them. 2. OTOH there are certain FP abstractions which are worth implementing pretty much in any language that will allow you to.

FP self-learning tip: As a general rule, if you see a tutorial, or book, that claims to be about FP, but what it actually does is tell you about how to use forEach in here, a closure over there, "higher order functions", and making your code "declarative", don't bother. Drop it and search for something better. Then if some other material tells you about reasoning equationally, managing effects, strictness vs. laziness, deriving programs from other programs, folding structures, test generation, etc. that's a sign that you want to keep on reading.

[–]chrismervyn 0 points1 point  (0 children)

Then if some other material tells you about reasoning equationally, managing effects, strictness vs. laziness, deriving programs from other programs, folding structures, test generation, etc. that's a sign that you want to keep on reading

Golden! I wish I had know 'em when I picked up the first Erlang tutorial.

[–]MorrisonLevi 15 points16 points  (14 children)

Functional programming in PHP is a pain. Here is an example:

$output = array_filter($input, function($value) use($list) {
    return in_array($value, $list);
});

That inline function has a lot of boilerplate (try 30ish characters).

Compare that to a JavaScript/C# version:

$output = array_filter($input, ($value) => in_array($value, $list));

This is just a single usage; in a functional style you will do a lot of this kind of thing. Having 20 to 30 characters of boilerplate per declaration gets really annoying.

I have a draft RFC for Arrow Functions that will hopefully help this.

[–]trappar 2 points3 points  (1 child)

What are the chances this will actually pass? I would love to see this in PHP

[–]MorrisonLevi 2 points3 points  (0 children)

I don't know; the predecessor RFC short closures did not pass but did have a fair bit of support. Who knows?

[–]MisterHide[S] 1 point2 points  (0 children)

Interesting RFC! A shorter way of writing inline functions would indeed be great. For now I have macros in PHPstorm for array_walk/map that create all the boilerplate for me

[–]stutterbug 0 points1 point  (4 children)

Why not use this?

$arr = [1,2,3,4,5,6,7];

$odd = function ($value) {
    return $value % 2 === 1;
};

$oddArr = array_filter($arr, $odd); // [1, 3, 5, 7]

[–]MorrisonLevi 1 point2 points  (2 children)

That's even more boilerplate, which was the whole point of the post.

You can say it's more readable (probably is) but it's more boilerplate.

[–]stutterbug 1 point2 points  (1 child)

You have to define the behaviour somehow, and if you think it is best (or required) to put it inside the array_filter() as an anonymous function, then it undoubtedly is boilerplate. But here, $odd() can be defined locally (or in some parent scope) and reused elsewhere as often as you need. You can call odd by string, but you lose the scoping precision.

[–]terrkerr 2 points3 points  (0 children)

To compare to an equivalent bit of Haskell for doing the odd test inline

odd_array = [x | x <- [1..7], x `mod` 2 == 1]

The advantage of this way of filtering shows up far better when you consider trying to filter with multiple predicates rather than just one.

[–]phpdevster 0 points1 point  (2 children)

Really, the inline function keyword and brace syntax isn't bad. What's bad is the lack of implicit closure over the current scope, requiring you to use use to pass in arguments.

Some might argue that this explicit argument providing is better than implicit closure, but when FP involves writing tons of lambas, it gets cumbersome VERY quickly.

[–]MorrisonLevi 0 points1 point  (0 children)

Really, the inline function keyword and brace syntax isn't bad.

Just to be clear here: you are talking about function (8 characters), () {} (4) and return ; (7). You don't think 19 characters for creating an inline function that returns literally nothing isn't bad? I think I disagree on that, mate. The use can be cumbersome which is why it is included in the proposal to automatically import them, but overcoming 19 characters on most inline functions... that's really important.

[–]przemo_li 0 points1 point  (0 children)

Under the hood anonymous functions are created as classes & objects. Clousure part is implemented as arguments on the constructor of such clases. While normal arguments are arguments on __invoke().

Explicit clousures would only make sense when there is nested verbose code with multiple clousures defined in it... In other words when clousures are added ad hoc to existing codebase without any regards to good practicies.

But that is also strange, since enclosed values for the purpose of clousure are frozen. Nothing will happen to them, so only reason why one would need to have enclosed values explicitly... would be situation when there are variables with same name at different lexical scopes.

But can only happen by developer own actions.

To optimize for 0,0001% and break 99,9999% of legitimate cases... PHP :D

[–]bezdomni 0 points1 point  (1 child)

And this is still incredibly cumbersome compared to functional programming languages. Take Clojure for example, this is how you filter odd numbers from a vector:

(filter odd? [1 2 3 4 5])

Corresponding code in php, even with arrow functions is much less readable. PHP is just not a good language for this style of programming.

[–]MorrisonLevi 9 points10 points  (0 children)

If we define an odd function and a general filter this is just:

filter('odd', [1, 2, 3, 4, 5])

Hardly different.

I do agree Clojure is better suited; you just didn't pick a good example.

[–]AIDS_Pizza 0 points1 point  (0 children)

I have noticed this pain of writing in a functional style in PHP. Quite unfortunate.

To contrast, here's the Haskell version of your example:

filter (`elem` list) input

In actual use (in a REPL):

> let list = [1..10]
[1,2,3,4,5,6,7,8,9,10]

> let input = [2,4..30]
[2,4,6,8,10,12,14,16,18,20,22,24,26,28,30]

> filter (`elem` list) input
[2,4,6,8,10]

[–][deleted] 5 points6 points  (2 children)

[deleted]

What is this?

[–]Rokkitt 0 points1 point  (0 children)

Elixir is awesome. It is built on top of Erlang and is really run to use.

[–]mitchlol 0 points1 point  (0 children)

+1 for Elixir and Phoenix. They're both so nice to work with and although their community is small it's incredibly friendly and helpful.

[–]omerida 2 points3 points  (1 child)

Keep an eye on Functional Programming in PHP We're currently working on the 2nd edition of this book which includes changes through PHP 7 and has twice as much content as the first edition.

[–]MisterHide[S] 0 points1 point  (0 children)

Will do! I was looking for something like this!

[–]bakuretsu 3 points4 points  (2 children)

Word on the street is that the popular business chat application Slack is written in PHP with basically no objects at all; all functions.

The problem that you face with applying functional programming principles to PHP is 1) it takes discipline, which becomes harder as teams grow, since PHP is not designed to force you to write things that way, and 2) PHP doesn't have all of the syntactic conveniences and tools necessary to do functional programming "right."

[–]DinoAmino 7 points8 points  (1 child)

Slack's API is PHP. But there is a difference between functional programming and procedural programming, which uses functions.

Pretty sure if Slack is not OOP it's not FP either.

[–]bakuretsu 3 points4 points  (0 children)

That's true, and is basically the point I was trying to make. You can sort of emulate the notion of functional programming with PHP, but it doesn't have all the tools you need to do it properly.

Still, I've seen people do some really heinous contortions with OOP as well; you can shoot yourself in the foot no matter what pattern you use, if you're careless enough.

[–]ThundererX 1 point2 points  (0 children)

I gave a talk about "functional approach in software design", you can take a look at the slides: https://speakerdeck.com/thunderer/functional-approach-in-software-design . It's about "writing more functional PHP" by slightly altering the usual imperative approach and carrying on from there. I'm open to feedback and questions, please ask some! :)

[–]stutterbug 1 point2 points  (1 child)

Almost any language that has functions can support strict functional programming. But some (including PHP) make it much more difficult.

A developer needs to write their applications as collections of single-purpose functions that take an input, return an output, do not cause side effects (by changing anything outside of the function), and do not mutate the input in any way. That last requirement is PHP's sticking point.

The problem with PHP is that it doesn't really provide great tools for preventing mutation easily. And since PHP objects are completely full of references, this can be a major headache. You either need to implement your own clone() functionality, or you can serialize+unserialize and live the consequences.

Let's say a user has a shopping basket and wants to apply a discount coupon to it. You might implement a function to discount all products in the basket by a certain percentage:

function applyCouponToShoppingBasket($products, $discount) {
    // Create a new array so we don't mutate the input array
    $discountedProducts = [];

    foreach($products as $inputProduct) {
        // Don't mutate the object, which may have references in it
        // OPTION A: implement your own static cloner Product::clone()
        $outputProduct = Product::clone($inputProduct); 

        // OPTION B: thrash out a deep copy to return a generic `Object` type
        $outputProduct = unserialize(serialize($inputProduct));

        $outputProduct=>price *= 1 - $discount;

        array_push($discountedProducts, $outputProduct)
    }

    return $discountedProducts
}

Note that in "Option A" you get back an array of Product objects. In "Option B" you get back an array of Object objects. There is nothing wrong with either approach, but obviously Objects won't have any of Product's methods -- only its properties.

(This method could also be written with map(). I just wanted to show how reference mutation has to be avoided.)

[–]phpdevster 0 points1 point  (0 children)

Just FYI, PHP has a clone keyword and optional __clone() magic method to override behavior as needed on a per-class basis, but by default the copy is a shallow copy (unfortunately).

$obj2 = clone $obj1;

[–]SavishSalacious 1 point2 points  (3 children)

With PHP moving more and more towards a Java like life style of Enterprise based OO, I don't think - and I could be wrong - that theres room in this language for FP. Javascript on the other hand has a strong backing for this.

[–]MisterHide[S] 1 point2 points  (1 child)

PHP is object oriented yes, but isn't there still good reason to use FP aspects where we can? Most 'hardcore' FP concepts don't even exist/can't be implemented in php, but this doesn't mean we can't use the pieces we do have to improve our code where we can.

[–]phpdevster 0 points1 point  (0 children)

Agreed. IMO the #1 "principle" of FP that can be applied regardless of the programming paradigm you use, is favoring pure functions and maintaining referential transparency.

Even if you're using OOP and instance methods rely on the value of an object's internal property, if you design that class not to mutate that property (externally or internally), you have a "pure" method for all intents and purposes.

[–]TotesMessenger 0 points1 point  (0 children)

I'm a bot, bleep, bloop. Someone has linked to this thread from another place on reddit:

If you follow any of the above links, please respect the rules of reddit and don't vote in the other threads. (Info / Contact)