Swiftについて何が嫌いですか

著者:Soroush Khanlou、 元のリンク 、元の日付:2016年8月14日
翻訳者: 喜び 、校正: 冬メロン 、最終: CMB

過去に、私はSwiftに多くの理由を書いてきました。 しかし、今日、私が書いたいのは、言語の欠如です。 これは比較されなければならない質問なので、言語が行っている良い場所、悪いこと、見込み客を指摘する例を挙げます。

言語内定義VS非言語定義

Ruby言語を見てみましょう

Rubyattr_accessorは、インスタンス変数のsettergetterを定義する方法です。 このように使うことができます


class Person
    attr_accessor :first_name, :last_name
end

一見すると、 Swiftletプロパティとvarプロパティがどのように宣言letと同様に、言語機能に似ています。 しかし、Rubyの関数は括弧なしでも呼び出すことができます。これはクラススコープで定義された関数です(Swiftでは静的関数を呼び出します)。


def self.attr_accessor(*names)
  names.each do |name|
    define_method(name) {instance_variable_get("@#{name}")} # 这是 getter 方法
    define_method("#{name}=") {|arg| instance_variable_set("@#{name}", arg)} # This is the setter
  end
end

あなたがRuby読むことができないなら、それは問題ではありません。 define_methodという関数を使用して渡す変数のgettersetterメソッドを作成します。 Rubyでは、 @first_name first_nameというfirst_nameインスタンス変数を意味します。

これはRuby言語に惚れ込んだ理由の1つです。まず、メタデータツールセットを設計して有用な言語機能を作成し、それらのツールを使用して必要な言語機能を実装しました。 Yehuda Katzは Rubyがこのアイデアをどのように実装しているかを説明していblocks 。 Rubyの言語機能は同じツールと同じ言語で書かれており、その言語のすべてのユーザーがアクセスできます。この言語や類似のスタイルのコンテキストで言語を書くことができます。特徴。

オプションのタイプ

Swiftの中核機能の1つは、オプションのタイプです。 変数を空にできるかどうかをユーザーが定義できます。 システムでは、列挙型の形式として定義されます。


enum Optional<WrappedType> {
    case Some(WrappedType)
    case None
}

attr_accessorと同様に、この機能はSwift言語構造を使用して自身を定義します。 これは、この架空のRemoteLoadingタイプのように、ユーザーがさまざまなセマンティクスを使用して類似のものを作成できることを意味します。


enum RemoteLoading<WrappedType> {
    case Loaded(WrappedType)
    case Pending
}

Optional (オプション)と同じ形式ですが、意味は異なります。 ( このブログのポストでは 、Arkadiusz Holkoはさらにこの列挙を詳述しています)

しかし、ある程度、 SwiftのコンパイラはOptional (オプション)型を知っていますが、 RemoteLoadingについては知らないので、何か特別なことをすることができます。 以下の同じ記述を見てください:


let name: Optional<String> = .None
let name: Optional<String> = nil
let name: String? = nil
var name: String?

それらの意味を分析してみましょう。 最初のステートメントは完全なステートメント(型推論付き)です。 独自のRemoteLoadingプロパティを同じ構文で宣言できます。 2番目のステートメントは、 NilLiteralConvertibleプロトコルを使用して、この値をnil設定したときの処理を定義します。 この構文は、あなた自身の型のアクセスにはRemoteLoadingていますが、 RemoteLoadingを使うRemoteLoadingはあまり真実ではありません。 これは、 C言語の開発者がSwiftをより快適に感じられるようにする最初の言語機能です。

3番目と4番目のステートメントでは、コンパイラはOptional (オプション)型の使用を開始し、特別なコードを書くことができます。 3番目のステートメントでは、 Optional (省略可能)型のOptional形T?を使用します。 これは構文的砂糖と呼ばれ、簡単な方法で共通コードを書くことができます。 最後の文は別の構文的な砂糖です:オプションの型を定義しても値を代入しなければ、コンパイラは値が.None / nilと推測します(変数がvar場合のみ)それは確立されただけだった。)

後者の2つのステートメントはカスタムタイプをサポートしていません。 言語のOptional型は、言語内に存在する構造体によって終了することができ、現在の型だけにアクセス可能な特定の型の例外があります。

家族

Swiftは、 "C言語ファミリのような"言語として定義されています。ループとForステートメントのおかげです。

Swiftfor..in構文構造は特別です。 SequenceTypeに準拠するデータ構造は、for..inループを介してトラバースできます。 これは、私が自分の型の値を定義し、それらが直列化されていることを宣言できることを意味し、for..inループを反復することができます。

if文とwhileループはSwift 2.2のBooleanType型でこのように動作しますが、Swift 3ではこの機能が削除されています。 for..inループ文のように私自身のブール値を定義することはできず、if文でそれを使用することはできません。

Swiftに反映されているように、言語機能を定義する基本的な2つの方法があります。 1つ目は、言語機能を定義するために使用できるメタツールを作成すること、もう1つは言語プロパティと言語タイプ値の間に明確で特定な関係を定義することです。

SequenceTypeの型の値がBooleanTypeに一致する型の値よりも有用であるという事実に異議を申し立てることができます。 しかし、Swift 3はこの機能を完全に削除しているので、ブール型が役に立たないので完全に禁止されると考える必要があります。

オペレーター

スウィフトのオペレーターも勉強する価値があります。 言語で演算子を定義するための構文があり、すべての算術演算子はこの構文で定義されています。 ユーザは自由に独自の演算子を定義することができます。これは、独自のBigInt型を作成し、標準の算術演算子を使いたい場合に非常に便利です。

しかし、 +演算子は言語で定義されており、三項演算子?:はしません。 +をクリックすると、この演算子の宣言にジャンプします。 ?:をクリックすると、三項演算子では何も起こりません。 これは、コード内に演算子として単一の疑問符と感嘆符を使用する場合は不可能です。 あなたのコードで感嘆符の演算子を使うのは良い考えではないと私はここでは言いません。 私は、この演算子が特別に扱われ、コンパイラーにハードコードされ、一般に他のC定義された演算子と同じであると言いたいと思いますC

3つのケースすべてで、私は2つのことを比較しました.1つは、標準ライブラリが機能を実装するために使用される便利な構文であり、もう1つは特権標準ライブラリがユーザコードを超えている特殊なケースです。

最高の文法と構文的な砂糖は、独自のタイプとシステムを使って言語の作者によって深く掘り下げられます。 SwiftはNilLiteralConvertibleSequenceTypeBooleanTypeを使用してこれらの状況を処理することがあります。 このvar name: String? 、デフォルトのプロパティ値(.None)をこの条件に適合しないように推論することvar name: String?できるため、これはあまり強力でない構文的砂糖です。

Rubyの構文が大好きですが、Rubyは演算子とfalsiness 2つの場所ではあまり柔軟ではありません。 既存の演算子の実装方法を定義できますが、新しい演算子を追加することはできません。演算子の優先順位は固定されています。 この点でSwiftはより柔軟です。 そしてもちろん、スウィフト3の前に、スウィフトはまた、 falsinessより柔軟に定義することがfalsiness

エラー

SwiftのオプションタイプはCの制御性にある程度類似しており、Swiftのエラー処理はCの例外処理と似ています。 Swiftのエラー処理でdodotrythrowthrowrethrowscatch新しいキーワードが導入されています。

throwsマークされた関数とメソッドは、値をreturnか、 ErrorType throwすることができます。 スローされたエラーはcatchブロック関数に捕捉されます。 背後では、成功や失敗を表す_Result型( antitypical / Resultなど )を持つ関数の戻り値の型をSwiftがオーバーライドしたと想像できます。たとえば、次のようになります。


func doThing(with: Property) throws -> Value

書き換え


func doThing(withProperty) -> _Result<Value, ErrorType>

実際、この_Result型は明示的には定義されていませんが、コンパイラでは暗黙的に処理されます )。 これは私たちの例に大きな違いはありません。 呼び出し関数の中では、値の成功はtry文を渡し、エラーが発生するとcatch block関数にジャンプして実行します。

これと前の例を比較すると、言語機能が言語の中に定義され、構文(演算子やSequenceTypeなど)と構文砂糖(オプションなど)が期待どおりになります。 逆に、Swiftのエラー処理は内部_Resultモデルを公開しないので、ユーザーはそれを使用したり変更することはできません。

場合によっては、Brad Larsonがロボットアームを動かすために使用する コードJSON解析コードなど、エラー処理にSwiftモデルを使用することをお勧めします 。 それ以外の場合は、Result型とflatMapを使用する方が適切です。

他のコードは、非同期処理に依存し、Result型の値をcompletion blockに渡す必要がありcompletion block 。 アップルのソリューションは、特定の状況下でのみ機能します。間違ったモデルに大きな自由を与えると、言語とユーザーとの距離を狭くすることができます。 結果が良いのは、それがたくさんのトリックを弾くほど柔軟であるからです。 try / catch構文はあまり強力ではありません。非常に厳密で、使用方法が1つしかないためです。

未来

Swift 4は、近い将来、非同期言語機能を使用することを約束します。 これらの機能がどのように実装されるのかは不明ですが、Chris LattnerはSwift 4について多くのことを書いています。

並行処理の1つのタイプ:アクター、同期/待機、アトミック性、メモリー・モデル、およびその他の関連トピック

Swiftの非同期処理メカニズムは、非同期/待機のように見えますが、私の主な理論です。 仮面ライダーの目には、非同期/待機文が関数が非同期のとき


async Task<int> GetIntAsync()
{
    return new Task<int>(() =>
    {
        Thread.Sleep(10000);
        return 1
    });
}

async Task MyMethodAsync()
{
    int result = await GetIntAsync();
    Console.WriteLine(result);
}

最初の関数メソッドGetIntAsyncは、しばらく待ってから値を返すタスクを返します。 この関数はTask返すので、 asyncとマークされます。 2番目の関数メソッドは、まずキーワードawaitを使用してMyMethodAsyncを呼び出しMyMethodAsync 。 これは、 GetIntAsyncタスクを完了する前に、システムが他のGetIntAsync実行できることをシステム全体に通知します。 このタスクが完了すると、この機能は制御を再開し、コンソールに印刷します。

この例から、 C#TaskオブジェクトはPromiseとよく似ています。 さらに、awaitを使用する関数はすべてasyncとして定義する必要があります。 コンパイラはこれを保証することができます。 このソリューションは、Swiftのエラー処理モデルと非常によく似ています。スローされる関数メソッドは捕捉されなければならず、そうでない場合、それらの関数メソッドもスローでマークされなければなりません。

また、エラー処理モデルのような欠陥もあります。 新しい構文といくつかのキーワードを追加した後は、便利なツールよりも構文的な砂糖に似ています。 この構造体は、標準ライブラリで定義された型と、コンパイラによって定義された構文に部分的に依存しています。

属性

属性の振る舞いは、Swift 4が紹介する重要な機能です。 ここでは、属性の振る舞いに対する拒否の提案があります。スイフト4では、この機能がより注意深く監視されています。

属性ビヘイビアを使用すると、レイジーを追加するなど、ビヘイビアをプロパティに関連付けることができます。 この遅延属性は、たとえば、最初にアクセスされたときにのみ値を設定します。 しかし、Swiftコンパイラに直接ハードコードされているこの特定の動作を使用できるようになりました。 属性の振る舞いによって、標準ライブラリがいくつかの振る舞いを実装しやすくなりますが、ユーザーが振る舞いを完全にカスタマイズするのが容易になります。

おそらくこれは既に世界でも最高の機能です。 コンパイラにハードコードされた機能から始めて、この機能で評判を得るには、より汎用的なフレームワークを作成して、言語自体を通してこの機能を定義することができます。 これに基づいて、スウィフト開発者は同様の機能を実装し、ニーズに合わせて微調整することができます。

Swiftのエラーモデルが同じパスをたどる場合、Swiftの標準ライブラリはResult型の値を公開し、Result値を返す関数はdo / try / catch構文を必要に応じて使用できます単一の失敗した並列同期イベント)。 非同期エラーのように、現在利用可能な構文に準拠する必要がないエラーの場合、ユーザーは共通のResultを使用できます。 このResultは多くのリンクを必要とし、ユーザーはflatMap実行できflatMap

非同期/待機は同じ方法で実行できます。 PromiseまたはTaskプロトコルを定義し、それに従うと、タスクが待たれます。 thenflatMapを利用できます。ユーザーのニーズに応じて、対応する言語機能を選択できます。

メタプログラミング

私はメタプログラミングについてもっと書いてみたいと思います。 私はObjective-Cメタプログラミングについて書いいますが、これは私たちが取り組んでいるものと非常によく似ています。 コードとメタコードの間の線はあいまいです。 Swiftコンパイラのコードはメタコードで、Swift自体はコードです。 あなたが(Rubyを使うのと同じように) operator関数の実装を定義すると、それはコードなので、新しい演算子を定義することはメタコードのように見えます。

プロトコル指向の言語として、Swiftは、 BooleanTypeSequenceType行ったように、言語の文法的な魅力を探求できる点で独特です。 これらの拡張機能を見てみたいと思います。

キーワードの停止と文法の開始や文法の停止と構文的な砂糖の開始の境界はあまり明確ではありませんが、この言語でコードを書くエンジニアは、標準ライブラリを開発するツールを使用できるはずです。

この記事はSwiftGG翻訳チームによって翻訳され、著者の翻訳によって承認されています。最新の記事はhttp://swift.ggをご覧ください

元のリンク