Hacker News new | ask | show | jobs
by lex-lightning 854 days ago
This isn’t always possible, even in lisp. Compare to Rust’s non-lexical lifetimes
2 comments

No it isn't. In C you often have to keep pointers around for longer than you need to so that you can free() them at the end of a block.

But still, the original post said that they have to deal with this:

> because I always see about 10 uninitialized variables at the start of each function, where half of those are used only once

That's bad!

I don't see how it could ever be not possible. Maybe not always convenient, but impossible? How?
a = getInt(); b = getInt() + a; c = getInt(); d = b / c; puts(d); puts(c); puts(b); puts(a);

Nothing in the middle of this code prevents using ‘a’ even though it isn’t relevant to ‘c’. And there’s no way to arrange it to change that.

That's only a problem if your language doesn't allow separating computation from executing effects. With monads it's pretty straightforward:

    def doD(b: Int, c: Int) = puts(b / c)
    def doCD(b: Int) = getInt >>= {c => doD(b, c) >> puts(c)}
    def doBCD(a: Int) = getInt >>= {tb => val b = tb+a; doCD(b) >> puts(b)}
    getInt >>= {a => doBCD(a) >> puts(a)}
If I wanted to deeply pedantic, I think the blocks associated with those monady bits keep the parameters in scope longer than needed.

But I never got into Haskell so I don’t really know.

> If I wanted to deeply pedantic, I think the blocks associated with those monady bits keep the parameters in scope longer than needed.

I don't think so? If you write it as a "flat" block using do notation then anything from earlier in the block is accessible later in the block. But local functions are real functions with lexical scope, they don't get hoisted up to the containing scope or anything. (And FWIW that's Scala-style syntax, although the ideas are very Haskell-like)

Dope. Thanks.

Have you written much Prolog?

I was thinking about in Prolog terms and I think it’s the hardest paradigm to avoid the scoping problem in

If `c` does not depend on `a`, then the re-arrangement seems simple: Define `c` later.
> If `c` does not depend on `a`, then the re-arrangement seems simple: Define `c` later.

How does that prevent 'a' from being in the scope where 'c' is defined? It is not clear to me what you mean by defining 'c' later. Later where? And how do we ensure that 'a' is not accessible there?

I think lex-lightning's comment has a point. We need to define 'a' and we need to define 'c' and then we need to print 'c' before printing 'a', so although 'c' does not depend on 'a', we still need to keep 'a' in scope because we cannot print it before we print 'c'.

Perhaps a convoluted solution to the problem posed in lex-lightning's comment would be something like this:

  #include <stdio.h>

  int getint() {
      return 42;
  }

  int main()
  {
      int a = getint();
      int b = getint() + a;
      int c;

      {
          // Shadow 'a' here to make the outer 'a' inaccessible in this scope.
          int *a = NULL;
          (void) a;
          c = getint();
      }

      int d = b / c;

      printf("%d\n", d);
      printf("%d\n", c);
      printf("%d\n", b);
      printf("%d\n", a);

      return 0;
  }
Output:

  $ cc -std=c99 -Wall -Wextra -pedantic foo.c && ./a.out
  2
  42
  84
  42
I see now how the problem is. Thanks. I have a suspicion, that this depends on side effects and that without assignments and without side effects things might look different. Somehow I don't seem to come across these scenarios, when I write FP programs.
Hey I’m down to party with some monads, but I leave that as an exercise to the reader.

One note: each variable only gets assigned to once, so FP won’t magic that away.

The side effects might have a different way of management in FP. But the IO will happen in the same order in reality anyway.

So I don’t think there’s a way out, but I honestly would be interested to see it.

I appreciate your interest.

I’d argue that even declaring ‘c’ is against the spirit though. To say nothing of the eldritch shadow cast there (no disrespect, I think we’re both enjoying playing around here)

> I’d argue that even declaring ‘c’ is against the spirit though.

Yes, I agree. My code example is meant to show the best we can possibly do to solve the problem in your comment and how contrived that solution is. The fact that this solution is contrived and diverges from the intended spirit of the problem only emphasizes the point of your comment.

That's why I am eager to understand what zelphirkalt really meant when they wrote, "the re-arrangement seems simple: define `c` later". It seems far from simple and, in fact, impossible if we want to avoid contrived solutions.