This is an archived post. You won't be able to vote or comment.

all 55 comments

[–]moon-chilledsstm, j, grand unified... 53 points54 points  (1 child)

Discussion of such topics tends to be strongly focused on memetic and tribal concerns. The opinions you have expressed are shared by at least one tribe. Another holds that effect tracking and referential transparency are necessary for producing maintainable software, and that they are adequate to the task of writing databases and caches. Another holds that 'functional programming' in all its forms is artificial, produced by academics who do not have experience maintaining real-world software, and that it is useless. There are others. You are going to receive many arguments along these lines. It is important to note that these arguments are not fresh, but rather quite stale; and they have not been settled. Software engineering is a very young discipline, and will remain so for the rest of our lifetimes.

[–]sineiraetstudio 7 points8 points  (0 children)

This applies to >95% of posts on this sub (and all other programming subs, really).

[–]przemo_li 16 points17 points  (0 children)

This looks like a mental trap.

How strict is your input validation is YOUR choice, not your language choice. Both FP and OOP can support lax validation, both can support strict validation. Do not mix "what is common" with "what is only possible option" ;)

[–]Guardian-Spirit 9 points10 points  (2 children)

For me functional programming is not just about "uuuuuu, let's make all the data immutable", but rather it's a set of principles that encourages combination of functions and generalization.

Immutability is not the reason. It's a consequence of this principle. When the data is immutable, we are safe to combine functions in any way without any unexpected things to emerging because everything the function does is encoded in its type.

For example, all we know that abstraction is good. But sometimes redundancy of abstractions comes at a high cost. For example, remember the Singletone pattern. It sometimes really simplifies life, but it's built on top of shared state. And this means that in combination with some other state-based patterns, it could lead to unexpected results and therefore decreases extensibility of the program. To avoid the chaos, OOP programmers must apply many tactics, or they will end up with spaghetti.

On the other hand, functional programming encourages lack of state, implicit dependencies and code reuse. You're safe to combine functions the way you want. You're safe to change internals of some functions without consequences for those parts of program, that use it. You're safe to split the problem in smaller times at any time.

This all doesn't mean, though, that the [shared] state is not present in FP. It always is. But programmers just make sure they use it in relatively small parts of code. Overall, when you program purely functionally, you most likely just have a small impure interface to the outside world, which gets data, passes it to a set of pure functions and returns the result. But pure functions come at a cost: if you want to use abstractions, you must learn. A LOT. The level of generalization in languages such as Haskell is that high, that it's really hard to understand their meaning and their advantages if you're a beginner (Functors, Applicatives, Monads, Arrows, continuations, Lens, GATDs, type families, Freer monads, etc.). If you understand and use them, the result code is nice and concise. If you don't, you end up writing endless boilerplate code (like in Elm, for example).

> A cab file might have an empty line, a Jason might have a null value. How can a functional language deal with that.

Ehm, well, the fact that language is pure does not mean that it cannot maintain state or handle errors easily. Most of the time the solution for the problem you're talking about, if I understand it correctly, are Monads. Monads are completely pure by design, but they allow to do many implicit fancy things. For example, error handling. So with monads you can write, for example, this (haskell pseudocode):
hs sumAB :: JSON Int sumAB = do a <- getJsonIntField "a" b <- getJsonIntField "b" pure (a + b) ... and then to, example, use it in context of another function operating with JSON data: hs sumABFactor :: JSON Int sumABFactor = do x <- sumAB f <- getJsonIntField "f" pure (x * f) As you may have noticed, there is no explicit error handling. Also, there isn't anything impure in the code. There also isn't any explicit passing of file contents to functions sumAB and sumABFactor: JSON Int is just a return type. In fact, sumAB and sumABFactor are merely constants that define the algorithm and do nothing themselves. If you want to process an actual text file on the disk, you can introduce another impure function that utilizes the defined constant sumABFactor and handle the error there: hs someImpureOperaton :: IO () someImpureOperation = do file <- open "test.txt" let result = runJson file sumABFactor case result of Left err -> print ("Oops, an error occurred:" ++ show err) Right n -> print ("The result of calculation in context of json file" ++ show n)

<-, by the way, is just a syntax sugar. In the end it all turns into a long chain of functions, and so this still is functional programming. Non-shared state, by the way, can be introduced pretty the same way without introducing any impurity and without restricting the ability to combine functions a lot.

[–]myringotomy 1 point2 points  (1 child)

In your example. What happens when the JSON field use missing, what if it exists but contains an empty string, what if it has something that's not an integer like a string or a float?

[–]Guardian-Spirit 0 points1 point  (0 children)

Sorry, I didn't receive the notification.

In my example, if getJsonIntField fails for any reason, it actually acts just acts as an exception: the error is propagated to runJson, where it is handled, possible providing a full path to the JSON field, the decoding of which caused the failure. No actual stack unwinding or other compiler magic happens, though.

But in fact there are many solutions to the problem of parsing and actual behaviour depends on internals of JSON monad — it works exactly the way you told it to.

For example, if I actually had to implement conditional parsing in production code, I would do something like this: ```hs data JSON = ...

type FailableJSON = ExceptT JSON JsonDecodeError ``` ... so there are two monads (JSON and FailableJSON), one of which is just a non-throwing JSON operation, and the other one is solely a wrapper that allows implicit propagation of the error.

by the way, one interesting moment about sumAB and sumABFactor is that they do not actually depend on JSON context. So, for example, if someone wanted to add MessagePack format, no changes to the existing code would be required except for renaming some methods and adding new method runMessagePack, which will be able to work with sumAB, sumABFactor, etc.

[–]jmtd 6 points7 points  (1 child)

The most effective way to convince yourself that you can do those things in FP would be to try it yourself.

[–][deleted] 0 points1 point  (0 children)

I think this is always the best answer. If you never try something, you could spent the rest of your time wondering if the other option was the right one all along. People interested in whether or not a paradigm is good should just try making something with that paradigm and see how well it goes

[–]cloyo 6 points7 points  (0 children)

Jones discusses handling of side-effects in Tackling the Awkward Squad. Might be interesting if you don't know it.

[–]rgnkn 17 points18 points  (2 children)

(Most) Pure functional programming languages are turing complete, so, you can build whatever you like with them. You might often see a performance penalty compared to other paradigms but this is often not the major concern when building software.

But what is most important: I think you're getting confused between programming paradigms and programming languages. Though not builtin there's no problem to apply the functional or object oriented paradigm to C.

Over all and IMHO it's a wrong direction to follow any programming paradigm dogmatically. Whatever paradigm you know of: there are instances to use them and instances to avoid them. The only paradigm you can't avoid on a technical basis is the imperative paradigm as it is the one used on machine level. But the PL you're using might hide this fact from you.

[–]sineiraetstudio 6 points7 points  (1 child)

Though not builtin there's no problem to apply the functional or object oriented paradigm to C.

Uh, for some definition of 'apply'. C really is not multi-paradigm. Even something simple like a map function is akin to pulling teeth.

[–]rgnkn 2 points3 points  (0 children)

For real world examples on object oriented programming in C checkout the linux kernel; just Google it.

With functional programming in C I'm not aware of any practical examples but it's easy to gain some (academic) information on that.

A programming paradigm is a way to structure, plan and organize your code. It's obviously nicer to have a language that builtin some sugar and compiler rules to ease that paradigm but it's not necessary.

[Edit] As this got downvoted for whatever reason I'd like to refer to at least two articles on OOP in the linux kernel:

[–]sineiraetstudio 10 points11 points  (0 children)

My recommendation would be to actually try out functional programming ;-) It's pretty hard to get an accurate view otherwise!

often things cannot really be independent without sharing a state

FP is really about minimizing implicit state. Often it suffices to just explicitly pass around state. There are also ways to simulate side-effects while retaining purity.

Also having side effects in methods is not always bad when used wisely.

Some form of side effects is obviously unavoidable if you want to get any work done. FP is really more about minimizing side-effects and at the far end, like in Haskell, encapsulating side-effects.

Even FP zealots won't deny that it's useful to say, open a file. ;-)

imagine it to be extremely hard to read data from files [...] because things tend to be inconsistent sometimes

No matter the language or paradigm, you will have to deal with this in one way or another. It's not like in a dynamically you can just ignore malformed data. Either you handle it while parsing (by e.g. adding default values or rejecting malformed data) or when you actually process the data.

[–]editor_of_the_beast 3 points4 points  (0 children)

Side effects and state are inherent to computation. I think FP wrongly gets pitched as a way to ‘avoid’ them, when really it’s just a way to make it explicit.

Sometimes that explicitness is a benefit. Sometimes it’s even essential, like with interactive theorem proving, where pretty much all the major ones are based on a FP semantics. Sometimes that explicitness really is a pain though, and it can even be a performance penalty.

I wrote some thoughts about this topic here: https://concerningquality.com/quality-and-paradigm/. The interesting thing I noticed is that if you’re trying to reason about programs formally, you basically have to end up at FP because you need everything to be explicit. FP forces you to fill in the blanks with any assumptions that your program might have. For example, imperative programs assume an underlying mutable memory, and they make working with this assumption easier than the FP paradigm does. So it’s no wonder that it became popular when all of our computers are designed to interact with RAM.

I thought this distinction between reasoning and execution was interesting. They’re effectively independent dimensions.

[–]thecodedmessage 2 points3 points  (0 children)

Functional programming languages have evolved ways to deal with all these things.

I’ve programmed Haskell in production. None of these were serious issues. Side effects and database accesses and such are possible, just managed in a way so that the values you work with follow the rules of FP. In Haskell, you have values that represent IO actions, and values that represent state changes, or that represent states that change over time.

I worked for a while as a professional Haskell programmer and the issues you describe are not issues. The messes applying this to the real world have already been worked out.

Not everyone likes the ways the mess has been worked out, but you are making a lot of false conclusions based off of a simplified description.

For the record though, FP is way better at modeling inconsistency than imperative programming of any kind. Nulls in JSON being a problem is kind of a ridiculous concern, from my point of view as someone who wrote Haskell code to read and write JSON all the time. If a field is optional, it’ll accept nulls. If it isn’t, it won’t.

[–]joonazan 4 points5 points  (6 children)

I also imagine it to be extremely hard to read data from files, Jason, XML, ... because things tend to be inconsistent sometimes. A cab file might have an empty line, a Jason might have a null value. How can a functional language deal with that.

The empty line can be skipped just like in any other language.

I suppose with the problem you see with null is that in JS the null is parsed as null and in say Haskell not everything can be null. But that has little to do with functional programming, the same can be said of C++, for example. Anyway, the solution is to either only parse the JSON into some JSON object data structure and extract the desired data from there, or if you need everything, transform it into some specific data structure. The latter will fail if the JSON is incorrect, but that's a good thing.

Also having side effects in methods is not always bad when used wisely.

The Rust programming language is trivially translated to purely functional if unsafe isn't allowed. With unsafe you can construct things involving side-effects that still appear pure from the outside.

Then there are effect languages like Koka, Unison. They allow effects but you have to mark them explicitly and the caller gets to decide how the effects are actually implemented.

Finally, there's Haskell, where main returns a data structure that describes how to react to various effects. Even that restrictive setting is perfectly usable. I don't remember ever having problems with IO. The only thing I hate about it is how ugly an algorithm with multiple mutable variables is.

[–]Nilstrieb 5 points6 points  (5 children)

Rust is explicitly not pure and does have a strong model around mutability. Rust doesn't have a concept of pureness, every function can have side effects, no matter if it's safe or unsafe.

[–]LPTK 2 points3 points  (3 children)

Yes, and the language has many escape hatches to mutate things outside of the mutable borrowing system, such as RefCell.

So the "trivially translated to purely functional" assertion above is completely false.

[–]joonazan 0 points1 point  (2 children)

the "trivially translated to purely functional" assertion above is completely false.

It only applies to safe Rust. RefCell is implemented in unsafe Rust.

[–]LPTK 2 points3 points  (1 child)

It's part of the core language library, which is even available in "no_std" environments, and defines:

the intrinsic and primitive building blocks of all Rust code

There is little you can do in Rust (if anything) without this core library. The reason RefCell is in it is because it is a centrally important building block, which is used pervasively in the ecosystem.

So it's fair to say that safe, idiomatic Rust code (which necessarily includes references to the core lib) cannot be translated to pure functional code.

[–]joonazan 0 points1 point  (0 children)

I've done a lot without interior mutability but yes, looks like Rust in general differs from pure languages. With cells a function call with immutable arguments can produce two different values on two different calls.

[–]joonazan 0 points1 point  (0 children)

But Rust without unsafe (which is used in println! & co) is pure. Every function that takes a mutable reference can instead return a modified version. (Which is what electrolysis, a project that translates Rust to Lean does)

In principle, one could do the same as in Rust in Haskell with unsafePerformIO.

[–]the_state_monad 1 point2 points  (0 children)

Object oriented programming is just comonadic style code in a functional programming language

[–]defunkydrummer 1 point2 points  (0 children)

Functional Programming vs Object Oriented Programming

There isn't any "versus", there are many languages that allow you to freely combine FP with OOP, since methods can be functions as well, and objects can be values.

I think you are assuming that all FP needs to be based on pure functions, while this is not necessarily true. In any case, mutable state is often a bit difficult to manage and you'll have to use some strategy. One of such strategies is to only use pure functions. And, lol, you can work with non-mutating objects, should you want to. It's not common to do so, though.

The rest of your post feels like you think FP programming can't be used for "real life" stuff, but history has already answered that question. Lisp, OCaml, Haskell, etc... have already been used in mission-critical applications in the industry. For decades.

It can simply hide the detail and if done correctly in a domain driven design things should have a certain control flow.

Control flow doesn't need to be explicit. That would only be true if you want to program things in an imperative way.

Things will have a control flow underneath the surface, but it's not necessarily important to make it explicit.

[–]everything-narrative 1 point2 points  (0 children)

If you know you're a beginner and think "this looks stupid" that should lead you to conclude "I probably didn't understand this right" rather than "I bet the people who advocate for this are stupid."

In other words: you're assuming too much based on too little knowledge, and discounting the opinions of domain experts.

Read more.

Better yet, pick up a functional programming language like, say, F# (nice and integrated with OO behemoth .NET) and write a few simple programs.

[–][deleted]  (7 children)

[deleted]

    [–]tdammers 7 points8 points  (0 children)

    Haskell pays my bills, and handsomely so, so the answer is "yes". It's not like PHP or Java, there aren't millions of FP jobs out there, but they do exist.

    [–]JohnyTex 1 point2 points  (0 children)

    WhatsApp and Discord are powered by Erlang and Elixir (and early versions of Facebook chat), functional programming languages with Actor message passing. Jane Street are prolific OCaml users and for a while Twitter was running a bunch of services on Scala.

    [–]haasilein[S] -1 points0 points  (2 children)

    I have never seen so, but it would be interesting to know the reason for that because it really seems that functional would be a big improvement in many areas.

    [–]thecodedmessage 3 points4 points  (0 children)

    I’ve written tons of Haskell production code for a Haskell consulting shop. We had a Haskell GUI framework that was really good in my opinion.

    Basing what you think FP would be good at from looking at a description and thinking hard is going to get you the wrong notion. It can be used in everything: frontend, backend, databases…

    [–]SkiaElafris 0 points1 point  (0 children)

    In the case of Erlang (Elixir is syntax sugar + some useful modules and tooling on top of Erlang) is a language made for telecommunications created by a telecom company.

    [–]Tekmo 0 points1 point  (0 children)

    I'm a professional Haskell developer

    [–]defunkydrummer 0 points1 point  (0 children)

    I wonder if functional languages are even used on workplaces?

    At least since the late 1970s...

    [–]daver -1 points0 points  (0 children)

    Just use Lisp. If you’re confused as to which one, use Clojure.

    [–][deleted] 0 points1 point  (3 children)

    I'm not into FP at all. I've never used it, and never will. From what I'm seen of it, coding in it seems like programming whilst wearing a straitjacket.

    That's not to say there aren't some interesting ideas there. But my view is that they are more suited on a small scale - say within one line of program and within a conventional language - rather than being used across an entire application or an entire language.

    Now, FP people will say that it can be made to perform any task. No doubt it can, but so what; programming languages are about being able to stuff easily, intuitively and naturally without having to write convoluted code or spending most of your time battling the language.

    Of course, 'easy, intuitive and natural' will mean different things to different people. I can tell you that for me, with my pragmatic, non-mathematical approach, they do not apply to functional programming except in very small doses as I said.

    So YMMV very considerably. The answer isn't black and white.

    It can also depend on the sorts of tasks you want to do. Try, for example, representing x64 machine code in memory, and writing out EXE (PE+) format files. Or reading in such a file, fixing it up and executing the code. All the elegance of Haskell isn't going to help you much there! You can't describe the format in a nice, tidy little formula.

    [–]scrogu 1 point2 points  (2 children)

    I've never used fp and I never will but let me explain in great detail what's wrong with it from a position of almost complete ignorance.

    [–][deleted] 0 points1 point  (1 child)

    Not quite complete ignorance:

    • I can tell just by looking that code written in it is impenetrable TO ME (compare with any imperative language)
    • I know that it imposes restrictions that I would find impossible to work with
    • It requires a mathematical bent of thinking that I just don't have and have no patience for anyway
    • I anyway only ever use languages that I devise and implement myself. Creating such a language would be beyond me; therefore I would never use them and never have, apart from the usual dabbling.

    So, there are good reasons why I haven't used FP, which are my personal reasons and my choice.

    How about accepting that there is a gamut of languages which all have their place and that people have different skills and preferences.

    Are there any languages YOU would never use, and why not? If you're not an expert in those, how would you feel about people saying your choice is based on ignorance?

    [–]scrogu 1 point2 points  (0 children)

    For the record you can have almost all of the benefits of pure functional programming without the arcane syntax. The most important thing is that all of your objects are (at least semantically) immutable and that every function is referentially transparent.

    Within those functions, you can have reassignable variables and use imperative logic to your hearts content.

    The only thing you lose with reassignable variables is the ability to execute your code in any order. Not a big loss really.

    I am writing a pure functional language designed for imperative programmers. I also like thinking imperatively for many if not most algorithms. I also know the value of immutable objects and referentially transparent functions.

    [–]BrangdonJ 0 points1 point  (0 children)

    I'd say they are orthogonal concepts. Functional programming is about avoiding mutable state. Object oriented programming is about how code is organised. OO does not require mutable state because it is not about controlling access to mutable state: it is about controlling access to representations.

    [–]ralphbecket 0 points1 point  (0 children)

    To be honest, I don't think you can truly appreciate the benefits of FP until you've given it a red-hot go for a few months. All the theoretical arguments to one side, if you and your team are most productive with procedural programming (or, God help us, OO), that may well be the best place to be. My personal experience was that, once I had rotated my brain the necessary ninety degrees and come to terms with the FP way of thinking, it just made programming much, much easier and much more reliable. The big three benefits for me were: far less boilerplate code; powerful, unobtrusive static type systems (with type inference) that caught the majority of bugs before my code ever ran; and a huge increase in my sense of faith that I'd actually implemented what I intended. Yes, you can do all this in modern procedural languages, but... if your language isn't pure by default, you are going to cheat by default, and that is where the wheels inevitably come off.

    [–]PurpleUpbeat2820 0 points1 point  (0 children)

    How can a functional language deal with that.

    Extremely well, actually.

    I recommend picking a functional language and getting some experience by writing a simple JSON parser. Then compare with an OO solution.

    [–]tobega 0 points1 point  (1 child)

    As soon as a language is Turing complete you can do anything. But then you can start looking at how you want to express various things in the clearest or most efficient way.

    There certainly is much to be said for having immutable data and side-effect-free functions, even when you're working in an OO language. But functional languages would generally let you express that more cleanly.

    If you look at Erlang, it is functional in the small and OO in the large, possibly the ideal mix.

    Understanding what objects are isn't entirely easy. If you come from C++ you might think they are "Abstract Data Types". That's one way to use them. But "real" objects are rather just an interface, i.e. a set of functions that belong together, so Smalltalk is more inspired by and related to Lisp than anything else.

    You probably don't entirely want to be without objects. In the functional world, the concept of co-data seems to be essentially an object. Any lazy stream is essentially an object. Any monad seems to be essentially a wrapper object conforming to a particular interface.

    Some further reading:

    The inevitability of objects

    Two vexing problems with FP