I come from an object oriented programming background, therefore, I am pretty curious about the core philosophy of functional programming? Why does it exist at the first place, and what types problems it is trying to solve?
What is the core philosophy of functional programming?
functional programming
IntroductionFunctional programming is a broad tent and so I don't think there is a single answer here. Rather, functional programming is a confluence of several ways of thinking about what a program is each with separate historical roots. I'll talk briefly about three, with an acknowledgment that the boundaries can get a little hazy. Sources of FunctionalnessComputation as Evaluating EquivalencesIn the earliest computational models (Turing machines, Von Neumann machines) and their actual implementation, a program was treated as a set of rules for manipulating a state. For example, a Turing machine is a set of rules for writing and moving along a tape. There is in these systems a well defined and linear ordering of states such that one can think of the execution of a Turing machine as a temporal process (this temporality is part of what makes physical implementation of a Von Neumann machine natural).Alonzo Church in studying the mathematical notion of computability, introduced the lambda calculus. This defines a simple language of variables, function (or lambda) abstraction and applications. For example it allows defining terms like (\x.x) which we can informally read as a function that given x yields x or the identity function. Its semantics are given by equivalence relations between terms. For example we can say that (\x.x) and (\y.y) represent equivalent terms (so-called alpha-equivalence) or that ((\x.x)(\x.x)) and (\x.x) represent equivalent terms (so-called beta-equivalence). But these equivalence relationships don't have a natural sense of order and so do not yield any time-bound notion of execution. To make this clear consider ((\y.(\x.x))((\t.(t t))(\t.(t t)))). Here we have two applications of functions, namely (\y.(\x.x)) and ((\t.(t t))(\t.(t t))) that we could take the beta-equivalent of. If we choose the former, going from outside-in, we simplify to (\x.x) but if we choose the latter and go inside-out we get right back to ((\y.(\x.x))((\t.(t t))(\t.(t t)))) [exercise: prove this]. The point is that the lambda calculus does not define a system of computation but you actually have to overlay some rules about traversing the graph of these equivalence relations to get a linear execution model.In this model, a program is just a collection of definitions and an interpretation of a program is just a way of expanding out those definitions and simplifying by substituting equivalents for equivalents. Actually implementing such programs was pioneered by LISP -- which is clearly inspired both in its syntax and its method of using functions as a primary method of abstraction.One fundamental flaw in the lambda calculus as a framework for studying computation is that it is unsound (https://en.wikipedia.org/wiki/Curry%27s_paradox). This is not so bad when we think of it in terms of actual programming, as all that means is that we can have programs that never halt. As a solution to the problem in mathematics, Church introduced the simply-typed lambda calculus. This brings us into our next sectionTypes and Programs as ProofsLogic itself is much older than computation and modern propositional logic developed shortly before and greatly influenced computer science (it was pioneered by such luminaries as Frege and Russel in the 1890's and early 1900's while Church and Turing were producing their seminal work in the 30's and 40's). One of the revolutionary and highly controversial ideas ideas that came out of Intuitionistic logic pioneered by Brouwer and Heyting is that logic was not in the business of studying truth and falsity (whatever those might be) but in the business of finding proofs and disproofs. In some such systems proofs become first class objects and implications become transformations of proofs. So we might have some proof p of, say, that 2 = 2 and we chain that with a transformation inc which says that n = n -> n + 1 = n + 1 to get inc(p), a proof that 3 = 3. When we start applying these ideas to our programming languages, we come out with two ideas:Objects can be proofs and types can be propositions.Type systems are therefore equivalent logical systems and so we can use logic to guide our types.The use of the first of these is most obvious in languages like Agda and Idris where programs so often really are proofs, but even in functional languages where this is less the case, basic propositions can inspire basic functions and their compositions -- e.g. with the basic fact of If P, Then P inspiring the identity function and If P implies Q and Q implies R, then P implies R giving function composition. And the second of these encourages the development of languages with different type features, such as sum types, union types, higher kinded types and yes dependent types, and most crucially make types more about meaning than storage location.Our first notion of programs as systems of equivalences provides us with functional abstractions and our second notion of programs as proofs gives us a sense of our programs having well-defined meanings, there is still a lot of meat left to go on the bones:Programs as AlgebrasAll of these sources are rooted in one way or another with very abstract mathematics, and there is one subfield of this abstract mathematics that we have not touched, namely abstract algebra. Abstract algebra takes the features of particular algebras (numbers, vectors, matrices, strings) and, well, abstracts them: forgetting the particularities of those domains while remembering the basic properties and relationships of operations over those objects. So we might notice that addition of numbers is associative (e.g. 2 + (3 + 4) = (2 + 3) + 4) and that concatenation of strings is associative (e.g. Hello + (  + World) = (Hello +  ) + World) and study the features of associative relationships.Now I think this field of mathematics has exerted a more passive influence over functional programming (though occasionally it has been more explicit -- one only needs to look at Haskell's type classes to see that), but I think this view of objects as fundamentally participants in algebras and as abstraction over algebras as a fundamental method of achieving genericity is an important part of functional programming. For example it is primarily in this sense that APL can be considered a functional language.The What of Why of Functional ProgrammingWhat counts as a functional program then to me is one that participates in one or more of the sources:It is written out of functional abstractions where evaluation becomes a matter of expanding out definitions (referential transparency is a big watchword here). Functions are basic building blocks and methods of abstraction.The semantics of the programs are there in the types. Types are used to guide correctness rather than execution.Objects are treated not as things stored in memory, but as values that participate in algebras. Polymorphic behavior is achieved by functional abstraction over algebras rather than object oriented delegation.Hopefully the short history I provided indicates that Functional programming is not some unified thing that someone thought up and advocated for but it is a mush of different but somewhat related ways of thinking about programming that emerged in complicated ways from mathematics and its intermingling with Computer Science. It is not, as the question states, something that evolved in order to solve particular problems or sets of them. Rather it evolved naturally over times.Nevertheless, it is enjoying popularity right now for a number of reasons:Functional and algebraic abstractions are wonderful ways of expressing reusable algorithms.Functional type systems provide effective ways of expressing semantics and enforcing some level of correctness.Features that are consequences of the above three such as referential transparency and immutability of objects benefit both readability and often performance of large systems (e.g. by making multithreading simpler and eliminating copying through sharing).In this analysis functional program is more about how one thinks about programs than the language and technology used (though of course some languages like Haskell support those ways of thinking about programs much more so than a language like C -- so the term functional programming language is not meaningless). It is one with benefits, especially in the world of large, concurrent systems, but it also does not have to be a strait-jacket.