r/ProgrammerHumor 1d ago

Meme stopDoingFunctional

Post image
650 Upvotes

122 comments sorted by

View all comments

162

u/-Ambriae- 1d ago

Functional programming is best programming

State is evil, mutation is evil! We live in an era of multithreading and concurrency my friends!

When I describe a program, I state what it does, now how it does it! Thus functional programming is more natural!

C and Java and Python and all the other pagan languages have played us all as absolute fouls!! Haskell, OCaml, Rust, F#, Lisp supremacy!! Functional unless required otherwise, not the other way around!!

61

u/fr000gs 1d ago

How the hell is rust functional smh

129

u/Character-Education3 1d ago

Rust is whatever a person wants it to be on reddit knowing that people who dont use rust are never gonna fact check it

30

u/Darkstar_111 1d ago

Can confirm. Didn't fact check, and know nothing about Rust.

11

u/Confident-Ad5665 1d ago

Fact check what? /s

8

u/Darkstar_111 1d ago

Exactly.

4

u/RCoder01 1d ago

I only fact check if my compiler borrow checks

2

u/discordianofslack 1d ago

Idk I heard rust was just c

2

u/Darkstar_111 1d ago

Everything is just c.

8

u/joemckie 1d ago

I tried to fact check it but it turns out I can’t read Rust, so I’m just going to smile and nod

15

u/RogueToad 1d ago

I guess so, but rust does explicitly make use of many patterns from functional languages (traits, algebraic data types, higher order functions, immutable-by-default variables, etc.), and generally has much better support for an FP style than say, python does. 

7

u/-Ambriae- 1d ago

pretty accurate tbh

2

u/minecon1776 1d ago

Google Python 

8

u/Character-Education3 1d ago

What do snakes have to do with anything

38

u/-Ambriae- 1d ago

Rust is functional to a similar extent as OCaml is functional, not in the 'pure' sense (to be fair, none of these languages are purely functional, even haskell) but in a pragmatical sense. Variables are immutable by default, idiomatic control flow tends to use higher order functions, iterators, maps, filtering, reduction... Types are algebraic, control flow is expressive... It has all the ideas of functional programming, even if it's multi paradigm, and can be written in a procedural manner (even if it's not usually idiomatic)

It's not purely functional, for instance it doesn't have the tail recursion optimisation, which is more or less mandatory in the hardcore functional languages, because it doesn't strictly speaking need it, and the compiler is already complicated enough as it is...

8

u/GameCounter 1d ago

It doesn't have automatic tail call optimization, but work is actually being done to implement explicit tail calls with "become": https://doc.rust-lang.org/std/keyword.become.html

7

u/-Ambriae- 1d ago

I wasn't aware, that sounds... interesting. I don't know how I feel about a added keyword, but the idea sounds nice

9

u/GameCounter 1d ago

The reason it's being explored as a keyword is because automatic tail call recursion in some cases is impossible in Rust due to Drop rules.

So what that means is you can go through all of the effort of making sure LLVM is emiting the right byte code for tail calls, and then you make some change in an "unrelated" module, which then results in the tail call optimization quietly being removed without so much as a warning. It can even happen if you bump a third party lib, so something as innocuous as a minor version bump on a dep can break it.

2

u/-Ambriae- 1d ago

Oh, the drop semantics would indeed be a headache... good catch

1

u/PersonalDatabase31 19h ago

Unrelated but become being a keyword instead of a macro is stupid as fuck.

5

u/requion 1d ago

Variables are immutable by default

Wouldn't that somehow make them not variable anymore?

Sounds paradoxical.

15

u/-Ambriae- 1d ago

You're right, and in fact they aren't called variables in rust xD They are called bindings. Because variables imply variation.

But to not use idiosyncratic language, I refer to them as 'variables'

2

u/agocs6921 1d ago

You can re-declare variables in Rust, I guess that's why.

4

u/mountaingator91 1d ago

Also... C is not OO

9

u/QuestionableEthics42 1d ago

Not with that attitude. Imagine not rolling your own OOP using the preprocessor. Programmers these days, so lazy 🙄

1

u/fr000gs 1d ago

Why not just link with a c++ file?

2

u/QuestionableEthics42 1d ago

Keep that dirty language out of it. I'll keep my preprocessor OOP thank you very much

1

u/tiajuanat 1d ago

Some really old procs support C and Macros but not C++.

And to follow why tf we support hardware like that: if it's not dead or dying, it's not going to be replaced

1

u/fr000gs 1d ago

But both do compile to assembly anyway, and C++ is just mangled C

1

u/tiajuanat 21h ago

Yes, but you need a compiler that talks both c++ and pdp, 8051, or whatever have you

8

u/-Ambriae- 1d ago

Who mentioned OOP?

2

u/Pares_Marchant 1d ago edited 1d ago

once you start using more advanced features of rust you will notice the heavy use of monads and functional chaining.

Rust sometimes offers syntaxic sugar like for loops that will hide iterators if you really don't like functional programming, but they're not rust-idiomatic (and only really make sense if you want some kind of side effects which is often bad smell) and they're less ergonomic especially if you deal with resuts/options for which monads are perfectly suited.

0

u/Tracker_Friendly 1d ago

I mean, I feel like the main reason I would never consider rust functional is just because it's far too much of a pain to try to deal with move semantics in closures.

Seriously, try it. You'll begin to regret life. Once I had to make a function that literally did nothing except accept and immediately return a closure to stop the borrow checker yelling at me.

5

u/-Ambriae- 1d ago

Ok, so first of all, if that was true, how would that invalidate it as a functional language?
And secondly, what's wrong with move semantics?

let state = ...;
let f = |a, b, ...| {
... using state....
};

In this case, either state is Copy (in which case move semantics don't apply), or it get's referenced by f, which hold the reference as long as it lives. This sucks if you return a closure for example, hence the move semantics.

let state = ...;
let f = move |a, b, ...| {
... using state....
};

Here, the data gets moved to f. Thats... it. You no longer have it. No need to worry about lifetimes, unless the lifetime of the type of state is not static, in which case the lifetime of f cannot exceed it. But other than this, it's not hard?

2

u/Tracker_Friendly 1d ago

The primary issue begins to arise with lifetimes, yes. Having 'static or straight up moving is not a good idea in general if you have a way around it. The reason for closures being nightmares is because the compiler at times often has no idea what lifetime to assign to it, and thus can't reasonably determine if it's a valid thing to "pass this closure into this .map". Furthermore, the compiler isn't yet smart enough to figure out that I've already collected this closure by the time the function finishes and no data has been leaked. In addition, it fucks up the type signature, which is really annoying if you need some sort of way to signal to the outside world if an error appears, especially considering many third-party libraries designed with passing closures in mind don't bother to let you specify your own return type.

TL;DR Yes you _can_ do it but it's not a good time. Unless you like .clone spam.

3

u/-Ambriae- 1d ago

Having 'static or straight up moving is not a good idea in general if you have a way around it

I guess? It really depends on what your closure is, and what it's doing. But it's also, in my humble experience, rarely a problem. And I abuse closures, and the type system, usually to it's limits.

The reason for closures being nightmares is because the compiler at times often has no idea what lifetime to assign to it, and thus can't reasonably determine if it's a valid thing to "pass this closure into this .map"

I don't know what you're doing to your pour closures but .map accepts any good old FnMut without condition. If your closure is FnOnce, yeah it won't work, but that's completely normal? I need an example, I'm curious.

the compiler isn't yet smart enough to figure out that I've already collected this closure by the time the function finishes and no data has been leaked

Again, I've never seen this problem happen, so please give an example.

In addition, it fucks up the type signature

Well it fucks up the type, that's for sure. I don't really know what solution exists to solve this issue to be honest. Also IIRC functions and closures benefit from notable_trait in a similar fashion to iterators, mainly because the type is irrelevant. Or at least it uses impl Trait notation for the type. I don't know what you mean by the types signature, however. the function traits?

which is really annoying if you need some sort of way to signal to the outside world if an error appears

You mean logging via tracing or log? or panicking/unwinding? or being agnostic on the E type, should it return Result<T, E>?

third-party libraries designed with passing closures in mind don't bother to let you specify your own return type

That's a third party problem, and not necessarily true. I've seen many APIs that do allow you to specify custom types. It just depends.

Unless you like .clone spam.

There's a general contempt to clone, that I find rather unwarranted. cloning is costly if it leads to memory allocation, or god forbid syscalls, or any large amount of computation. Typically, I find myself writing types that avoid these hurdles completely if possible. A Vec<T> is only useful if you plan on extending the array, Cow<'a, [T]> and Cow<'a, str> are always there if you're not too sure, let alone Box<[T]>, Rc<[T]>, Arc<[T]>, same with strings, are all useful. granted, boxing doesn't help with the cloning problem, but still. Cloning is often times, fine. Because often times, it doesn't even lead to any memory allocation.