Hi Grega
You might like to check out some of the information in our
Getting started with Functional programming thread - it's a little old now but most of the material is still valid.
I tend to think of OOP as an imperative style of programming where you combine data structures and functions to create an object, so a method is like a function that is attached to a specific object instance. Of course this is very simplistic and there is a lot more to OOP than this!
Functional programming treats functions as independent objects, so they can be passed around separately e.g. you can pass a function as an argument into another function, or return a function from a function, and so on. This shifts the emphasis from objects-as-data-with-behaviour (OOP) to functions-plus-data (FP) i.e. you have lots of functions that may be applied to different types of data. In many functional programming languages, we often use a relatively small set of common data abstractions e.g. various Collections, and have a lot of common functions that can be applied to these data structures e.g. map, reduce, filter etc. You can combine these functions with your own custom functions to create pipelines of processing that transform your data to meet your needs. This encourages a declarative style of programming i.e. you don't write lots of imperative for-loops, but you apply different functions as transformations to your data pipeline to produce the desired results.
"Pure" functions always produce the same result for the same inputs and have no side effects (such as I/O or writing to a DB etc), and the usual goal is to write most of your code as pure functions and limit the side effects as far as possible. Of course, a program that has no side effects (e.g. no I/O) will not do anything useful, but it's important to know where the side effects occur so you know how to manage them and control errors etc.
Another common feature of functional programming languages is immutable data. OOP tends to rely on changing the state of an object - all those "setters" and other methods that change the internal state. This makes it hard to reason about the internal state of an object if somebody else might be changing it at the same time, especially in large multi-threaded systems. FP languages typically have immutable data by default, so these problems do not arise: once you've created an "object" it always stays the same, so if you need to change it, you actually create a new one with different state, which means your new version will not affect anybody who is still using the old one. This makes it much easier to reason about your object, which is why using immutable data where possible is also recommended in Java programming.
This immutability is also one of the reasons why FP languages like
Scala are becoming popular for Big Data and concurrent systems.
As for programming languages, Python has some nice functional features e.g. functions can be treated as independent objects and Python supports comprehensions to transform collections of data, but Python also has mutable state by default. Python is not a fully functional programming language, but it is used with FP techniques e.g. the
Apache Spark processing engine is written in Scala but has a powerful Python API which has a lot of FP features.
Scala is a hybrid FP/OOP language i.e. it supports all the expected FP features (e.g. variables are immutable by default, functions are first-class objects, etc), but it is also object-based with inheritance and
polymorphism etc. This means you can choose to write Scala in a fairly Java-like OOP style, or you can go to the other extreme and write it in a fairly pure functional style - or you can combine both approaches e.g. Scala's
case classes are a nice feature that looks OO but is actually designed for functional programming with pattern-matching.
Clojure is based on Lisp, and is a pretty pure FP language with dynamic typing, while
Haskell has static types and is famous for being one of the "purest" functional programming languages.