Comparing two Objects

74 views
Skip to first unread message

Dietmar Höhmann

unread,
Jul 16, 2015, 12:37:27 PM7/16/15
to ceylon...@googlegroups.com
I'm trying to do the following:

Object? a = ...
Object? b = ...

...

//assert a and b are of the same type

//assert a and b satisfy Comparable<>

if (a > b) {...}

Is this at all possible?

In other words: I have two Objects and if both are the same Type and both satisfy Comparable, I'd like to compare them - without needing to know if they are String, Integers, Floats or whatever.

John Vasileff

unread,
Jul 16, 2015, 12:50:45 PM7/16/15
to ceylon...@googlegroups.com
This appears to work:

shared void testCompare() {
    Object a = 1;
    Object b = 2;
    print(`function Comparable.compare`.memberInvoke(a, [], b));
}

Although I think it would be much better to not lose the type information in the first place (in other words, make the function that needs to invoke `compare` generic.)

John

--
You received this message because you are subscribed to the Google Groups "ceylon-users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to ceylon-users...@googlegroups.com.
To post to this group, send email to ceylon...@googlegroups.com.
Visit this group at http://groups.google.com/group/ceylon-users.
To view this discussion on the web visit https://groups.google.com/d/msgid/ceylon-users/b5348f86-256d-4a92-8779-2d3896c3c49e%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Dietmar Höhmann

unread,
Jul 16, 2015, 4:04:44 PM7/16/15
to ceylon...@googlegroups.com
Thanks John, your solution works perfectly.

BUT only on JVM :-( on JS it throws:

/home/dietmar/Software/eclipse/plugins/com.redhat.ceylon.dist.repo_1.1.0.v20141013-1416/repo/ceylon/language/1.1.0/ceylon.language-1.1.0.js:5498
if (a[i]!==undefined && !is$(a[i],val_t))throw IncompatibleTypeException$meta$
                                               
^
ceylon
.language.meta.model::IncompatibleTypeException "Wrong type for argument 0, expected ceylon.language::Integer got ceylon.language::Integer"

This is most likely a bug in the language module.

The background for all this is: I have a nice little script interpreter I'm porting from C# to Ceylon. And I don't have any chance to predict the types the script will later use. In general I'm absolutely with you: Make applications as type safe as possible. But this is a special case. I guess I need to put all the dynamic parts in a separate module that uses the meta model on JVM and dynamic blocks on JS.


Gavin King

unread,
Jul 16, 2015, 4:23:50 PM7/16/15
to ceylon...@googlegroups.com
Report the bug please.
> --
> You received this message because you are subscribed to the Google Groups
> "ceylon-users" group.
> To unsubscribe from this group and stop receiving emails from it, send an
> email to ceylon-users...@googlegroups.com.
> To post to this group, send email to ceylon...@googlegroups.com.
> Visit this group at http://groups.google.com/group/ceylon-users.
> To view this discussion on the web visit
> https://groups.google.com/d/msgid/ceylon-users/1725155c-b37e-4693-8b44-8021ba37e148%40googlegroups.com.
>
> For more options, visit https://groups.google.com/d/optout.



--
Gavin King
ga...@ceylon-lang.org
http://profiles.google.com/gavin.king
http://ceylon-lang.org
http://hibernate.org
http://seamframework.org

Tom Bentley

unread,
Jul 16, 2015, 4:30:09 PM7/16/15
to ceylon...@googlegroups.com
Comparison cmp<T>(T a, T b) given T satisfies Comparable<T> => a <=> b;

shared void use() {
    Object x = 2;
    Object y = 3;
    assert(is Comparable<Anything> x);
    assert(is Comparable<Anything> y);
    print(cmp(x, y));
}

This seems to do most of what you need, but it throws a CCE if the types are not compatible (e.g. if y is a String), which would seem to be another bug.

Gavin King

unread,
Jul 16, 2015, 4:34:05 PM7/16/15
to ceylon...@googlegroups.com

Gavin King

unread,
Jul 16, 2015, 4:34:20 PM7/16/15
to ceylon...@googlegroups.com
In fact, nothing is a Comparable<Anything>.

Tom Bentley

unread,
Jul 16, 2015, 4:35:52 PM7/16/15
to ceylon...@googlegroups.com
Well it runs in the IDE, so there's a bug *somewhere*.

John Vasileff

unread,
Jul 16, 2015, 4:37:21 PM7/16/15
to ceylon...@googlegroups.com
But `x` is not in fact `Comparable<Anything>`, right???

Gavin King

unread,
Jul 16, 2015, 4:39:08 PM7/16/15
to ceylon...@googlegroups.com

Gavin King

unread,
Jul 16, 2015, 4:41:00 PM7/16/15
to ceylon...@googlegroups.com
Hrm WTF on JS you get an assertion failure as expected but not on the
JVM. Do yes, indeed, there's a bug in the Java runtime. Strange: I
tested a lot of that kind of stuff recently...

Gavin King

unread,
Jul 16, 2015, 4:53:32 PM7/16/15
to ceylon...@googlegroups.com

Dietmar Höhmann

unread,
Jul 16, 2015, 4:54:40 PM7/16/15
to ceylon...@googlegroups.com
done - but not quite sure if this is the right place: https://github.com/ceylon/ceylon-sdk/issues/405
Does this belong to the platform modules (i.e. SDK)?

Dietmar Höhmann

unread,
Jul 16, 2015, 4:54:48 PM7/16/15
to ceylon...@googlegroups.com

Gavin King

unread,
Jul 16, 2015, 4:56:02 PM7/16/15
to ceylon...@googlegroups.com
Naw, it's a language module bug. Project ceylon.language.
> --
> You received this message because you are subscribed to the Google Groups
> "ceylon-users" group.
> To unsubscribe from this group and stop receiving emails from it, send an
> email to ceylon-users...@googlegroups.com.
> To post to this group, send email to ceylon...@googlegroups.com.
> Visit this group at http://groups.google.com/group/ceylon-users.
> To view this discussion on the web visit
> https://groups.google.com/d/msgid/ceylon-users/51f6f464-0a9a-4031-8983-3c9f0aa838cc%40googlegroups.com.

John Vasileff

unread,
Jul 16, 2015, 10:30:48 PM7/16/15
to ceylon...@googlegroups.com
I just realized that this problem raises an interesting use case for a couple proposed enhancements.


The idea is that we would create a type alias for the unknown type of the objects, check to see if the type is Comparable, and then perform operations that require Comparables on instances of this type without using the metamodel api:

shared void printSorted(objects) {
    "Must be a homogeneous sequence"
    [Object+] objects;

    // Create a type reference for the element type of `objects`
    alias T = type(objects.first);

    // Test to see if `T` really is `Comparable`
    if (!T satisfies Comparable<T>) {
        throw Exception("oops, we expected Comparables");
    }
    else {
        // In this block, `T satisfies Comparable<T>`

        "A correctly type container of our Comparables (discarding non-Ts)"
        {T*} ourComparables = objects.narrow<T>();

        

        // Sort (because we can!), and print
        print(sort(ourComparables));
    }
}

John

--
You received this message because you are subscribed to the Google Groups "ceylon-users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to ceylon-users...@googlegroups.com.
To post to this group, send email to ceylon...@googlegroups.com.
Visit this group at http://groups.google.com/group/ceylon-users.

Gavin King

unread,
Jul 17, 2015, 12:50:32 PM7/17/15
to ceylon...@googlegroups.com
Not quite right, since the runtime type of objects.first is not
necessarily a supertype of the runtime type of the other objects. You
would need to iterate the objects and form a union of their types
somehow. (That's possible in principle if we provided the right APIs.)
> https://groups.google.com/d/msgid/ceylon-users/B061CF78-A690-40BE-8B5C-0121C93494D9%40vasileff.com.
>
> For more options, visit https://groups.google.com/d/optout.



John Vasileff

unread,
Jul 17, 2015, 12:55:08 PM7/17/15
to ceylon...@googlegroups.com
Yeah, I cheated a bit by saying “homogeneous sequence” :)
> To view this discussion on the web visit https://groups.google.com/d/msgid/ceylon-users/CAP7PoCe2G0w7D%2Bwsd%3DVrewCjY%3Dntr9TiCwa3AoWOVFQ4mzcD9A%40mail.gmail.com.

Ross Tate

unread,
Jul 17, 2015, 11:02:01 PM7/17/15
to ceylon...@googlegroups.com
Don't screw up your type system to satisfy this use case. This use case is exactly what gradual typing is designed for. Since you don't have gradual typing, then the first solution given is totally the correct one: you should just reflect on each object to get the compare method for that object and then supply that method with the appropriate object. If you really want to check that the compare method has Comparable's contract, then in each iteration you should grab the dynamic type of the second object and then check if the first object satisfies Comparable of that type.

I'm working from my phone for a while, so sorry if this is rudely terse!

John Vasileff

unread,
Jul 18, 2015, 12:57:49 PM7/18/15
to ceylon...@googlegroups.com
I agree that this use case doesn’t provide a compelling reason for a language change, since it is rare to need to recover self types. But would supporting these features really be harmful to the type system? I’ve thought of these proposals as a sort of meta programming rather than a more fundamental type system change, but perhaps I’m missing something.

More generally, I think they would help make meta programming safer with improved clarity, which I think is very relevant for Ceylon for things like web and persistence frameworks.

Keeping with the Comparable them, an apples-to-apples, uh, comparison:

Current:

shared [Object*] sortObjects([Object+] objects) {
    value comparableType = comparableTypeFor(objects.collect(type));

    // no discards, since we trust comparableTypeFor()
    value comparables = `function Iterable.narrow`.memberInvoke(
        objects, [comparableType]);

    value result = `function sort`.invoke([comparableType], comparables);
    assert (is [Object*] result);
    return result;
}

Proposed:

shared [Object*] sortObjects([Object+] objects) {
    alias T = comparableTypeFor(objects.collect(type));

    // safe since we trust comparableTypeFor()
    assert (T satisfies Comparable<T>);

    // safe with no discards, since we trust comparableTypeFor()
    value comparables = objects.narrow<T>();

    return sort(comparables);
}

I think the static type checking and code clarity in the second example is far superior.

For completeness, the implementation for `comparableTypeFor` that seems to work:

Type<Anything> comparableTypeFor([Type<Anything>+] types) {
    value t = types.fold<Type<Anything>?>(null)((a,b) {
        if (!exists a) {
            if (!b.subtypeOf(`Comparable<Nothing>`)) {
                throw Exception("oops, ``b`` does not satisfy Comparable");
            }
            return b;
        }
        else {
            // evidence of existence and prior checks ensure `apply<>(a)` is valid
            if (!b.subtypeOf(`interface Comparable`.apply<>(a))) {
                throw Exception("oops, ``b`` cannot be compared to ``a``");
            }
            return a.union(b);
        }
    });
    assert (exists t);
    return t;
}

John

Reply all
Reply to author
Forward
0 new messages