セイロンまたはコトリンに完全に切り替える必要がある理由(パート1)

最近、Kotlinの人気が高まっています。 しかし、よりエキゾチックな言語を選択し、それらに同じ引数を適用しようとするとどうなりますか? 記事はこれに基づいて書かれており、Kotlinのすべての議論を事実上繰り返しています。 主なタスク:Javaに関して、Ceylonが実質的にKotlinと同じであることを示すこと。 しかし、これに加えて、セイロンには次の記事で説明する何か他のものがあります。







Ceylonと呼ばれる新しいプログラミング言語について話し、次のプロジェクトで使用する理由を説明したいと思います。 私はJavaを使用して(10年以上、Java 1.4で始まりJava 8で終わる10年以上)、Javaが好きでした。 それからScalaは私に大きな印象を与えました。その結果、言語としてのJavaが少しだけ好きになり始めました。 しかし、運命はセイロン言語と一緒に私をもたらしました、そして、この一年半で、我々はどこでもセイロンを書いていました。 実際の商業プロジェクトでは、真実は内部にあります。 そして、現時点では、Javaを選択したほうがよい状況を想像していません。Javaを新しいプロジェクトを開始する価値がある言語とは考えていません。







CeylonはRed Hatで開発され、言語の作成者はHibernateなどのフレームワークで知られるGavin Kingです。 Javaの欠点をよく理解している人々によって作成されました。主な目標は、純粋に適用された問題を解決し、コードの読みやすさを可能な限り容易にし、曖昧さや落とし穴を避けることでした。 許容可能なコンパイル時間にも多くの注意が払われました。 現在、言語バージョンは1.3.2であり、バージョン1.2.0がリリースされたときに言語を直接知りました。







CeylonはJavaScriptでコンパイルしますが、主な環境であるJVMに集中します。







そのため、Ceylonに完全に切り替える必要があるいくつかの理由(順序は、対応するKotlin記事の同じ名前のアイテムと同じです):







0#Javaの互換性







Kotlinのように、Scalaのように、Ceylonは100%Java互換です。 文字通り、古いJavaプロジェクトで作業を続けることができますが、すでにCeylonを使用しています。 すべてのJavaフレームワークも利用できます。どのフレームワークを作成する場合でも、Ceylonは頑固なJavaの愛好家に簡単に受け入れられます。 Java Ceylonから問題なくコードを呼び出すことができ、Javaコードも問題なく呼び出されます。







1#使い慣れた構文







Ceylon言語の主な機能の1つは、既存の開発者にとって最も読みやすい構文です。 既存のJava開発者を取り上げる場合、Ceylon構文を理解していれば、彼はわずかな問題を抱えることはありません。 ScalaやKotlinのような言語でさえ、Javaにはあまり似ていません。 以下は、Kotlinの例に似た、かなりの数の言語構成を示すコードです。







class Foo(String a) { String b= "b"; // unmodifiable variable Integer i = 0; // variable means modifiable void hello() { value str = "Hello"; print("``str`` World"); } Integer sum(Integer x, Integer y) { return x + y; } Float maxOf(Float a, Float b) => if (a > b) then a else b }
      
      





したがって、Ceylonで簡単にJavaスタイルで書き続けることができます。







2#文字列補間







これは、言語に組み込まれたJavaのString.format()のよりスマートで読みやすいバージョンのようなものです。







 value x = 4; value y = 7; print("sum of ``x`` and ``y`` is ``x + y``") ; // sum of 4 and 7 is 11
      
      





ここの構文は、Kotlinの構文よりも快適です。Javaと比較したくはありません。







3#型推論







読みやすさが向上すると思われる場合、Ceylonはタイプを出力します。







 value a = "abc"; // type inferred to String value b = 4; // type inferred to Integer Float c = 0.7; // type declared explicitly List<String> d = ArrayList<String>(); // type declared explicitly
      
      





4#スマートキャスト







Ceylonコンパイラーはロジックを監視し、可能な場合は自動的に型変換を実行します。つまり、明示的なキャスト後のinstanceofチェックは不要になります。







 if (is String obj) { print(obj.uppercased) // obj is now known to be a String }
      
      





5#直観的に等しい







==演算子が構造的な等価性をチェックするようになったため、明示的にequals()を呼び出すことはできなくなりました。







 value john1 = Person("John"); //we override equals in Person value john2 = Person("John"); print(john1 == john2); // true (structural equality) print(john1 === john2); // false (referential equality)
      
      





6#デフォルト引数







引数が異なる複数の同一のメソッドを定義する必要がなくなりました。







 void build(String title, Integer width = 800, Integer height = 600) { return Frame(title, width, height); }
      
      





7#名前付き引数







デフォルトの引数と組み合わせて、名前付き引数を使用すると、ビルダーを使用する必要がなくなります。







 build("PacMan", 400, 300) // equivalent build {title = "PacMan"; width = 400; height = 300;} // equivalent build {title = "PacMan"; height = 300;} // equivalent with default width
      
      





8#式スイッチ







分岐演算子は、はるかに読みやすく柔軟なswitchステートメントに置き換えられました。







 switch (obj) case(1) { print("x is 1"); } case(2) { print("x is 2"); } case(3 | 4) { print("x is 3 or 4"); } case(is String) { print ("x is String"); } case([Integer a, Float b, String c]) {print ("x is tuple with Integer ``a``, Float ``b`` and String ``c``");} else { print("x is out of range");}
      
      





スイッチは式として機能し、スイッチの結果を変数に割り当てることもできます。







 Boolean|IllegalStateException res = switch(obj) case(null) false case(is String) true else IllegalStateException();
      
      





これは本格的なパターンマッチングではありませんが、ほとんどの場合、現在の機能で十分です。







Kotlinとは異なり、Ceylonでは、スイッチのすべての条件が互いに素である必要があります。つまり、オーバーラップしないことが必要です。 範囲の一致が必要な場合、または条件が交差する場合は、通常のifを使用する必要があります。







9#プロパティ







カスタムセットを追加して、パブリックフィールドにビヘイビアを取得できます。つまり、クレイジーなゲッターとセッターでコードの入力を停止できます。







 class Frame() { variable Integer width = 800; variable Integer height = 600; Integer pixels => width * height; }
      
      





10#データクラス







残念ながら、この機能はまだ利用できません。 toString()、equals()、hashCode()、copy()を自動的にオーバーライドする不変のクラスが必要ですが、Javaとは異なり、100行のコードを占有しませんでした。







しかし、これがまだ言語にないという事実は、それが不可能であることを意味しません。 言語自体を使用するライブラリを介して、目的の機能を実装する方法の例を示します。







 class Person(shared String name, shared String email, shared Integer age) extends DataObject() {} value john = Person("John", "john@gmail.com", 112); value johnAfterBirhstday = john.copy<Person>({`Person.age`->113;}); assertEquals(john, john.copy<Person>()); assertEquals(john.hash, john.copy<Person>().hash);
      
      





つまり、ライブラリレベルでは、toStringを再定義し、クラスを不変のままにしておくことが可能で、個々の属性を変更したクローンを作成する機会がありました。 残念ながら、これはサポートが言語で行われた場合ほど速く動作しません。 また、コンパイル時に型チェックはありません=年齢で再定義してクローンを作成し、値として文字列を指定すると、実行時にエラーが発生します。 そのような機能がまだないという事実は確かに悪いです。 しかし、必要に応じてライブラリレベルで独立して必要な機能を記述できるという事実は非常に優れています。







11#オペレーターのオーバーロード







読みやすくするためにオーバーロードできる事前定義された一連の演算子:







 class Vec(shared Float x, shared Float y) satisfies Summable<Vec> { shared actual Vec plus(Vec v) => Vec(x + vx, y + vy); } value v = Vec(2.0, 3.0) + Vec(4.0, 1.0);
      
      





12#宣言の破壊







一部のオブジェクトは破棄できます。これは、たとえば、マップの反復処理に役立ちます。







 for ([key -> [val1, val2, val3]] in map) { print("Key: ``key``"); print("Value: ``val1``, ``val2``, ``val3``"); }
      
      





13#範囲







読みやすさを改善するには:







 for (i in 1..100) { ... } for (i in 0 : 100) { ... } for (i in (2..10).by(2)) { ... } for (i in 10..2) { ... } if (x in 1..10) { ... }
      
      





対照的に、KotlinはキーワードdownToなしで実行しました。







14#拡張機能







彼らはそこにいません。 初期の言語仕様では、このような可能性が考慮されていたようです。 ただし、原則として、拡張機能の代わりにトップレベルの機能が機能します。 たとえば、sayHelloメソッドをStringクラスに追加する場合、「world」.sayHello()はsayHello(「world」)よりも見栄えがよくありません。 将来的には表示される可能性があります。







原則として、クラスで使用可能な対応する関数はIDE自体で見つけることができますが、動作することもあります。







15#Nullセキュリティ







Javaはほぼ静的に型付けされた言語と呼ばれるべきです。 その内部では、String型の変数はStringを参照することが保証されていません-nullを参照する場合があります。 私たちは慣れていますが、これは静的型付けチェックのセキュリティを低下させ、その結果、Java開発者はNPEに対する絶え間ない恐怖に耐えざるを得ません。







Ceylonでは、null値を許可するタイプと許可しないタイプを分離することで、この問題を解決しました。 デフォルトでは、型はnullを許可しませんが、追加することで型を許可するように変換できますか?:







 variable String a = "abc"; a = null; // compile error variable String? b = "xyz"; b = null; // no problem
      
      





ユニオン型文字列の機能のためですか? それはString | Nullの単なる構文糖です。 したがって、次のように記述できます。







 variable String|Null  = "xyz";  = null; // no problem
      
      





Ceylonを使用すると、null許容型を呼び出すときにNPEと格闘できます。







 value x = b.length // compile error: b might be null
      
      





大きく見えるかもしれませんが、いくつかの機能のおかげで非常に便利です。 nullを許可する型をnullを許可しない型に変換する場合でも、スマートな型変換があります。







 if (!exists b) { return; } value x = b.length // no problem
      
      





安全な呼び出しを使用することもできますか?。、NPEをスローする代わりにnullを返します。







 value x = b?.length; // type of x is nullable Int
      
      





セキュアコールをチェーンして、他の言語で記述されることがあるネストされたnullでないifチェックを回避できます。 デフォルトでヌル値が必要ない場合は、elvis else演算子を使用します







 value name = ship?.captain?.name else "unknown";
      
      





これらのすべてがあなたに合わず、あなたが絶対にNPEを必要とするなら、そう明示的に言ってください:







 value x = b?.length else NullPointerException() // same as below assert(!NullPointerException x);
      
      





16#改善されたラムダ







これは優れたラムダシステムです。いくつかの賢明なソリューションのおかげで、読みやすさと簡潔さの完璧なバランスが保たれています。 構文は簡単です:







 value sum = (Integer x, Integer y) => x + y; // type: Integer(Integer, Integer) value res = sum(4,7) // res == 11
      
      





したがって、構文は次のようになります。







 numbers.filter( (x) => x.isPrime() ); numbers.filter(isPrime)
      
      





これにより、簡潔な機能コードを作成できます。







  persons .filter ( (it) => it.age >= 18) .sort(byIncreasing(Person.name)) .map ( Person.email ) .each ( print );
      
      





ラムダシステムと言語の構文機能により、CeylonはDSLを作成するための優れたツールになります。 Ceylon構文のAnkoのようなDSLの例:







  VerticalLayout { padding = dip(30); { editText { hint = "Name"; textSize = 24.0; }, editText { hint = "Password"; textSize = 24.0; }, button { "Login"; textSize = 45.0; } } };
      
      





17#IDEサポート







ちなみに彼女はとてもいいです。 Eclipseプラグインがあり、IDEAプラグインがあります。 はい、機能とバグの点では、すべてがScalaやKotlinよりもやや悪いです。 しかし、原則として、IDEの問題は実際には開発速度に影響しないため、非常に快適に作業できます。







合計すると、Kotlinの強みを活用すると、CeylonはDataObject機能の欠如によりKotlinに劣ります(ライブラリを使用して個別にエミュレートできます)。 それ以外の場合、それは以下の機能を提供しますが、より読みやすい構文を備えています。







まあ、Kotlinと同様に、CeylonはAndroid用に作成できます。







上記を読んだ後、印象が生じるかもしれません-なぜこれが必要なのですか? 同じことがKotlinにもあり、ほぼ1分の1です。







そして、セイロンにはコトリンにもスカラにもないものがあるという事実と、これらのことのために、言語自体は他の言語よりもはるかに優れています。 たとえば、ユニオン型、交差型、列挙型、より強力なジェネリック、モジュール性、群れ、注釈とメタモデル、タプル、理解用。 これにより、プログラミングへのアプローチが大きく変わり、より信頼性が高く、理解しやすい、普遍的なコードを書くことができます。 しかし、それについては次のパートで詳しく説明します。







興味のある方のために、いくつかの便利なリンク

https://ceylon-lang.org/documentation/1.3/introduction/

https://ceylon-lang.org/documentation/1.3/faq/language-design/

https://dzone.com/articles/a-qa-with-gavin-king-on-ceylon

https://www.slant.co/versus/116/390/~scala_vs_ceylon

https://www.slant.co/versus/390/1543/~ceylon_vs_kotlin

https://dzone.com/articles/ceylon-enterprise-ready

http://tryge.com/2013/12/13/ceylon-and-kotlin/








All Articles