ceylon icon indicating copy to clipboard operation
ceylon copied to clipboard

Allow stateful functions through static variables

Open xkr47 opened this issue 7 years ago • 6 comments
trafficstars

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

xkr47 avatar Oct 09 '18 17:10 xkr47

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.)

lucaswerkmeister avatar Oct 09 '18 21:10 lucaswerkmeister

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();
}

jvasileff avatar Oct 10 '18 16:10 jvasileff

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.

ghost avatar Oct 18 '18 05:10 ghost

Vote for closure!

xkr47 avatar Oct 19 '18 12:10 xkr47

@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.

arseniiv avatar Oct 29 '18 16:10 arseniiv

@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.

ghost avatar Oct 29 '18 16:10 ghost