Live data from Hacker News

Why Rust closures are somewhat hard

stevedonovan.github.io

31–40 of 92 posts

Re: Why Rust closures are somewhat hard

#31
There's a lot of confusion about this.

A lambda is just a function without a name. (This feature tends to come with special syntax, although it doesn't have to.)

A nested function is a function defined inside another function which can access the variables of the enclosing function. (A nested function can be a lambda, but it doesn't have to be. Some languages have named nested functions. A lambda doesn't have to be a nested function; it doesn't have to pull in any variables from an outer scope.)

A closure is a nested function which can outlive the outer function, keeping its data alive. (A closure can be named, the usual case in Python and Javascript, or anonymous. So a closure need not be a lambda.)

Closures are easy to implement in garbage collected languages, but hard in explicitly allocated ones, because extending the lifetime of the imported data gets complicated.

So the options are:

- Lambda without any external data access -- typical use, comparison function for a sort.

- Lambda with external data access, but not outliving its enclosing function - typical use, iteration expression

- Named function with no external data access. Typical use, a local function in languages that don't do local functions well, such as C.

- Named function with external data access, not outliving its enclosing function. Typical use, internal function within a function to avoid passing extra parameters.

- Named function with external data access, outliving its enclosing function. A true closure, but not a lambda. Typical use, saving state for a callback in Javascript by passing the function to something that will save it and invoke it later. An object, really. This was how LISP did objects.

- Lambda function with external data access, outliving its enclosing function. A true closure. Same uses as above, but in different languages.

Most languages offer some subset of these six options.

Re: Why Rust closures are somewhat hard

#32
post #13
post #8

I don't understand the intuition of closures and they turn me off to languages immediately. They feel like a hack from someone who didn't want to store a copy of a parent-scope variable within a function. The idea that I can touch variables that have gone out of scope (and that have ostensibly been GC'd) makes me feel that it is impossible to reason about variable lifetimes when dealing with closures. Is there some p…

I don't have a problem with the intuition but I'm with you on programming style. It's sort of a contrary opinion but I prefer to be explicit about state, and closures are sort of silently bundling up state for you behind the scenes. It's not very apparent from the syntax. I think languages should have a lighter-weight syntax for classes and that would subsume many use cases for closures. ----- While I've never progra…

C++ lambda has explicit capture list as well.

Re: Why Rust closures are somewhat hard

#33
> With Rust 1.26, there’s a simpler but completely equivalent notation:

  fn new_invoke(f: impl Fn(f64)->f64, x: f64) -> f64 {
      f(x)
  }
That actually isn’t completely equivalent. With the former example, `invoke::(x)` works, but with impl, `new_invoke::(x)` doesn’t work. This is a deliberate aspect of the design of impl in argument position, and part of the reason why it can be better to avoid it in libraries.

In the case of functions, this difference probably doesn’t matter, because you normally can’t name the type of a function anyway, and are extremely unlikely to wish to; but in other cases being able to type the turbofish can matter for ergonomics.

As an arbitrary example, take std::convert::Into::into: the type parameter is on the trait rather than the method, so you can’t do `x.into::()`, but if you need to constrain the type you must do so otherwise, e.g. `let y: T = x.into();` or `>::into(x)`. (In that case in particular, you’d write `T::from(x)` instead, but there won’t always be such an ergonomic replacement.)

Re: Why Rust closures are somewhat hard

#34

Earlier quoted context omitted.

It seems to me that it's not that Rust is hard, it's that all programming is hard but Rust exposes the complexity so we can make a more informed decision about the trade offs of safety and performance etc, while other languages tend to hide the complexity (null reference, race conditions, etc).

Yeah. Makes sense. Rust just explicitly exposes certain features so that the programmer has more control.

something i've read times and times again: programmers of other languages who learned rust tend to discover potentially problematic code in their earlier work.

Re: Why Rust closures are somewhat hard

#35

The fact that you can even do stuff like this in Rust is amazing. It’s simultaneously: relatively clean code, very efficient and type-safe. I like it.

> The fact that you can even do stuff like this in Rust is amazing.

Stuff like this? You mean closures? That's what you find amazing? What is considered ordinary in other programming languages is considered amazing in Rust. Amazing.

> It’s simultaneously: relatively clean code, very efficient and type-safe. I like it.

You like it? Do you work for mozilla? If there is a tech evangelist of the year award, I will vote for you. I've never seen someone take absolutely nothing and try to spin it into something positive.

Edit: Instadownvotes by the evangelists. I like it!

Re: Why Rust closures are somewhat hard

#36
post #14

Earlier quoted context omitted.

Trait aliases will make some of those unwieldly declarations a bit easier on the eyes #![feature(trait_alias)] trait Map = Fn(T) -> T; fn compose (f1: impl Map , f2: impl Map ) -> impl Map { |n| f1(f2(n)) }

This is a nice feature, do you know any other language that has something similar?

Scala's type aliases are pretty similar.

Re: Why Rust closures are somewhat hard

#37
post #31

There's a lot of confusion about this. A lambda is just a function without a name. (This feature tends to come with special syntax, although it doesn't have to.) A nested function is a function defined inside another function which can access the variables of the enclosing function. (A nested function can be a lambda, but it doesn't have to be. Some languages have named nested functions. A lambda doesn't have to be a…

[deleted]

Re: Why Rust closures are somewhat hard

#38
post #8

I don't understand the intuition of closures and they turn me off to languages immediately. They feel like a hack from someone who didn't want to store a copy of a parent-scope variable within a function. The idea that I can touch variables that have gone out of scope (and that have ostensibly been GC'd) makes me feel that it is impossible to reason about variable lifetimes when dealing with closures. Is there some p…

I've got just the paper for you! The Implementation of Closures in Lua:

https://pdfs.semanticscholar.org/73a2/e3c03f799956aa5a3188e4...

This is just one way to do it, but it's an efficient way, and easy to understand.

Re: Why Rust closures are somewhat hard

#39
It feels like the part that's missing is the ability to abstract over these different types of function, polymorphically. At least, that seems to be where things fall down when we try to talk about e.g. implementing a Functor trait in Rust.

As a concrete example, the `compose` method given should clearly be the same for `Fn`, `FnOnce`, and `FnMut`. Can we write it once and reuse it for `Fn`, `FnOnce` and `FnMut`? If not, why not?

Re: Why Rust closures are somewhat hard

#40

The fact that you can even do stuff like this in Rust is amazing. It’s simultaneously: relatively clean code, very efficient and type-safe. I like it.

Not sure I agree with "relatively clean code" when one of the examples show "fn compose (f1: impl Fn(T)->T, f2: impl Fn(T)->T) -> impl Fn(T)->T {" which is just a mix-match of keywords and other things, with tons of syntax embedded in just one line. But as always, depends on where you come from. I mostly deal with lisp languages nowadays, so guessing it's just my view that the line quoted above seems complex enough t…

[deleted]
Post reply on HN