46

Coming from an OOP language, I am familiar with the SOLID principles of object oriented design. It seems like some of these would fit into a functional programming model, while other parts make no sense in a world lacking state. Is there a similar set of principles for refactoring functional code?

1
  • 5
    Actually, SOLID taken to the extreme essentially is functional programming with immutable state in an object oriented language. Commented Feb 18, 2014 at 21:22

4 Answers 4

60

As far as I know (I'm no expert), SOLID principles do not tell anything about state. They should be applicable as well in a functional programming languages. They're more advice about how to achieve modularity.

Some of them are rather obvious or at least well-known. Single-responsibility is the UNIX principle "do one thing and do it well", which is even more popular in functional languages where "composition" is widely used, similarly. The Interface Segregation Principle is very natural as well (have your interfaces modular and keep orthogonal concepts separated). Finally, Dependency Inversion is just a name for "abstraction" and is omnipresent in functional programming.

The "OL" principles, Open/Closed and LSP, are more oriented towards languages based upon inheritance as a core software engineering concept. Functional languages values/modules do not have open recursion by default, so "implementation inheritance" is only used in very specific cases. Composition is preferred. I'm not sure how you should interpret the Open/Closed principle in that setting. You may consider it is about encapsulation, which functional programs also use a lot, using abstract types and such.

Finally, the Liskov Substitution Principle may seem to be about inheritance. Functional languages do not always use subtyping, but when they do it is indeed assumed that "derived types" should preserve the specification of "base types". Functional programmers are of course careful to specify and respect the interface and properties of their programs, modules etc., and may use algebraic reasoning (this is equivalent to this so I can substitute...) based on those specifications when programming, refactoring, etc. However, once you get rid of the "inheritance by default" idea, you have much less problems of interface violations, so LSP is not emphasized as a vital safeguard as it is in OOP.

2
  • this comment is not only useful to see links between OO and FP, it also helps to understand more intuitively the SOLID principles. Thanks. Commented Dec 2, 2015 at 1:12
  • 1
    The OCP is not bound to inheritance, this is a often seen misconception probably because it is so often explained in terms of the strategy pattern or the template method pattern. But having black-box modules which can be reused as well and extended without changing their internals, just by passing (maybe functional) parameters to them has absolutely nothing to do with inheritance. And even the "strategy pattern" is simpler to implement using functors instead of strategy object hierarchies.
    – Doc Brown
    Commented Jun 18, 2016 at 8:24
7

This video presents the SOLID principles, and how to apply them in Clojure.

It shows how these principles hold in the Functional world as much as in OOP, because we still need to solve the same underlying problems. And overall, it made me think Functional Programming is better suited for SOLID design.

7

Actually, SOLID Could be a good idea to have a better principle for Functional Programming:

  • SRP: Each function or module should have only one responsibility or reason to change. This means that each function should perform a specific task or computation, and should not be responsible for multiple unrelated tasks. By following the SRP, code can become more modular, easier to understand, and less prone to bugs and errors.

  • OCP: Software entities such as functions, modules, or classes should be open for extension but closed for modification. This means that existing code should not be modified to add new functionality, but rather should be extended through the addition of new code. By following the OCP, code can be made more flexible and maintainable, and can avoid the introduction of bugs and errors that can occur when existing code is modified.

  • LSP: Objects or functions of a parent type should be able to be replaced by objects or functions of a child type without changing the correctness of the program. This means that any function or module that operates on a parent type should be able to operate on any child type without any unexpected behaviour or errors. By following the LSP, code can be made more modular and extensible, and can avoid the introduction of bugs and errors that can occur when code assumes too much about the behavior of its dependencies.

  • ISP: Software interfaces should be designed so that they are cohesive and focused on a specific set of related functions. This means that a module or function should not depend on interfaces that contain methods or properties that it does not use. By following the ISP, code can be made more modular and easier to understand, and can avoid the introduction of unnecessary dependencies that can increase complexity and reduce maintainability.

  • DIP: The high-level modules should not depend on low-level modules, but should depend on abstractions. This means that modules or functions should depend on interfaces or abstract classes, rather than concrete implementations. By following the DIP, code can be made more flexible and extensible, and can avoid the introduction of tight coupling between modules that can increase complexity and reduce maintainability. The DIP can be achieved in functional programming through the use of higher-order functions, dependency injection, or other techniques that allow for the decoupling of modules and functions.

HINT: SOLID principles can be applied to functional programming in the same way as object-oriented programming but with some adaptations. Each principle (SRP, OCP, LSP, ISP, and DIP) can help to write more modular, maintainable, and extensible functional code by encouraging the separation of concerns, avoiding tight coupling between modules, and promoting the use of abstractions and interfaces. By following SOLID principles, functional programmers can create code that is more reliable, easier to understand, and more adaptable to changes.

3
  • 1
    Robert Martin says Structured, OOP, and FP are all used to achieve SOLID principles. OOP for crossing boundaries, FP for providing discipline around when/where you perform mutations/IO, and Structured programming at the algorithmic foundation of our modules. Like you said, Structured programming is low level FP so to make it fully FP is to provide functional tenants to boundary crossings. Commented Jun 21, 2022 at 18:34
  • This answer seems to be a copy paste of the answer at softwareengineering.stackexchange.com/a/171534. If so, please provide credit to that answer. Commented May 3, 2023 at 14:15
  • Yeah @Antony, thanks, I should changed it by my idea. I'll do it for sure now.
    – AmerllicA
    Commented May 3, 2023 at 16:28
2

"Richard Warburton, a member of the London JCP Committee, in his presentation describes the SOLID principles as one example of well-established object oriented programming design principles, identified by Robert C. Martin in the early 2000s, and looks at each of the five principles trying to find a functional equivalent or at least something related on the function side. Richards experience is that although many developers don’t know how to use their existing design skills in functional design, functional programming can often help in implementing the SOLID principles and also that a functional mindset can actually help in achieving one important aspect of object-orientation, encapsulation."

Further information:

Not the answer you're looking for? Browse other questions tagged or ask your own question.