Rust has perfected the elegance of functional programming and the efficiency of C++. It is genuinely refreshing to be able to write high level code without the worry of massive performance impact.
Rust has perfected the elegance of functional programming and the efficiency of C++. It is genuinely refreshing to be able to write high level code without the worry of massive performance impact.
Rust is low level and not functional
Many functional concepts since its beginning.
https://serokell.io/blog/rust-vs-haskell
Where is currying? Where is function composition? Where is foldr? Where is lambda functions that borrow from the environment?
Also not exclusively functional but where is function nesting? In Rust nested functions cannot capture variables from their parent function
Rust is very far from functional. It's an imperative language with map, filter and fold implemented, match and a type system that shouldn't be available in a low level language because you get this
With monads you could bind each functuon that returns an option and only get Option<T> in the end
NTA but this is no true scottsman fallacy.
How?
Put simply in Rust you don't express your program as applications and relations between functions but rather as procedures of of discrete steps
>Can be done manually
Anything can be done manually so no
>You can do this.
No you can't as far as I know
add_5 = (+ 5)
double = (2 *)
double_and_add_5 = add_5 . double
Can you do this in Rust (without the currying of +, *)
>That's just this, right?
Yes I wasn't aware of it's existance
>Rust has these and uses them all over the place for the simplest of things.
There are some things you can't do with them. Actually due to the borrow checker it's impressive how much you can do with them (because they can move the data to themselves). However I think you cannot return a closure that has borrowed a value or you can't pass it somewhere else or something
>You can do this by defining them as lambdas
Lambdas can't be recursive though
>and_then, the early-return ? operator
That's true only for Option and Result but user defined types can't work with that. However I don't really consider abstract data types a functional feature and the type system is too messy in general. Like it has function pointers, each closure has it's own type (to avoid functional features like writting functions that take function arguments. First class functions is a lie)
let add_5 = |x| x + 5;
let double = |x| 2 * x;
let double_and_add_5 = |x| add_5(double(x))
>b-b-but that is not the same
literally nobody cares
Rust then cannot claim to have first class functions
Yeah. The borrow checker as a system allowes you to do a lot more with closures without garbage collection (idk how C++ deals with closures). However they do not have their full power
Function composition in C?
#define CALL(f, x) f->fn(f, x)
There is no point in continuing this discussion anymore since no one else seems to have experience in functional programming. The following is impossible in C and Rust because they are not functional languages (unless you implement the runtime of a functional language):
Haskell
fix f = f (fix f)
Ocaml
let rec fix f x = f (fix f) x
Example usage
factorial = fix (f n -> 1 if n == 0 then 1 else n * f (n - 1))
In that case Rust may have "first class functions" but theu are not first class enough to be able to implememt the above
>I guess you are right but I'm going to move the goalpost
recursion is a code smell btw
recveursi fib is one of the classical-est of classical programing problems doe
every CS course should have one
(nta)
fn recursive_fibonacci(n: usize) -> usize {
print!("{}", n);
match n {
0 => panic!("zero is not a right argument to fibonacci_reccursive()!"),
1 | 2 => 1,
_ => recursive_fibonacci(n - 1) + recursive_fibonacci(n - 2)
}
}
0 fib = 0 moron
>the 0st number is 0
dumb moron
If you define index 1 and 2 as numeral 1, then index 0 is numeral 0
Fib starts 0, 1
>index 0
It's not index, it's n
>Fib starts 0, 1
According to Fibonacci himself it starts with 1, 2
dumb moron
Smartest rust user, everyone.
smartest Wikipedia quoter everyone.
https://en.wikipedia.org/wiki/Fibonacci_sequence
>The sequence commonly starts from 0 and 1, although some authors start the sequence from 1 and 1 or sometimes (as did Fibonacci) from 1 and 2.
>I'm right if you ignore modern developments
This is also true of rust in general, so I can see why you're a fan.
It even says it right in your screenshot that in some definitions F_0 is omitted.
dumb moron
>I'm right if you just ignore what most people do
By consensus, you are wrong.
Also nice goalpost shift from
>nice goalpost shift
There is no 0st number. You start numbering from 1.
By consensus, you are a dumb moron.
F0 = 0
F1 = 1
The 0th number is 0
Or, if counting the other way, the first number is zero and F1 is the second number.
Calling F1 the second number is only something a moronic rust programmer would do.
dumb moron, F_1 is the first number. There even is a 1, why would it be the second?
So you admit I was right, then?
Very interesting.
dumb moron, the first number (F_1) is 1, the second number (F_2) is 1, the third number (F_3) is F_1 + F_2.
and F0 is rightly defined as 0
F0 of course, coming before F1
Because 0 < 1
and since 0 is well defined, and your program fails on 0, you have failed to write Fib in rust, thus proving you're a moron.
dumb moron, F_0 doesn't exist in the definition I use.
see
Then you failed to properly define fib. SART (simple as, moron)
see the picture you yourself posted
dumb moron
You explicitly defined 0 as not there, where the pic I posted myself explicitly states some start with F1 and F2 rather than F0 and F1
It's not that F0 isn't defined, it's that some people skip 0 because it's the most trival case.
dumb moron
F0 is ommitted, so the sequence starts with F1
Which is exactly what I said
Perhaps reading comprehension is not your strong suit, you should go .unwrap(|x| { println!().unwrap()})
>so the sequence starts with F1
exactly as I did here
dumb moron
>defines f0
>defines it wrong
It doesn't start with F1, you put in F0 and put it in incorrectly.
>lose argument
>write nonsense
dumb moron
>F0 = 0
>"There's no such thing!"
>F0 = 0 (proof)
>"I wrote something else that's still valid!"
You may as well have written F0 = 123 and called it 123Fib
>In some places, F0 is omitted
>So even though I explicitly defined F0 incorrectly, I'm still right
Sometimes people exclude 2 from the list of primes P, aka P where P > 2
But if you wrote a program that supposedly told you if a number was prime, and then wrote is_prime(2) and it panic!()'d, you wrote a flawed program.
You can use the fix point combinator defined above to avoid recursion as demonstrated in the non recursive definition of factorial. If it was possible in Rust the syntax would be
let factorial = fix(|f, n| if n == 0 { 1 } else { n * f(n - 1) }
I don't know scala but what you wrote is an infimite loop:
f(x) {
return f(x)
}
Function composition would be
compose(f, g) {
return |x| f(g(x))
}
or in a functional language
compose f g x = f (g x)
And then using caurrying you can create a function simply calling
compose f g
Furthermore what I wrote in my post which you cannot implement in C or Rust is the fixed point combinator
Actually I am well versed in functional programming, or does Scala and F# does not count as functional programming language in your book?
Function composition is nothing more than f(x) = (f x).
In Rust this is just (minus the dumb bullshit)
struct Fact<'s> { f: &'s dyn Fn(&Fact, u32) -> u32 }
let fact = Fact { f: &|fact, x| if x == 0 { 1 } else { x * (fact.f)(fact, x - 1) } };
How terrible
That is cool. I'd actually like to learn Rust better so can you walk through everything that's happening there? I still think you did not define a stand alone fix combinator that can be used with any function
My argument is more along the lines that it gets too tedious to write types with all the low level constructs like references, lifetimes, heap allocations, multiple types for strings, integers etc
>I still think you did not define a stand alone fix combinator
yeah, I removed the dumb bullshit
Yeah you can't do it
fn fix<T, R, F>(func: F) -> Box<Fn(T) -> R>
where T: 'static,
R: 'static,
F: Fn(T, &Fn(T) -> R) -> R + 'static
{
use std::cell::RefCell;
use std::rc::Rc;
let fixed = Rc::new(RefCell::new(None));
let fixed_fn = {
let fixed = fixed.clone();
move |val: T| -> R {
let fixed_ref = fixed.borrow();
let fixed_ref: &Box<_> = fixed_ref.as_ref().unwrap();
func(val, &**fixed_ref)
}
};
*fixed.borrow_mut() = Some(Box::new(fixed_fn));
Box::new(move |val: T| -> R {
let fixed_ref = fixed.borrow();
let fixed_ref: &Box<_> = fixed_ref.as_ref().unwrap();
fixed_ref(val)
})
}
You are implementing the runtime of a functional language. Nothing wrong with that, it just shows that Rust is not really a functional language but rather an imperative one. Especially when the code for the same thing in functional languages is just a one liner with few characters
Yeah I was thinking of a built in compose function
>you have to follow the lifetime rules
Yes in functional languages you don't need to follow any rules and you don't have any issues with safety. The reason you need to follow these rules in Rust (besides the whole memory saftey thing) is that you need a garbage collector if you want to not follow them. It is rare but yeah the point is that this is more like the illusion of functional programming
>Coming Some Time In The Future™
That's cool I guess
>You can write higher-order functions either by monomorphizing them or by boxing them
In functional languages you can just write higher order functions. Also you can write higher order functions using rank n polymorphic types
Sure. It has features from functional programming. It still remains an imperative language
It's not just the lack of automatic memory management but also the existence of state.
But yeah, I don't consider Rust a functional language and said as much in an earlier post. Regardless of capabilities it's just not the idiomatic way to write code. I'm only pointing out which features it does have.
>My argument is more along the lines that it gets too tedious to write types with all the low level constructs like references, lifetimes, heap allocations, multiple types for strings, integers etc
The only thing here that actually gets bothersome is lifetimes.
OP and some other posters are either trolling/exaggerating or delusional if they are arguing without reservation that Rust is designed as a functional language, because it's a systems language first
However, it is fair to say that in much the same way Java was supposed to drag C++ "halfway to Lisp," Rust is supposed to bring it halfway to Haskell
(Maybe more)
(Imagine)
>Rust then cannot claim to have first class functions
wrong
https://en.wikipedia.org/wiki/First-class_function
>In computer science, a programming language is said to have first-class functions if it treats functions as first-class citizens. This means the language supports passing functions as arguments to other functions, returning them as the values from other functions, and assigning them to variables or storing them in data structures.
>Function composition in C?
You just write an interpreter. Python, Ruby, JavaScript, Lua, etc.
>idk how C++ deals with closures
C++ closures have the same restrictions as Rust closures (modulo borrow granularity) except that the rules are not enforced
The programmer is just supposed to notice when the rules are broken
c++ lambdas are so fricked. even a "back to basics" talk is full of potential UB before you get like 25 minutes in lol
>However I think you cannot return a closure that has borrowed a value or you can't pass it somewhere else or something
References cannot outlive their referents, so a structure/closure that borrows data cannot live longer than the lifetimes of its borrows
If a function's return value borrows something that can outlive its arguments, the type signature of the function has to specify this
>Rust you don't express your program as applications and relations between functions
You can do that in Rust just fine. Hell you can do that even in C.
>Can you do this in Rust (without the currying of +, *)
There's no syntax for it but you can write a higher-order function for this yeah
fn compose<T, U, V>(f: impl Fn(T) -> U, g: impl Fn(U) -> V) -> impl Fn(T) -> V {
move |x| g(f(x))
}
fn main() {
let add_5 = |x| x + 5;
let double = |x| x * 2;
assert_eq!(
compose(add_5, double)(10),
30,
);
}
>I think you cannot return a closure that has borrowed a value or you can't pass it somewhere else or something
You can, but you have to follow the lifetime rules and you need to know how to spell the function signatures for this. You might have to write + 'a or for<'b> here and there, and know the difference between Fn/FnMut/FnOnce, but it's there.
>Lambdas can't be recursive though
Sure, I guess
>That's true only for Option and Result but user defined types can't work with that.
They actually can in nightly. Coming Some Time In The Future™
https://doc.rust-lang.org/std/ops/trait.Try.html
>Like it has function pointers, each closure has it's own type (to avoid functional features like writting functions that take function arguments. First class functions is a lie)
You can write higher-order functions either by monomorphizing them or by boxing them (or by doing things C-style and banning closures I guess). You do have to decide how to make the tradeoff.
I actually wouldn't say that Rust is functional but you're underestimating it
>Where is currying?
Can be done manually with closures but it's tedious, in part because of the lack of variadic function generics.
>Where is function composition?
You can do this.
>Where is foldr?
That's just this, right?
https://doc.rust-lang.org/std/iter/trait.DoubleEndedIterator.html#method.rfold
I don't remember how lazy evaluation plays into it in Haskell though, it's been a while.
>Where is lambda functions that borrow from the environment?
Rust has these and uses them all over the place for the simplest of things.
>Also not exclusively functional but where is function nesting? In Rust nested functions cannot capture variables from their parent function
You can do this by defining them as lambdas. Makes the lifetimes much clearer so maybe it's for the best that fn functions don't do this.
>a type system that shouldn't be available in a low level language because you get this
such an elegance, beautiful. this is underappreciated masterpiece. truly, a hidden gem. a silver bullet to all computational problems.
>With monads you could bind each functuon that returns an option and only get Option<T> in the end
You can flatten Options very nicely in Rust in a couple of ways, like with the map/and_then/etc. methods and with the early-return ? operator. You're not naturally going to end up with deeply nested options.
But if you do legitimately want to nest options for some reason then an Option<Option<Option<bool>>> still only takes a single byte. That's the only point of that example.
>Option<Option<Option<bool>>>
oh no no no
>a type system that shouldn't be available in a low level language because you get this
such an elegance, beautiful. this is underappreciated masterpiece. truly, a hidden gem. a silver bullet to all computational problems.
It's a stupid example. Rust can be a bit ugly but its type system is quite good by imperative systems language standards.
Rust is functional and even has the most useless yet universal FP-aproved function in the history of programming (for_each) in its standard iterator library.
Rust code is almost never written in a functional style so it's not a functional language
>the most useless yet universal FP-aproved function in the history of programming (for_each)
something something internal vs external iteration, I've seen legitimate performance differences
>FP-approved
It doesn't even return a value, is it really? It's not map, it's only useful because of side effects
Unless you are being ironic, for_each only exists in Rust and C++
><Option<Option<Option<Option<Option<bool>>>>>>
such an elegance, beautiful. this is underappreciated masterpiece. truly, a hidden gem. a silver bullet to all computational problems.
>yet another brainlet hard-filtered by generics
Many such cases
>he thinks that calling out moronic syntax for <G>enerics makes one not being able to comprehend parametric polymorphism
keep::going::rust.troon();
What's your alternative?
ok, but where are the rust jobs?
Professional programming is deprecated.
why does the header appear twice? the language must be really bad if it can't even make a table.
don't forget to install cargo mommy
I was thinking of learn Rust instead of C++, I checked the community and... C++ seemed way more attractive since then.
In a lot of instances I analyze things without the regard of human preferences, which make me come to conclusions that I don't even like, but accept it, since it's the most coherent. I'm afraid of saying something without any bad intent and get impaled through a torn iron rod while burning alive in case I say something "incovenient", I don't want to be near people like that, it disturbs me.
The language seems pretty good, but that "outside part" scares me so much.
Like rudeness or (things that might be construed as) politics or something else?
Their Code of Conduct states:
>This code of conduct and its related procedures also applies to unacceptable behavior occurring outside the scope of community activities when such behavior has the potential to adversely affect the safety and well-being of community members
So basically, you don't need to directly do something wrong, if you're a good emphatic guy and say something that might be misinterpreted as "harmful" in your personal blog post, you can be doomed.
I haven't seen this happen, FWIW.
The prototypical case that rule is supposed to address is that somebody goes around stalking or harassing another contributor. You want to be able to show somebody like that the door even if it technically happens on MySpace and not GitHub.
If you have a few weird opinions in a blog post somewhere but you're polite when interacting with people then I think that you'll be okay.
In general people aren't really interested in playing inquisition. They just don't care that much. If you're very high-profile on either side of things there may be spillover but "the guy who filed this issue said something weird on an obscure blog" is too petty to bring up.
I predict eventually people will make jokes about needing to use nested generics to do simple shit all the time. It’s like the functional programming equivalent of a an overly elaborate inheritance tree.
There is no such thing as high level code under the tyranny of the borrow checker.
Immutable data structures are high level code and you don't need to mutate anything
You still need to care about lifetimes tho
Not for writing code. Infinite immutable references
I think cybersecurity traitors deserve the rope for enslaving people.
>option<i32> has 2x overhead
jesus christ.
This is why oopsie poopsie and "generic" programming sucks ass - throwing a bunch of "objects" everywhere where they're "single responsibility classes" that bloats everything up because you cant precisely control layout for related data.
struct optional_ints {
u32 *have_value; // tightly packed bitset where bitN is the optional::have_value() for values[N]
i32 *values;
u32 num_values;
};
It would be very unlikely that you'd ever have an array of optionals. Normally you use this in a context where you care about the presence of a value AND the value (like a function return) so scattering loads across multiple places is strictly more overhead.
Did you know, if you wanted to write "tightly packed bitsets", you can just use non-generic structures? This doesn't undermine the need of Optional types at all.
Why is board so dumb at formulating a counter argument? It's like you are a bunch of 14 year olds in rebel phase.
>jesus christ
There's no way around it. Every value for i32 is a valid i32, so you must have at least one byte of overhead for a tag. And due to memory alignment being a thing, that tag must be at least as large as the i32 it shares a struct with. This is usually not a problem because the primary utility of optionals is an optional return that is typically immediately checked by the caller. If we look at disassemblies of this, we may find this often involves packing two 32-bit values into a 64-bit register, or in the case of Option<i64>, just using two registers.
Incidentally, if I wanted to store a bunch of optional i32 values, I'd strongly consider a hash table. Especially if I had a lot of missing values.
I really hate the amount of code you have to write to do anything. I write a lot of code by hand, so it really matters to me. Really painful writing Rust by hand.
>I really hate the amount of code you have to write to do anything.
You have to write more code than Haskell and Lisp but a lot less than C and C++.
What I thought was cool is that Rust's strict aliasing rules basically solve the functional problem of mutable data as well.
You literally cannot have mutability concerns in rust by design and most FPisms are sort of moot.
Truly a fascinating language.