2

I am new to Ceylon, and am currently exploring how to port some existing software written in TypeScript (essentially JavaScript) to Ceylon, so that it can run both on JavaScript engines and JVM.

Does anybody know how to code an equivalent of this Java stuff in Ceylon:

public class Engine { ... } // Some given class definition

public interface Cont extends Callable1<Cont,Engine> {}

where Callable1<Y,X> is a Java equivalent of Ceylon's Callable<Y,[X]>.

The idea is that an instance of Cont, say named c, would be a function either returning another Cont, or a null.

In Java, the code using this would look like:

// Somewhere
public static void exec(Cont c, Engine e) {
  while (c != null) c = c.call(e);
}

(This is essentially a trampoline, where each called function returns the continuation, or null when the computation is done.)

Also, in Ceylon I would like to pass functions as instances of Cont.


After reading the replies, I've come up to the following solution which uses both the proper result typing (Cont? instead of Anything), and null-testing (for performance):

shared interface Cont { shared formal Cont? call(Engine e); }

// A wrapper class for an actual continuation function
class ContWrap(Cont?(Engine) fun) satisfies Cont {
    shared actual Cont? call(Engine e) => fun(e);
}

shared Cont wrap(Cont?(Engine) fun) {
    return ContWrap(fun);
}

// Somewhere
shared void exec(variable Cont? cont) {
    while (exists cc = cont) {
        cont = cc.call(this);
    }
}

This suits me, at the cost of creating an additional small object every time, and passing functions through wrap.

user241495
  • 23
  • 3
  • If `wrap(fun)` is the same as `ContWrap(fun)`, why not just do the latter directly? – gdejohn Feb 21 '15 at 12:14
  • 1
    @gdejohn, this is purely for design reasons: `ContWrap` is an auxiliary internal class implementing a specific behaviour, so the users should not be aware of it (and should certainly not write code that depends on it). On the other hand, function `wrap` is a part of the API (i.e., it is a factory function). – user241495 Feb 23 '15 at 10:21

2 Answers2

3

Custom implementations of Callable have been discussed, but it's not yet possible. You don't need that, though.

shared class Engine() {
    // ...
}

shared interface Continuation {
    shared formal Continuation? call(Engine engine);
}

shared void execute(variable Continuation? continuation, Engine engine) {
    while ((continuation = continuation?.call(engine)) exists) {}
}

object continuation satisfies Continuation {
    call(Engine engine) => null;
}

// usage
execute(continuation, Engine());

// usage in 1.2 with inline object expression
execute(object satisfies Continuation { call(Engine engine) => null; }, Engine());

Since Continuation doesn't (can't) satisfy Callable, you can't just pass in a function. But in the upcoming release (1.2, available now via GitHub), you'll at least be able to use inline object expressions.

Note that this isn't idiomatic Ceylon, just a fairly direct translation from your Java.

Paŭlo Ebermann
  • 73,284
  • 20
  • 146
  • 210
gdejohn
  • 7,451
  • 1
  • 33
  • 49
  • Thanks @gdejohn! This could be a good solution, at least in 1.2 (otherwise I guess one would have to write a class for each function object, which would be impractical). But, a great advantage is that it uses fast `null` checking rather than (I guess much slower) `is Type` checking. I'll wait for the 1.2 to be released with the updated IDE support. – user241495 Feb 19 '15 at 08:57
1

This seems to compile, but it looks pretty horrible:

class Engine() {}

Anything(Engine) foo(Engine e) {
    return foo;
}

// Somewhere
variable Anything(Engine)? f = foo;
while (is Anything(Engine)(Engine) f2=f) {
    f = f2(e);
}
Quintesse
  • 452
  • 2
  • 9
  • Thanks a lot for the suggestion! My only concern is that `is`-testing may incur a significant overhead over `null`-testing, as the former needs to look at the runtime type information and follow a class hierarchy, and the latter is simply an integer comparison. – user241495 Feb 19 '15 at 06:42
  • (to continue) That is why I'd opt for a `null`-testing variant. Btw, I understand the intricacies of the type system from the theoretical point of view, but for instance in Typescript, one can simply specify `interface Cont { (e: Engine): Cont; }` the so-called call-signature interface and pass functions there. So this can be done easily and typechecked in Java and Typescript, but unfortunately not that easily in Ceylon. – user241495 Feb 19 '15 at 09:01
  • @user241495, is [this](https://github.com/ceylon/ceylon-spec/issues/521) what you're talking about? – gdejohn Feb 19 '15 at 09:22