Skip to content
This repository has been archived by the owner on Apr 13, 2023. It is now read-only.

Allow stateful functions through static variables #7433

Open
xkr47 opened this issue Oct 9, 2018 · 6 comments
Open

Allow stateful functions through static variables #7433

xkr47 opened this issue Oct 9, 2018 · 6 comments

Comments

@xkr47
Copy link
Contributor

xkr47 commented Oct 9, 2018

Sometimes it would be cool if functions could have static variables, which would be about the same as an object with fields and implementing a java Functional interface.

So I could do:

value x = foo.filter((y) {
    static variable value done = false;
    if (!done) {
      doit();
      done=true;
    }
    return true;
  });

instead of

variable value done = false;
value x = foo.filter((y) {
    if (!done) {
      doit();
      done=true;
    }
    return true;
  });

This was discussed in gitter; (hopefully correct) summary here:

  • @gavinking seemed interested
  • @jvasileff was worried that "static" would have the appearance of being shared between all "instances" of the function (as opposed to how it was described by the above "instead of" example where every time the outmost scope is entered a new instance variable is created).
  • @FroMage was worried about function transformations and calls in super calls
@lucaswerkmeister
Copy link
Contributor

A feature like this might be useful, but I don’t see what this has to do with the existing meaning of static, and I don’t think we should copy C’s habit of reusing keywords for wildly different behavior here. If I saw a static value in a function –

class C() {
    void f() {
        static variable Integer i = 0;
        print(++i);
    }
}

C c = C();

– then I would expect one of the following to be allowed:

Integer i = C.f.i;
Integer i = c.f.i;

Which one? No idea. (I suppose that’s the same as @jvasileff’s concern.)

@jvasileff
Copy link
Contributor

jvasileff commented Oct 10, 2018

I think it's interesting to explore reducing the differences between classes/objects and functions, and this feature (syntax aside) looks pretty neat. But, I wonder if it can be really justified.

  • Would it be used often enough to not just be an obscure feature that is rarely seen and therefore not understood when it is seen?
  • Are there use cases where it significantly improves code clarity and readability?

Is memoizeNew below that much better than memoizeOld?

shared Result() memoizeOld<Result>(Result() f)
        given Result satisfies Object {
    variable Result? memo = null;
    return () => memo = memo else f();
}

shared Result() memoizeNew<Result>(Result() f)
        given Result satisfies Object => () {
    static variable Result? memo = null;
    return memo = memo else f();
};

Edit: for completeness, the curried form:

shared Result memoizeNew2<Result>(Result() f)()
        given Result satisfies Object {
    static variable Result? memo = null;
    return memo = memo else f();
}

@ghost
Copy link

ghost commented Oct 18, 2018

Doesn’t this have the same problems as classes did before constructors were a thing?

Like, the argument of the function (which is in scope) must not be referenced from the static value’s specifier, which is awkward.

@xkr47
Copy link
Contributor Author

xkr47 commented Oct 19, 2018

Vote for closure!

@arseniiv
Copy link

arseniiv commented Oct 29, 2018

@Zambonifofex Could it be fixed the same way, then? A function with “constructors”, why not? It could even be a way to implement “overloaded” functions (whose types are perfectly expressible from the start).

// has type `Callable<Integer,[String]|[Boolean]>`
shared Integer sillyExample {
    value magic = 3;
    shared new (String s) {
        return s.size * magic;
    }
    shared new (Boolean b) {
        return if (b) then magic else 0;
    }
}

P. S. Oops, I forgot overloading the default constructor in classes seems to not be allowed, my bad. But some analogous idea seems not that crazy, nonetheless.

@ghost
Copy link

ghost commented Oct 29, 2018

@arseniiv, I thought about it myself, but I figured that, since Ceylon doesn’t have overloading (except within native("jvm")), it’d only be right to allow named constructors, which don’t really make sense in functions.

I guess it could make sense to allow a single default “constructor” in functions:

Integer hmmm
{
    static variable Integer foo = 0;
    new (Integer i)
    {
        foo++;
        return i + foo;
    }
}

shared void run()
{
    // In lambdas too:
    foo(
        function
        {
            static variable Integer foo = 0;
            new (Integer i)
            {
                foo++;
                return i + foo;
            }
        }
    );
}

Unfortunately, this not only is kinda silly, but, for lambdas, it also collides with the syntax for #7190, which I have grown to like.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

4 participants