Questions about Ceylon from newbie :)

184 views
Skip to first unread message

Alexander Kornilov

unread,
Nov 30, 2017, 4:17:13 AM11/30/17
to ceylon-users

Hi guys!

 

At first I want thank you very much for Ceylon! Hope this language has a great future.

 

I am a big fan of Java and wrote many lines of code on it. I like Java and now, but I want reduce verbosity and I'm getting a little tired from Null Pointer trap. Also I don't happy how Java growing - lambda syntax and implementation in 1.8 is ugly for me.

I tried to C#, but don't like it so much, it look like a Java with similar downsides and additional limitations.

I had great hopes of Scala, but most the language's idioms excite discomfort for me.

Maybe Kotlin... But I found Ceylon!

It was great! This language is very beautiful logical and think over.


Now I'am trying to learn Ceylon and have some questions:

1) I don't understand any reason why references to function and method couldn't be compared. I am not mathematic and look from programmer point of view:

shared Integer someFunction(String param) {

   ...

}

 

value ref1 = someFunction;

value ref2 = someFunction;

 

In my opinion ref1 is identical (and equals) with ref2.

 

Could you please explain why them not equal?

 

class SomeClass() {

    shared void classMethod() {

    }

}

 

value firstInstance = SomeClass();

value secondInstance = SomeClass();

 

value ref1 = fistInstance.classMethod;

value ref2 = fistInstance.classMethod;

value ref3 = secondInstance.classMethod;

value ref4 = secondInstance.classMethod;

In this example ref1 === ref2 and ref3 === ref4, but ref1(2) != ref3(4).

It’s logical for me.

It’s no matter in most cases but some useful things couldn’t be performed:

e.g. we have unique list of listeners: Set<Integer(Event)> listeners…

listeners.add(refToFunc); // OK

listeners.add(refToFunc); // We’ll had same listener two times, not good for unique set.

And we can't remove listener from the set.

It's sad… Are there some heavy reason for this strange restriction?


2) I don’t understand reasons of ‘protected’ visibility level elimination. In my opinion it helpful for example in design pattern ‘Template method’ when base abstract class has some protected methods which should be visible only in subclasses.

I think some customization of shared annotation is reasonable. The extra 'restricted' annotation could be replaced by shared with optional visibility level:

+ shared

+ shared(subclasses)

+ shared(package)

+ shared(module)


3) I'm trying to write something working on Ceylon as to learn it faster.

To investigate it metamodel I want create some base class 'DataContainer' which allow to instantiate immutable classes with build-in equals+hash implementation: Identifier(125, "ab") == Identifier(125, "ab")

 

So I wrote this:

shared abstract class DataContainer(ClassDeclaration classDecl) {
   
value members = {
       
for (item in classDecl.memberDeclarations<ValueDeclaration>())
           
if (!item.variable
                   
&& item.name != "hash"
                   
&& item.name != "string") item
   
};
   
variable Integer? hashCode = null;

   
shared actual Boolean equals(Object that) {
       
if (is DataContainer that) {
           
for (item in members) {
               
value thisMember = item.memberGet(this);
                
value thatMember = item.memberGet(that);
               
if (exists thisMember, exists thatMember, thisMember != thatMember) {
                   
return false;
                }
               
if (thisMember exists != thatMember exists) {
                    
return false;
                }
            }
           
return true;
        }
       
return false;
    }

   
shared actual Integer hash => hashCode else (hashCode = calculateHash());

   
Integer calculateHash() {
       
variable value result = 0;
       
for(item in members) {
           
value itemValue = item.memberGet(this);
           
if (exists itemValue) {
               
result = result.xor(itemValue.hash);
            }
        }
       
return result;
    }
}

class Identifier(shared Integer? id, shared String? name) extends DataContainer(`class`) {}

 

All good for me except invoking DataContainer constructor with (`class`). Because if I use `class` inside super class it doesn't see any members of subclass.

How can I obtain actual list of extended class's members in base class methods?

Something likes `this` is not working...

Message has been deleted

Alexander Kornilov

unread,
Nov 30, 2017, 4:31:54 AM11/30/17
to ceylon-users
https://stackoverflow.com/questions/47561579/ceylon-metamodel

4) Question about generics. For example I have generic class:

shared class GenericTest<Element>() {

 

    shared void dump(Element precision) {

        if (this is GenericTest<String>) {

            print("Method for String called");

        } else if (this is GenericTest<Integer>) {

            print("Method for Integer called");

        }

    }

}

I found solution to detect Element type in runtime in this way: “if (this is GenericTest<Integer>) …”.

Can I just ask “Element” like “if (is Integer Element) …”? Maybe something wrong with syntax?

Can I write special method which will be used only for particular type e.g.:

    shared void dump<String>(Element precision) {

    }

Version of method when Element is String.

    shared void dump<Integer>(Element precision) {

    }

Version of method for Integer.

    shared void dump (Element precision) {

    }

Version for other types of Element.

 

5) Question about Eclipse IDE. The Idea IDE starting suggest me something when I typing text it very helpful – I don’t need write exactly “shared” and other annotations, keywords, values and etc. I just choose required. But in Eclipse completion triggered only when I typed dot. Is it possible to change triggering logic? For example activate completer when user typed at least 3 characters and after 200 ms delay?

Alexander Kornilov

unread,
Nov 30, 2017, 5:32:51 AM11/30/17
to ceylon-users
6) Are there possibilities to load external module in runtime? E.g. I need support of plugins. Each plugins is separate module (.car file). I need load it in runtime and instantiate some main class which satisfied plugin interface

Colin Bartolome

unread,
Nov 30, 2017, 11:25:19 AM11/30/17
to ceylon-users
For #4, try using "if (is Integer precision)" instead of "if (is Integer Element)" and it should work.

For #6, check out this part of the Tour: https://ceylon-lang.org/documentation/1.3/tour/modules/#services_and_service_providers

I haven't tried it myself, but it sounds like you might be looking to have your plugins act as service providers.

Alexander Kornilov

unread,
Nov 30, 2017, 12:09:33 PM11/30/17
to ceylon-users


четверг, 30 ноября 2017 г., 19:25:19 UTC+3 пользователь Colin Bartolome написал:
For #4, try using "if (is Integer precision)" instead of "if (is Integer Element)" and it should work.
It's OK when you have at least one parameter of method with type Element (ie generic), but if not? Nevertheless infromation about Element type is present in runtime but I don't know simple syntax to extract it.

 
For #6, check out this part of the Tour: https://ceylon-lang.org/documentation/1.3/tour/modules/#services_and_service_providers

I haven't tried it myself, but it sounds like you might be looking to have your plugins act as service providers.
I thought  about it, but where modules files will be searched? I need provide some path on file system where my plugins are installed but API doesn't allow this.

Colin Bartolome

unread,
Nov 30, 2017, 12:32:36 PM11/30/17
to ceylon-users
On Thursday, November 30, 2017 at 9:09:33 AM UTC-8, Alexander Kornilov wrote:
It's OK when you have at least one parameter of method with type Element (ie generic), but if not? Nevertheless infromation about Element type is present in runtime but I don't know simple syntax to extract it.

Good point. There might be an issue with the design of the class. That is, if it's trying to be generic, why do some of its functions need to change their behavior so much when the parameter is Integer or String?
 
I thought  about it, but where modules files will be searched? I need provide some path on file system where my plugins are installed but API doesn't allow this.

Good question. I'm not sure.

Alexander Kornilov

unread,
Nov 30, 2017, 12:48:43 PM11/30/17
to ceylon-users


четверг, 30 ноября 2017 г., 20:32:36 UTC+3 пользователь Colin Bartolome написал:
Good point. There might be an issue with the design of the class. That is, if it's trying to be generic, why do some of its functions need to change their behavior so much when the parameter is Integer or String?
 

If we are talking about design of the particular class I can agree with you, but now I am interesting in the Ceylon languge's posibilities itself.
e.g. C++ allow this (define different implementations of methods according template type) and it might be helpful for performance optimization I think.

Alexander Kornilov

unread,
Nov 30, 2017, 4:26:39 PM11/30/17
to ceylon-users

7) Anything - what kind of beast is that?

On the one hand it is synonym of 'void' in Ceylon and means "nothing" in this case.

But on the other hand it is the root of classes hierarchy and means any object as superclass of any other classes. If we take into account first definition (synonym of 'void') I can return from 'void' function e.g. Object because 'Anything' is superclass of it.

One substance with two mutually exclusive definitions - is enough confusing for me.


8) Are there direct support of JNI in Ceylon? I found 'native' annotation but it means call of Java (or JS). Are there in Ceylon something like Java 'native' keyword?

Colin Bartolome

unread,
Nov 30, 2017, 5:26:27 PM11/30/17
to ceylon-users
For the Anything class, it's not quite a synonym of void. Rather, void functions are considered to have a return type of Anything. It doesn't quite mean "nothing," either, but the Nothing class does! Basically, I think what happened is that the type checker needed to have a concept of the union of every type in the universe, which is the Anything class. For symmetry, it also needed to have the concept of the *intersection* of every type in the universe, which should be a completely empty class, so they made the Nothing class.

And as for the question of void functions being considered to return Anything, it makes sense when you're, say, making a collection of callback functions or something. You don't want to declare it as "collection of void functions," in case you want to add a function that returns something. You would want to declare it as "collection of functions that have return type Anything." Since void functions count as returning Anything, you can do exactly that.

Paco Soberón

unread,
Nov 30, 2017, 7:24:09 PM11/30/17
to ceylon...@googlegroups.com
2) The truth is that Java "protected" does not offer the kind of protection its name promises. You can call a protected method from anywhere in your code, just by creating an auxiliary class that extends the "protective" class:


class A {
    protected void p() {}
}

A a = new A();
a.p(); // sure, this does not compile

class Aux extends A {
    public void p2() {
        p();
    }
}

A a = new Aux();
a.p2(); // but this does compile


I've used this hack dozens of times in my life. So, the designers of Ceylon thought that mixing inheritance and protection in the same concept was not a good idea. You either want to share your code or not, it doesn't matter if the consumer is a child of yours or a total stranger.


3) Your intuition is right, you don't need to pass the ClassDeclaration of the as a constructor argument. You can get it in the parent like this:


    late value members = {
        for (item in classDeclaration(this).memberDeclarations<ValueDeclaration>())

        if (!item.variable
        && item.name != "hash"
        && item.name != "string") item
    };


Note that, since "value members" is declared in the initializer section, it initially forbids the use of "this", which can be solved by annotating it as "late". Read the chapter on initialization of the Ceylon Tour for more on this.


4) I guess you want something like this:


    shared void dump(Element precision) {
        if (`Element` == `String`) {

            print("Method for String called");
        } else if (`Element` == `Integer`) {

            print("Method for Integer called");
        }
    }


7) Colin has already given you an elaborated answer on this... Let's recap:

- "void" is not a synonym for any type. Like in other languages, a void function is one that does not return a value. Period. Try returning a value of any type, and the compiler will complain.

- "Anything" is the superclass of all classes, the union of all types. "Nothing" is the opposite, the intersection of all types. Although it may seem absurd to have a type that represents the absence of a type, it's incredibly useful when you start playing with the language. For example, in the documentation of the "Iterable" interface, see how you can define a nonempty stream just by using "Nothing" as the second type argument.

- For convenience, the type system considers a void function identical to a function declared to return "Anything". As Colin said, that allows you, for instance, to implement a set of callbacks as a set of functions that return "Anything", and populate it with both void and non-void functions, while in other languages you would probably need some kind of wrapper to do the same.


HTH.


2017-11-30 23:26 GMT+01:00 Colin Bartolome <cpc...@gmail.com>:
For the Anything class, it's not quite a synonym of void. Rather, void functions are considered to have a return type of Anything. It doesn't quite mean "nothing," either, but the Nothing class does! Basically, I think what happened is that the type checker needed to have a concept of the union of every type in the universe, which is the Anything class. For symmetry, it also needed to have the concept of the *intersection* of every type in the universe, which should be a completely empty class, so they made the Nothing class.

And as for the question of void functions being considered to return Anything, it makes sense when you're, say, making a collection of callback functions or something. You don't want to declare it as "collection of void functions," in case you want to add a function that returns something. You would want to declare it as "collection of functions that have return type Anything." Since void functions count as returning Anything, you can do exactly that.

--
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+unsubscribe@googlegroups.com.
To post to this group, send email to ceylon...@googlegroups.com.
Visit this group at https://groups.google.com/group/ceylon-users.
To view this discussion on the web visit https://groups.google.com/d/msgid/ceylon-users/86e9dd47-2702-46bb-88a0-c097bc9d6b17%40googlegroups.com.

For more options, visit https://groups.google.com/d/optout.

Alexander Kornilov

unread,
Dec 1, 2017, 3:39:44 AM12/1/17
to ceylon-users


пятница, 1 декабря 2017 г., 1:26:27 UTC+3 пользователь Colin Bartolome написал:
For the Anything class, it's not quite a synonym of void. Rather, void functions are considered to have a return type of Anything. It doesn't quite mean "nothing," either, but the Nothing class does! Basically, I think what happened is that the type checker needed to have a concept of the union of every type in the universe, which is the Anything class. For symmetry, it also needed to have the concept of the *intersection* of every type in the universe, which should be a completely empty class, so they made the Nothing class.

And as for the question of void functions being considered to return Anything, it makes sense when you're, say, making a collection of callback functions or something. You don't want to declare it as "collection of void functions," in case you want to add a function that returns something. You would want to declare it as "collection of functions that have return type Anything." Since void functions count as returning Anything, you can do exactly that.

Thank you, I got it.

Alexander Kornilov

unread,
Dec 1, 2017, 4:19:36 AM12/1/17
to ceylon-users
пятница, 1 декабря 2017 г., 3:24:09 UTC+3 пользователь Paco Soberón написал:
2) The truth is that Java "protected" does not offer the kind of protection its name promises. You can call a protected method from anywhere in your code, just by creating an auxiliary class that extends the "protective" class:


class A {
    protected void p() {}
}

A a = new A();
a.p(); // sure, this does not compile

class Aux extends A {
    public void p2() {
        p();
    }
}

A a = new Aux();
a.p2(); // but this does compile


I've used this hack dozens of times in my life. So, the designers of Ceylon thought that mixing inheritance and protection in the same concept was not a good idea. You either want to share your code or not, it doesn't matter if the consumer is a child of yours or a total stranger.


I couldn't agree with you. Let me explain my point of view.
Consider class A from your example. It design is clean for me. When I looking to this code I understand intention of author: he want that the method p() will be visible only in subclasses and Java allows it by the 'protected' keyword.
Lets go to Aux class. The protected method p() is used in the public method p2(). Is it bad design? My answer is: it depends on context if you created p2() only to open access to p() the design is really bad, but if you have more code in p2() and you need p() why not? Anyhow it is the issue of Aux class design and not of the class A and all the more not of languge which provide you 'protected' visibility instrument.

My example is very simple variation of the "Template method" design pattern in Java:
    public abstract class TemplateMethodBaseClass {

        // Here is the public interface of class
        public void PerfromSomthing(String data) {
            // Here is common logic which is applicable for most subclasses

            // The evaluation of dataVal is specific for each subclasses
            final int dataVal = EvaluateSomeValue(data);
        }

        // Here is protected part which should be visible only in subclasses

        protected abstract int EvaluateSomeValue(String data);
    }

    public class TemplateMethodClient extends TemplateMethodBaseClass {

        @Override
        protected int EvaluateSomeValue(String data) {

            // Perform this class specific logic here
            return 0;
        }
    }

Naturally I don't want external classes which use TemplateMethodClient can access to EvaluateSomeValue().

How can I implement this pattern in Ceylon?

Maybe "shared restricted formal int EvaluateSomeValue(String data);" ?
 
3) Your intuition is right, you don't need to pass the ClassDeclaration of the as a constructor argument. You can get it in the parent like this:


    late value members = {
        for (item in classDeclaration(this).memberDeclarations<ValueDeclaration>())
        if (!item.variable
        && item.name != "hash"
        && item.name != "string") item
    };


Note that, since "value members" is declared in the initializer section, it initially forbids the use of "this", which can be solved by annotating it as "late". Read the chapter on initialization of the Ceylon Tour for more on this.

I have tried use this one:
late value members = {
for (i in `class`.memberDeclarations<ValueDeclaration>())
if (!i.variable, i.name != "string", i.name != "hash") i
};

But result is same - members is empty.
`this` - not working - compilation error.

What is "classDeclaration(this)." in your example?
 
4) I guess you want something like this:


    shared void dump(Element precision) {
        if (`Element` == `String`) {
            print("Method for String called");
        } else if (`Element` == `Integer`) {
            print("Method for Integer called");
        }
    }

Thank you, it is what I really want :)
Is it possible to use in switch/case?

#8 (JNI): I have tried this one:

Import 'native' keyword from Java:
import java.lang { jni = native }

Declare native method without body in some class:
native void doNativeCall(Integer num);

It's OK on this stage, but "doNativeCall" is just call to Java (not JNI).

Try add 'jni' annotation (synonym of java.lang.native):
native jni void doNativeCall(Integer num);

And have got compilation error: "illegal reference to native declaration 'jni': native declaration 'doNativeCall' has a different backend"
What backend means?




Enrique Zamudio

unread,
Dec 1, 2017, 9:45:15 AM12/1/17
to ceylon-users
Anything is the root class. It only has two subclasses: Object and Null. Yes, Null is a type in Ceylon; that's how you get null safety: all types extends Object, except Null. The only way you can accept null somewhere is if the type is Anything, because it's the supertype of Null, or if you use a union type of Null and something else.

Nothing, on the other hand, is the bottom class. It extends all existing types in the system, and it cannot have any subtypes.

Alexander Kornilov

unread,
Dec 1, 2017, 11:00:57 AM12/1/17
to ceylon-users
пятница, 1 декабря 2017 г., 17:45:15 UTC+3 пользователь Enrique Zamudio написал:
Anything is the root class. It only has two subclasses: Object and Null. Yes, Null is a type in Ceylon; that's how you get null safety: all types extends Object, except Null. The only way you can accept null somewhere is if the type is Anything, because it's the supertype of Null, or if you use a union type of Null and something else.

Nothing, on the other hand, is the bottom class. It extends all existing types in the system, and it cannot have any subtypes.


Thank you, I understand this, but I had confused by connection between 'Anything' and 'void'.

Alexander Kornilov

unread,
Dec 1, 2017, 11:07:13 AM12/1/17
to ceylon-users


пятница, 1 декабря 2017 г., 12:19:36 UTC+3 пользователь Alexander Kornilov написал:
I have tried use this one:
late value members = {
for (i in `class`.memberDeclarations<ValueDeclaration>())
if (!i.variable, i.name != "string", i.name != "hash") i
};

But result is same - members is empty.
`this` - not working - compilation error.

What is "classDeclaration(this)." in your example?#8 (JNI): I have tried this one:

Ignore this please, I have got your solution - classDeclaration() is the function of ceylon.language.meta.
This is the final code - it work fine for me:
shared abstract class DataContainer() {
variable Integer? _hash = null;
variable ValueDeclaration[]? _members = null;

shared actual Boolean equals(Object that) {
if (is DataContainer that) {
            for (i in members) {
value thisMember = i.memberGet(this);
value thatMember = i.memberGet(that);
if (exists thisMember, exists thatMember) {
if (thisMember != thatMember) { return false; }
} else if (thisMember exists != thatMember exists) { return false; }
}
return true;
}
return false;
}

shared actual Integer hash => _hash else (_hash = calculateHash());

ValueDeclaration[] members => _members else (_members = [
for (i in classDeclaration(this).memberDeclarations<ValueDeclaration>())

if (!i.variable, i.name != "string", i.name != "hash") i
    ]);

Integer calculateHash() {
variable Integer result = 0;
for (i in members) {
if (exists member = i.memberGet(this)) {
result = result.xor(member.hash);
}
}
return result;
}
}
 
 
Import 'native' keyword from Java:
import java.lang { jni = native }

Declare native method without body in some class:
native void doNativeCall(Integer num);

It's OK on this stage, but "doNativeCall" is just call to Java (not JNI).

Try add 'jni' annotation (synonym of java.lang.native):
native jni void doNativeCall(Integer num);

And have got compilation error: "illegal reference to native declaration 'jni': native declaration 'doNativeCall' has a different backend"
What backend means?


I have tried this (without Ceylon 'native'):
jni void doNativeCall();
And it compilable, does it means that is real JNI call?
 

Alexander Kornilov

unread,
Dec 2, 2017, 4:45:04 PM12/2/17
to ceylon-users
Hi All! I continue my investigations on Ceylon's metamodel and have improved the DataContainer class. I have added method copy() which allows make copy of instance with change of attributes values in this way:
value id1Copy = id1.copy(`Identifier.name`->"newName");

If you passed incompatible type with attribute method will returns 'null'.

I tried to be very 'ceylonic' :) but guru might say where in code is Java aсcent is present. Please go ahead and post your comments.
shared abstract class DataContainer<Subclass>() {

variable Integer? _hash = null;
    variable Map<String, ValueDeclaration>? _members = null;


shared actual Boolean equals(Object that) {
        if (!is DataContainer<Subclass> that) {
return false;
}
for (i in members) {
if (!isAnythingEqual(i.item.memberGet(this), i.item.memberGet(that))) { return false; }
}
return true;

}

hash => _hash else (_hash = calculateHash());

    "Returns copy of instance with modified attribute(s) if there are changes in provided attributes and returns the same instance otherwise."
shared Subclass? copy(Entry<Attribute<>, Anything>* attributes) {
try {
value changedValues = HashMap<String, Anything>();
for (attr -> v in attributes) {
if (exists member = members[attr.declaration.name], member.openType == attr.declaration.openType) {
if (!isAnythingEqual(v, member.memberGet(this))) {
changedValues[attr.declaration.name] = v;
}
} else { return null; }
}
if (changedValues.empty) {
return if (is Subclass thisInstance = this) then thisInstance else null;
}
value constructor = classDeclaration(this).defaultConstructor;
if (!exists constructor) {
return null;
}
value args = ArrayList<Anything>();
for (p in constructor.parameterDeclarations) {
if (exists member = members[p.name], member.openType == p.openType) {
args.add(if (changedValues.defines(p.name)) then changedValues[p.name] else member.memberGet(this));
} else { return null; }
}
return if (is Subclass newInstance = constructor.invoke([], *args)) then newInstance else null;
} catch (Exception e) {
return null;
}
}

Map<String, ValueDeclaration> members => _members else (_members = HashMap {

for (i in classDeclaration(this).memberDeclarations<ValueDeclaration>())
            if (!i.variable, i.name != "string", i.name != "hash") i.name -> i
        });

Integer calculateHash() {
variable Integer result = 0;
for (i in members) {
            if (exists member = i.item.memberGet(this)) {

result = result.xor(member.hash);
}
}
return result;
}

    Boolean isAnythingEqual(Anything value1, Anything value2)
=> if (exists value1, exists value2) then value1 == value2 else value1 exists == value2 exists;
}

class Identifier(shared Integer? id, shared String? name) extends DataContainer<Identifier>() {}


Alexander Kornilov

unread,
Dec 2, 2017, 5:08:43 PM12/2/17
to ceylon-users
To be on same page:
#1 Question/proposal about function references: still actual.
#2 Question/proposal about visibility level ('protected'): still actual.
#3 Solved.  Paco, thank you again!
#4a Questions about generics: 'Element' == 'String' partially solved, still actual about ability using this in switch/case.
#4b Define special implementation according generic type: still actual.
#5 The ticket https://github.com/ceylon/ceylon-ide-eclipse/issues/1891 was created.
#6 I think solution with services providers/consumers is good if findServiceProviders() would be search in specified paths. Maybe I'll prepare pull request which fix this. But still actual information about another ways of solve.
#7 Solved. Thanks all for detailed explanations.
#8 Question about JNI. Maybe jni void doNativeCall(); will work, but I need to check. Go ahead if you have more information.

Alexander Kornilov

unread,
Dec 3, 2017, 7:55:12 AM12/3/17
to ceylon-users
I have clean my code :) sorry for spam but I pusblish it here:
shared abstract class DataContainer<Subclass>() {
variable Integer? _hash = null;
    variable Map<String, ValueDeclaration>? _members = null;


shared actual Boolean equals(Object that) {
        if (!is DataContainer<Subclass> that) {
return false;
}
for (i in members) {
if (!isAnythingEqual(i.item.memberGet(this), i.item.memberGet(that))) { return false; }
}
return true;
}

hash => _hash else (_hash = calculateHash());

"Returns copy of instance with modified attribute(s) if there are changes in provided attributes and returns the same instance otherwise."
shared Subclass? copy(Entry<Attribute<>, Anything>* attributes) {
try {
            value changedValues = HashMap {
for (a in attributes) if (isAttributeChanged(a)) a.key.declaration.name->a.item
};

if (changedValues.empty) {
return if (is Subclass thisInstance = this) then thisInstance else null;
}
            return if (exists constructor = classDeclaration(this).defaultConstructor, is Subclass newInstance = constructor.invoke([], for (p in constructor.parameterDeclarations) getArg(p, changedValues))) then newInstance else null;

} catch (Exception e) {
return null;
}
}

Map<String, ValueDeclaration> members => _members else (_members = HashMap {
for (i in classDeclaration(this).memberDeclarations<ValueDeclaration>())
if (!i.variable, i.name != "string", i.name != "hash") i.name -> i
});

Integer calculateHash() {
variable Integer result = 0;
for (i in members) {
if (exists member = i.item.memberGet(this)) {
result = result.xor(member.hash);
}
}
return result;
}

    Boolean isAttributeChanged(Entry<Attribute<>, Anything> attribute) {
value member = members[attribute.key.declaration.name];
assert(exists member, member.openType == attribute.key.declaration.openType);
return !isAnythingEqual(attribute.item, member.memberGet(this));
}

Anything getArg(FunctionOrValueDeclaration param, Map<String, Anything> attributes) {
value member = members[param.name];
assert(exists member, member.openType == param.openType);
return if (attributes.defines(param.name)) then attributes[param.name] else member.memberGet(this);

}

Boolean isAnythingEqual(Anything value1, Anything value2)
=> if (exists value1, exists value2) then value1 == value2 else value1 exists == value2 exists;
}

class Identifier(shared Integer? id, shared String? name) extends DataContainer<Identifier>() {}

And yet another question :)

9) Can I disable generation of sources package for module (just .car with comipled code)?

Alexander Kornilov

unread,
Dec 5, 2017, 3:36:29 AM12/5/17
to ceylon-users
6) I continue investigation Ceylon module structure and have tried to solve this using custom ModuleLoader:
shared {Service*} loadServices<Service>(ClassOrInterface<Service> service, {String+} paths, String? filter = null) {
value manager = CeylonUtils.repoManager()
.noSystemRepo(true)
.noDefaultRepos(true)
.noCacheRepo(true)
.noOutRepo(true)
.offline(true)
.extraUserRepos(JavaList(JavaStringList(ArrayList<String> { *paths })))
.buildManager();

value loader = CeylonModuleLoader(manager, false);
value searchResult = manager.searchModules(ModuleQuery(filter else ".",ModuleQuery.Type.ceylonCode));
for (name in searchResult.moduleNames) {
try {
loader.loadModuleSynchronous(name.string, searchResult.getResult(name.string).lastVersion.version);
} catch (Exception e) {
//print(e.message);
}
}
for (m in modules.list) {
if (JString(m.name) in searchResult.moduleNames) {
print("Name: ``m.name``, services: ``m.findServiceProviders(service)``");
}
}
return `module`.findServiceProviders(service);
}

Actually, I could found modules in provided directories and load them (I saw them in modules.list).
But this one doesn't see any services:
return `module`.findServiceProviders(service);

So I have tried to search direct in module object:
for (m in modules.list) {
if (JString(m.name) in searchResult.moduleNames) {
print("Name: ``m.name``, services: ``m.findServiceProviders(service)``");
}
}

But have got exception: "Exception in thread "main" java.util.ServiceConfigurationError: com.loggersoft.ceylon.plugin.IPlugin: Provider com.loggersoft.ceylon.plugin1.TipPlugin not a subtype".

Guess the CeylonModuleLoader should be singleton in system or my custom loader should be registered somewhere... Or callerModuleLoader should be parnet of mine loader. I really don't know now.

"ceylonic" guys, where are you? I thought Ceylon community is more alive...
Probably my questions is easy for language designers, but them don't want to give me answers, or too busy to talking with languge's users.

Stephane Epardaud

unread,
Dec 5, 2017, 4:38:30 AM12/5/17
to ceylon...@googlegroups.com
We've never used this outside of the runtime and JBoss Modules. I don't think it will work because it's not meant for composition. You should take a look at how `ceylon.test` in the SDK loads modules dynamically, or the JBossModuleLoader class. We've never standardized dynamic module loading, sorry.

--
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+unsubscribe@googlegroups.com.
To post to this group, send email to ceylon...@googlegroups.com.
Visit this group at https://groups.google.com/group/ceylon-users.

For more options, visit https://groups.google.com/d/optout.



--
Stéphane Épardaud

Stephane Epardaud

unread,
Dec 5, 2017, 4:59:48 AM12/5/17
to ceylon...@googlegroups.com
On 3 December 2017 at 13:55, Alexander Kornilov <akorni...@gmail.com> wrote:

9) Can I disable generation of sources package for module (just .car with comipled code)?

I don't think so. 

Alexander Kornilov

unread,
Dec 5, 2017, 5:03:14 AM12/5/17
to ceylon-users
вторник, 5 декабря 2017 г., 12:38:30 UTC+3 пользователь Stéphane Épardaud написал:
We've never used this outside of the runtime and JBoss Modules. I don't think it will work because it's not meant for composition. You should take a look at how `ceylon.test` in the SDK loads modules dynamically, or the JBossModuleLoader class. We've never standardized dynamic module loading, sorry.

Hi! Thank you for answer, because I start to think that I just talk to myself :)

I have prepared some workable solution, but it can only dynamically load modules from "~/.ceylon/repo" (paths customizations is not possible):
shared {Service*} loadServices<Service>(ClassOrInterface<Service> service, String? filter = null) {
value loader = ceylonModuleLoader;
if (!is CeylonModuleLoader loader) {
return {};
}
value searchResult = CeylonUtils.repoManager()
.noSystemRepo(true)
.noDefaultRepos(false)
.noCacheRepo(true)
.noOutRepo(true)
.offline(true)
.buildManager()
.searchModules(ModuleQuery(filter else ".",ModuleQuery.Type.ceylonCode));

if (searchResult.moduleNames.empty) {
return {};
    }
for (name in searchResult.moduleNames) {
try {
            value details = searchResult.getResult(name.string);
if (!details.remote) {
loader.loadModuleSynchronous(name.string, details.lastVersion.version);
}
} catch (Exception e) {
}
}
return { for (m in modules.list)
if (Types.nativeString(m.name) in searchResult.moduleNames)
for (s in m.findServiceProviders(service)) s };
}

As I understand from Ceylon sources the class CeylonModuleLoader is custom implementation of JBossModuleLoader, correct?

Could you please point me where in ceylon.test code the module loaded dynamically?

Stephane Epardaud

unread,
Dec 5, 2017, 5:23:30 AM12/5/17
to ceylon...@googlegroups.com
On 5 December 2017 at 11:03, Alexander Kornilov <akorni...@gmail.com> wrote:

I have prepared some workable solution, but it can only dynamically load modules from "~/.ceylon/repo" (paths customizations is not possible):

I don't see why not, but the loader you're using is using the settings passed at startup. You can add your lookup repos at startup. Otherwise if you're using the current loader, then yes indeed you can't add paths after startup.
 
As I understand from Ceylon sources the class CeylonModuleLoader is custom implementation of JBossModuleLoader, correct?

No, I don't think so. They're two separate systems.
 
Could you please point me where in ceylon.test code the module loaded dynamically?

Alexander Kornilov

unread,
Dec 5, 2017, 10:24:20 AM12/5/17
to ceylon-users
вторник, 5 декабря 2017 г., 13:23:30 UTC+3 пользователь Stéphane Épardaud написал:

I don't see why not, but the loader you're using is using the settings passed at startup. You can add your lookup repos at startup. Otherwise if you're using the current loader, then yes indeed you can't add paths after startup.
You right, I am using existing module loader because ceylonModuleLoader from my code is:
import org.jboss.modules {
JBossModule = Module {
ceylonModuleLoader = callerModuleLoader
}
}
I cann't add new path for seach to existing CeylonModuleLoader.
 
As I understand from Ceylon sources the class CeylonModuleLoader is custom implementation of JBossModuleLoader, correct?

No, I don't think so. They're two separate systems.
Really? But I see that CeylonModuleLoader is subclass of JBoss ModuleLoader:
/**
* Ceylon JBoss Module loader.
* It understands Ceylon repository notion.
*
* @author <a href="mailto:">Ales Justin</a>
*/
public class CeylonModuleLoader extends ModuleLoader

Any way, what is your idea? How JBoss module loader might help me? Can it direct load .car file?
 

Could you please point me where in ceylon.test code the module loaded dynamically?


OK. Thank you. I already use this trick in my code. But why when I created  new instance of CeylonModuleLoader it doesn't work as expceted?

Stéphane, what you think about my other questions? As I understand from FAQ you working on Ceylon compiler.
1) About function/method's refernces equality.
2) About "protected"/subclass visibility?

Stephane Epardaud

unread,
Dec 5, 2017, 10:40:39 AM12/5/17
to ceylon...@googlegroups.com
On 5 December 2017 at 16:24, Alexander Kornilov <akorni...@gmail.com> wrote:
Really? But I see that CeylonModuleLoader is subclass of JBoss ModuleLoader:

JBoss Module's ModuleLoader class is another API to the JBossModuleLoader I mentionned ;)
 
Any way, what is your idea? How JBoss module loader might help me? Can it direct load .car file?

Yes
 
 OK. Thank you. I already use this trick in my code. But why when I created  new instance of CeylonModuleLoader it doesn't work as expceted?

Indeed you can't create a new one.
 
Stéphane, what you think about my other questions? As I understand from FAQ you working on Ceylon compiler.
1) About function/method's refernces equality.

Function equality can only work in certain limited cases, and fails when capturing, so we disabled equality for every function, to make sure people never depend on implementation-specific equality.
 
2) About "protected"/subclass visibility?


Alexander Kornilov

unread,
Dec 5, 2017, 11:17:09 AM12/5/17
to ceylon-users
вторник, 5 декабря 2017 г., 18:40:39 UTC+3 пользователь Stéphane Épardaud написал:
 
Any way, what is your idea? How JBoss module loader might help me? Can it direct load .car file?

Yes
Will be loaded module by the JBossModuleLoader work correctly with other Ceylon classes? Maybe some registration in metamodel required?
Are there some examples?

 OK. Thank you. I already use this trick in my code. But why when I created  new instance of CeylonModuleLoader it doesn't work as expceted?

Indeed you can't create a new one.
Why? ;)
 
2) About "protected"/subclass visibility?


Yeh, I already read it... But what about my example:

My example is very simple variation of the "Template method" design pattern in Java:
    public abstract class TemplateMethodBaseClass {

        // Here is the public interface of class
        public void PerfromSomthing(String data) {
            // Here is common logic which is applicable for most subclasses

            // The evaluation of dataVal is specific for each subclasses
            final int dataVal = EvaluateSomeValue(data);
        }

        // Here is protected part which should be visible only in subclasses

        protected abstract int EvaluateSomeValue(String data);
    }

    public class TemplateMethodClient extends TemplateMethodBaseClass {

        @Override
        protected int EvaluateSomeValue(String data) {

            // Perform this class specific logic here
            return 0;
        }
    }

Naturally I don't want external classes which use TemplateMethodClient can access to EvaluateSomeValue().

How can I implement this pattern in Ceylon?

Is 'template method' design pattern "superstition"? :)

And more questions:
10) In module descriptor I have to set particular version of module:
import ceylon.collection "1.3.3";
But if I don't want has dependency on particular version? I just want have latest version of module. How can I do this?

11) Now I am investigating possibilities of Ceylon and likes it more and more. But I want make decision about main language in next project.
What is the Ceylon status? Unfortunately I see some lack of information about Ceylon, community is not very big and active. What is it development perspectives? Will be it grow? Or goes to freez like Fantom?

Colin Bartolome

unread,
Dec 6, 2017, 11:28:30 AM12/6/17
to ceylon-users


On Tuesday, December 5, 2017 at 8:17:09 AM UTC-8, Alexander Kornilov wrote:
Naturally I don't want external classes which use TemplateMethodClient can access to EvaluateSomeValue().

How can I implement this pattern in Ceylon?

Is 'template method' design pattern "superstition"? :)

I think the designers' view was that, if you're importing that base class from a module you didn't write and extending it with code you did write, you're going to need to know about the EvaluateSomeData method anyway, so it's pretty much part of the public interface already. If you're not planning to extend the base class anywhere outside the package or module it's in, this could be a place where the restricted annotation is useful; by annotating the method shared restricted(`module) or shared restricted(`package`), you can limit its visibility.
 
And more questions:
10) In module descriptor I have to set particular version of module:
import ceylon.collection "1.3.3";
But if I don't want has dependency on particular version? I just want have latest version of module. How can I do this?

You can't. This was designed this way on purpose, so somebody publishing a new version of their module couldn't suddenly break your code. (Unless they forget to change the version number, of course!)

John Vasileff

unread,
Dec 6, 2017, 11:48:16 AM12/6/17
to ceylon...@googlegroups.com

On Dec 5, 2017, at 11:17 AM, Alexander Kornilov <akorni...@gmail.com> wrote:

2) About "protected"/subclass visibility?


Yeh, I already read it... But what about my example:

Although there are some valid uses for protected, it’s worth noting that it has become unpopular among language designers.

Many argued for adding protected access in Swift, but the designers resisted. See https://developer.apple.com/swift/blog/?id=11

Even the current Java architects wish protected could be removed. See https://youtu.be/FdkPHShh628?t=1522 with the quote “I hate protected with a passion”.

John

Tako Schotanus

unread,
Dec 6, 2017, 12:50:02 PM12/6/17
to ceylon-users
Not even then. You can't publish the same module+version twice, nor can you "overwrite" a module. Once published a module is immutable. You want to publish a new one you _must_ give it a different version.

Alexander Kornilov

unread,
Dec 6, 2017, 1:15:43 PM12/6/17
to ceylon-users


среда, 6 декабря 2017 г., 19:28:30 UTC+3 пользователь Colin Bartolome написал:

I think the designers' view was that, if you're importing that base class from a module you didn't write and extending it with code you did write, you're going to need to know about the EvaluateSomeData method anyway, so it's pretty much part of the public interface already.


OK. This is reasonable. I can agree this you: EvaluateSomeData() should be part of public interface of base class.
And also It would be good if EvaluateSomeData() not be visible as part of public interface of subclass.

I have tried this:

The class from first module:
shared abstract class TemplateMethod() {

shared String performSomthing(String input) {
return evaluatePrefix() + input;
}

shared formal String evaluatePrefix();
}

The "client" from another module:
shared class TemplateClient() extends TemplateMethod() {

shared actual restricted String evaluatePrefix() {
return "Client prefix";
}
}

Great! Ceylon allow to narrow visibility of the method evaluatePrefix() within package. So "clients" of TemplateClient() will see performSomthing(), but not evaluatePrefix(). It's fine form me.

Alexander Kornilov

unread,
Dec 7, 2017, 5:07:38 AM12/7/17
to ceylon-users
No, it was strange issue of Ceylon compiler, when I clean and built it again I have got error:
"error: Ceylon backend error: evaluatePrefix() in TemplateClient cannot override evaluatePrefix() in TemplateMethod

    shared actual restricted String evaluatePrefix() {
                  ^
  attempting to assign weaker access privileges; was public"

Sad... I can't hide the implementation details of auxilary class from user.

I think designers pandering to the latest popular fads which sounds good only in teory. In practice 'protected' is very helpful tool and I didn't hear any compelling argument about real harm of 'protection' visibility level. Just emotions and puting labels like "superstition".

As I rembmer first versions of language hadn't have 'static' attributes and methods - I think it was fashionable. :) But reality dictate its terms and the 'static' paradigm was returned to the language. In my opinion elimination of 'protected' is mistake too and I hope it will be fixed in next versions.

среда, 6 декабря 2017 г., 21:15:43 UTC+3 пользователь Alexander Kornilov написал:
Message has been deleted

Alexander Kornilov

unread,
Dec 7, 2017, 7:30:22 AM12/7/17
to ceylon-users
It would be good to have one and more powerful annotaion instead of two 'shared' and 'restricted': :)

shared abstract class VisibilityLevel() of outside | limited | subclass {}

"[Default] The element is visible outside block"
shared object outside extends VisibilityLevel() {}

"The visibility of element is restricted by package or module"
shared object limited extends VisibilityLevel() {}

"The element is vivible only in subclasses"
shared object subclass extends VisibilityLevel() {}

shared final sealed annotation class PublicAnnotation(VisibilityLevel level = outside, Module* modules)
satisfies OptionalAnnotation<PublicAnnotation,
FunctionOrValueDeclaration
| ClassOrInterfaceDeclaration
| ConstructorDeclaration
| Package> {}

shared annotation PublicAnnotation public(VisibilityLevel level = outside, Module* modules)
=> PublicAnnotation(level, *modules);

void privateFunction() {

}

public void publicFunction() {

}

public(subclass) void protectedFunction() {

}

public(limited) void packageRestrictedFunction() {

}

public(limited, `module`) void moduleRestrictedFunction() {

}

Alexander Kornilov

unread,
Dec 8, 2017, 4:25:27 AM12/8/17
to ceylon-users
12) How can I narrow visibility level of refined method? e.g. from shared to resticted? Or why not?

Alexander Kornilov

unread,
Dec 8, 2017, 4:51:15 AM12/8/17
to ceylon-users
13)  Have you thought about 'const' method/reference paradigm in Ceylon?
Usually Ceylon has pairs of interfaces in Collection:
e.g. Map and MutableMap.
But I think this approach is not very protective:
shared class SomeService() {
Map<Integer, String> clients = HashMap();

shared Map<Integer, String> getClients()
=> clients;
}

shared void someServicesUser(SomeService service) {
value clients = service.getClients();

//clients[125] = "Hi Guys!";
// This doesn't work but I can do:
if (is HashMap<Integer, String> clients) {

//Yeh!! I have got hack them!!!
clients[125] = "Hi Guys!";
}
}


But if we have 'const' mechanism complier could protect us from shooting yourself in the foot:
shared interface Map<out Key=Object, out Item=Anything>
satisfies Collection<Key->Item> &
Correspondence<Object,Item>
given Key satisfies Object {

shared const actual formal Item? get(Object key);

All methods which doesn't change object should be marked by 'const' annotation - code in those methods might be validated by compiler on compilation stage.

"The clients is 'const' reference"
shared void someMapUser(const HashMap<Integer, String> clients) {
clients[125] = "Hi Guys!"; // Doesn't work because 'put' is not marked as 'const'
}

In my opinion the 'const' approach it more usable and protective than pairs of interfaces.

What you think? :)

Colin Bartolome

unread,
Dec 8, 2017, 11:51:13 AM12/8/17
to ceylon-users
I could be mistaken, but I think Map and MutableMap being separate interfaces was to be able to use covariance and contravariance to make the types more flexible, not to imply that values satisfying Map were guaranteed to be immutable:

https://ceylon-lang.org/documentation/1.3/tour/generics/#covariance_and_contravariance

In your example, by the way, if you really did want to let other people see the map of clients, you could just add the "shared" annotation to the "clients" attribute, instead of making a separate "getClients()" function.

Alexander Kornilov

unread,
Dec 8, 2017, 5:18:55 PM12/8/17
to ceylon-users
I know :) just stupid example.

Main idea is: some class has the private object (map in my exmaple) which on the one hand should be shared with class's users in read-only mode and on the other hand might be modified within class.
I see possible solutions:
1. To do cast the object to immutable version of interface before share with user. Overhead is zero in this case, but object could be corrupted by intruder.
2. To make a copy of the object. Huge overhead for big objects.
3. To use some wrapper class like 'unmodifiableMap'. Overhead is less then case (2) but extra object-wrapper.
4. To use 'const' method/reference paradigm. There is zero overhead and read/write access policy could be fully performed on compilation stage.

I prefer (4) case. Because user of shared object by 'const' refrence can access only methdos marked as 'const' by author of class.

пятница, 8 декабря 2017 г., 19:51:13 UTC+3 пользователь Colin Bartolome написал:

Alexander Kornilov

unread,
Dec 9, 2017, 6:09:35 AM12/9/17
to ceylon-users
I am studying Kotlin in background at moment and notice there are many same things in the language design between Ceylon and Kotlin.
At least:
1) Null safety. Syntax is very similar. Of course in Ceylon it's based on types union and it more consistent.
2) Smart Casts.
3) Ranges.
4) Named arguments.
5) Intuitive equals.
6) Properties.
7) Java interop.
8) Operator overloading.
9) switch.

Both languages appeared in 2011...
I am interesting who was the first in ideas?

Ceylon is more beautiful,  well-designed and consistent for me (but it doesn't have 'protected' visibility yet! :) ). It supported by RedHat, but Kotlin which supported only by JetBrains (it is not very big company) look likes more popular... maybe marketing is reason.
Reply all
Reply to author
Forward
0 new messages