Ceylon v.s. Kotlin
Published: 2020-05-24 15:26:43

---
title: Ceylon v.s. Kotlin
published: true
date: 
tags: 
canonical_url: https://mmap.page/dive-into/ceylon-kotlin/
---

## void function

Kotlin uses `Unit` for functions returning nothing, like many functional languages,while Ceylon uses `void` keyword.void functions in Ceylon actually returns `Anything`.This allows to use other non-void functions when void functions are expected,and other non-void functions to accept void function as parameter.

Ceylon’s `Anything` approach is flexible,but it may not fit in a more disciplined coding style.If non-void functions are used when void functions are expected,then we are using side effects with non-void functions.But a non-void function should avoid exposing side effects.

We can declare our own `Unit` class in Ceylon:

```
abstract class Unit() of unit {}
object unit extends Unit() {}

Unit f() {
    print("hi");
    return unit;
}

shared void run() {
    f();
}

```

Like assignment returning a value, Ceylon’s behavior may also be influenced by C.In C, any function can be converted to `void` via `(void) f`, discarding its return value.

## Variadic arguments

Kotlin uses `vararg` keyword to mark variadic argument, while Ceylon just typesthem with `T*` or `T+` (at least have an argument). This is an example ofCeylon’s regularity of type system. Kotlin uses `Array<T>` for `vararg p: T`underhood but special array types for basic types, e.g. `IntArray` for `vararg
p: Int`. However, `p: Array<T>` and `vararg p: T` behaves differently. In otherwords, given a function `Array<T> -> Unit`, we do not know how to invoke itjust from its signature.

## Function reference

Kotlin’s function reference syntax is strange:

```
fun gf(x: Double, f: (Double) -> Double): Double {
    return f(x)
}
gf(2.0, ::findFixPoint)

```

Without `::` it will not work.

Languages using special function reference syntax usually saves `f` for `f()`.But that is not allowed in Kotlin, either.Instead, Kotlin saves `f` for a variable/property `f` with the same name!

```
val i = 2
fun i(): Int { return 2 };
print(i)
print(i())

```

Still Kotlin compiler cannot resolve the function referenceif there is a property which has the same name:

```
fun h(f: () -> Int) {
    print(f())
}
h(::i) // error: overload resolution ambiguity

```

Also, Kotlin only allows reference to `Foo::bar`[not `foo::bar`][bound-callable-references].

For anonymous function, there is no ambiguity.Thus the function reference is normal.

```
val f = fun(): Int { return 1 }
h(i)

```

Ceylon’s function reference is normal.

Ceylon does not support overloading. And function reference will not work forJava overloading functions.

## Inline functions

Kotlin supports inline functions.

```
inline fun foo(inlined: () -> Unit, noinline notInlined: () -> Unit) {}
inline fun f(crossinline body: () -> Unit) {
    val f = object: Runnable {
        // body is called in another context `object: Runnable`,
        // thus we need the `crossinline` keyword.
        override fun run() = body()
    }
    // ...
}

```

Kotlin’s inline function supports reified generics:

```
inline fun <reified T> TreeNode.findParentOfType(): T? {
    var p = parent
    while (p != null && p !is T) {
        p = p?.parent
    }
    return p as T
}

```

Ceylon does not support inline functions, and all generics are reified, exceptinteroperation with Java code.

IMO, languages should make function calls cheap, instead of bring in confusinginline features.

## Object

Kotlin’s object looks like class, and provides an `invoke` method to mimicfunction call with object initialization.

```
object SingletonExample {
   fun singletonMethod() {
        return "We cannot initialize a singleton."
   }
   operator fun invoke() {
        return "`invoke` will be called when we write `SingletonExample()`."
   }
}

```

I dislike this feature.Now every time I see `CapsName(something)`I am not sure it returns an instance of `CapsName`.It may return anything!

Ceylon’s object does not have a `invoke` method.

```
shared object consoleWriter satisfies Writer {
    formatter = StringFormatter();
    write(String string) => process.write(string);
}

```

The downside of object (anonymous class) in Ceylon is if we ever need to writecode that refers to the concrete type consoleWriter, we must use the very uglysyntax `\IconsoleWriter` as the type name.A toplevel object in Ceylon is a singleton.

Object (anonymous class) in Ceylon is actually a value constructor:

```
// object thing {}
class \Ithing {
    shared new thing {}
}
\Ithing thing => \Ithing.thing;

```

Kotlin also has object expression.

```
val adHoc = object {
    var x: Int = 0
    var y: Int = 0
}
open class A(x: Int) {
    public open val y: Int = x
}

interface B

val ab = object : A(1), B {
    override val y = 15
}

```

Object expression in Ceylon:

```
return object extends Foo() satisfies Bar {
    // ...
};

```

An object expression is basically a shorthand for a local object declaration.

```
object foo extends Foo() satisfies Bar {
    // ...
};
return foo;

```

Kotlin uses component object to mimic Java static methods:

```
class ExampleClass(val x: Int) {
    companion object {
        // still a real object at runtime
        fun aMethodLooksLikeStaticButNot() = println("not static")
        @JvmStatic fun aMethodToBeCompiledToStaticMethodOnJVM() {
            println("compiled to static methods on JVM")
        }
    }
}
ExampleClass.aMethodLooksLikeStaticButNot()
ExampleClass.aMethodToBeCompiledToStaticMethodOnJVM()

```

Ceylon dose not support this.

Both Kotlin and Ceylon support top level function declaration,something like package level static method.

Ceylon 1.3.1 introduces static member.

Also, Kotlin supports extension method.Extension method on Kotlin is resolved statically,only providing a way to call with `Class.method` syntax,not actually modifying the extended class.

Similarly to functions, Kotlin supports extension properties. Again, Ceylondoes not support this.

## Final class

Classes in Kotlin are `final` by default.Also, Kotlin requires explicit `open` modifier for overridable members.And a member marked override is itself open.Also, properties declared in Interface are open.

```
open class OpenClass(x: Int) {
    open fun overridableMember() {}
}
open class ChildOfOpenClass(x: Int) : OpenClass(x) {
    override fun overridableMember() {}
}

```

Ceylon classes are by default open, unless annotated with `final`.However, Ceylon also requires explicit annotation for overridable members:

- `formal` for declaration without implementation,
- `default` for `open` in Kotlin,
- `actual` for `override` in Kotlin (no `default` unless denoted).

Also, attributes in interface are not open by default in Kotlin.You need to explicitly annotate it as `formal`.

## `super` ambiguity

Kotlin uses `super<A>.f()`.

```
open class A() {
    open fun f() { print("A") }
}

interface B {
    // Interface members are open by default.
    fun f() { print("B") } // Allowing method implementations, like Java 8.
}

class ChildOfAB() : A(), B {
    // The compiler requires `f()` to be overridden.
    // So you do not need to remember rules of priorities
    // and check super class definitions for possible conflicts.
    override fun f() {
        super<A>.f()
        super<B>.f()
    }
}

```

Ceylon uses `(super of A).f()`.

```
class SelfReference() {
    // Ceylon has keywords `this` and `super`.
    // To deal with ambiguity of what `super` refers to, use the widening operator.
    class Impl() extends Class() satisfies Interface {
    // Suppose Impl inherits two different implementations of `ambiguous`
    // from Class and Interface.
        (super of Interface).ambiguous()
    }
}

```

## getter and setter

Kotlin uses `get()` and `set()` functions with optional backing field:

```
class GettersAndSetters(val x: Int) {
    var isEmpty: Boolean = null
        get() = this.x == 0
        // By convention, the name of the setter parameter is value,
        // but you can choose a different name if you prefer.
        set(value) {
            if (value == null) {
                // Kotlin provides an automatic backing `field`.
                field = false
            } else {
                // The field identifier can only be used in the accessors of the property.
                field = value
            }
        }
}

```

Ceylon uses anonymous setter and `assign` for getter,without backing field.

```
class GetterSetterExample() {
    variable String? rank = null; // Ceylon never auto initializes to null.
    variable String? comment = null;
    shared String review => "``rank``: ``comment``";
    assign review {
        value tokens = review.split(":").iterator();
        if (is String first = tokens.next()) { // narrowing down types
            rank = first;
        }
        if (is String second = tokens.next()) {
            comment = second;
        }
    }
}

```

In Kotlin, immutable variable `val` does not allow setter.Ceylon’s getter and setter are more flexible.In the above example, we have a setter for immutable `review`.The setter is valid, since it actually changes value of mutable `rank` and `comment`.

## const

Kotlin has compile time `const`:

```
const val VERSION: String = "alpha"

```

`const` only allows `String` or a primitive type.

Ceylon does not have this.

I have no idea why this need to be explicitly marked in Kotlin.The compiler cannot automatically optimize this?

## late init

Kotlin has `lateinit` and Ceylon has `late` annotation.But they are two different things.

Kotlin only allows non-nullable, not-primitive types to use `lateinit`. Kotlinuses the `null` value to mark that a `lateinit` property has not beeninitialized and to throw the appropriate exception when the property isaccessed. But primitive Java types can’t have a ‘null’ value. In other words,the property must be not nullable in Kotlin level, but nullable in Java level.

```
public class MyTest {
    lateinit var subject: TestSubject

    @SetUp fun setup() {
        subject = TestSubject()
    }

    @Test fun test() {
        subject.method()
    }
}

```

Ceylon uses `late` to suppress definite initialization checks.In Ceylon, all declaration is done after initialization,which may cause problems on circular reference.

```
class Child(parent) {
    shared late Parent parent;
}

class Parent() {
    // This won't work,
    // because `this` refers to an instance of Parent in its own initializer section,
    // where Parent has not been initialized yet.
    // shared Child child = Child(this);
    shared Child child = Child();
    child.parent = this; // ok, since parent is late
}

```

## declaration-site variance

Both Kotlin and Ceylon uses declaration-site variance `<out Bar>`.

Ceylon also support use-site variance for interoperation with Java code.So does Kotlin, with a slightly different syntax `<out Bar!>!`.

## Generics constrains

Kotlin uses common `T : Upper` syntax:

```
fun <T : Comparable<T>> sort(list: List<T>) {}

```

More than one upper bounds need to be specify in a where clause:

```
fun <T> cloneWhenGretter(list: List<T>, threshold: T): List<T>
        where T : Cloneable, T : Comparable {
    return list.filter{it > threshold}.map{it.clone()}
}

```

Ceylon has a more consistent syntax,and uses intersection type for more than one upper bounds.

```
Value genericFunction<Value>(Value num, Value denom)
        given Value satisfies Comparable<Value> & Summable<Value>
        => if num > denom then num else num + denom;

```

Both Kotlin and Ceylon does not support raw type.Kotlin has a similar star projection syntax:

Given `interface Function<in S, out T>`, `Function<*, *>` is a shortcut for`Function<in Nothing, out Any?>`

Ceylon just supports explicit `Function<in Nothing, out Anything>`.

Also note that Ceylon uses `Anything` instead of `Any?` in Kotlin.`Any` in Kotlin is not truly `Any` because it does not hold `null`.Ceylon’s `Anything` is an enumerated type:

```
shared abstract class Anything()
        of Object | Null {}

```

`of Object | Null` can be used in generics as enumerated constrain.

## Anonymous function

Kotlin has both anonymous function and lambda:

```
// anonymous function
fun(): Unit { println(numbers.size) }
// lambda
{ n: Int -> n <= 0 }
// one parameter lambda can be abbreviated,
// also parameter type can be omitted if it can be inferred by compiler
{ it <= 0 }

```

The difference between lambda and anonymous function is:

- lambda cannot specify a return type
- control flow `return` in lambda returns the outer function

Ceylon just have anonymous function.

```
// Same as lambda in Kotlin, an anonymous function cannot specify return type.
(Integer n) => n <= 0
// Except `void`.
void (Integer n, Integer m) => print(m + n)
// Mark it with `function`
function (Integer n) => n <= 0
// Like `function`, `void` can be omitted.
() => print("Hi!")
// Same as above, with block
() { print("Hi!"); }
// Similar to Kotlin's anonymus function
function () { print("Hi!"); }

```

## case

Kotlin uses `when` for both `case` and `cond` in Scheme.

```
when {
    i == 0 -> false
    else -> true
}
when (i) {
    0 -> false
    else -> true
}

```

Ceylon just uses `switch` for `case`:

```
  void printID(String|Integer id) {
      switch (id)
      case (is String) {
          println(id);
      }
      case (is Integer) {
          switch (id)
          case (0) {
            println("Error");
          }
          else {
            println("id number: ``id``");
          }
      }
      // No `else` clause since all cases of a union type is exhausted.
}

```

Ceylon uses `switch` as statements,while Kotlin uses `when` as both statements and expressions.

`case` must be both disjoint and exhausted in Ceylon.

```
shared abstract class Comparison(shared actual String string)
        of larger | smaller | equal {}
switch (x<=>y) // `<=>` produces an instance of `Comparison`.
case (equal) {
    print("same same");
}
case (smaller) {
    print("x smaller");
}
case (larger) {
    print("y smaller");
}

```

Cases must be exhausted in Kotlin only in `when` expression.

In Ceylon, `switch` can declare a scoped local variable:

```
switch (name = process.arguments.first)
case (null) {
    print("Hello world!");
}
else {
    print("Hello ``name``!");
}

```

This is frequently used in Ceylon,because Ceylon’s case condition does not allow arbitrary expressions.Only conditions like `exists`, `is Type`, `nonempty`and literal values of basic types are allowed.

## Catch checked exceptions from Java

Kotlin does not have checked exceptions. So, normally, the Java signatures ofKotlin functions do not declare exceptions thrown. If you want to call them intry catch clause from Java, you need to annotate functions with`@Throws(IOException::class)`.

The root of the exception hierarchy in Ceylon is `ceylon.language::Throwable`,Unlike Java’s `Throwable`, `ceylon.language::Throwable` is sealed and the onlysubclasses available to users are `ceylon.language::Exception` and`ceylon.language::AssertionError`.

The JVM implementation of `ceylon.language::Exception` is a`java.lang.RuntimeException`. On the other hand,`ceylon.language::AssertionError` is a `java.lang.Error` at runtime. This meansthat pure Ceylon code compiled for the JVM can only generate uncheckedexceptions.

Impure Ceylon (that is, Ceylon code which access Java code) may throw anyexception that is thrown by that Java code, including checked exceptions. If aCeylon function may throw checked exceptions, you need to call it in a trycatch clause in Java.

## Reassignment of function parameters

Kotlin does not allow reassignment of function parameters,i.e. function parameters are always implicitly `val`,like function parameters are always implicitly `let` in Swift.

Ceylon disallows this unless the function parameter is annotated as `variable`.This is consistent with variable declaration.