Quantcast
Channel: ++C++; // 未確認飛行 C ブログ
Viewing all 482 articles
Browse latest View live

ピックアップ Roslyn 7/21

$
0
0

PDBファイルへのソースコード埋め込み

Proposal: Embed sources in PDBs #12625

提案されたの自体はだいぶ前だし、実作業を開始したのも2週間くらい前からなんですが、PRの方で出たフィードバックをまとめて1 issueにしたみたい。結構おもしろい。

Visual Studio上で、参照しているライブラリのソースコードにF12キーとかで飛びたいわけですが。PDBみたいなデバッグ用情報にソースコードがまとまってないと、ライブラリ(+ PDB)を公開する側としても、参照する側としてもかなり面倒。issueページ中に背景シナリオがいくつか書かれてますが、例えば、

  • ビルド時にソースコード生成していて、その生成される分についてはバージョン管理に含めていない。ビルド時生成分のソースコードも含めてパッキングしてくれる仕組みがないとやってられない
  • 会社的にソースコードを出すこと自体はOKだけど、ソースコードを置いてあるリポジトリはプライベート。ライブラリ利用者にアクセス権は与えられない
  • GitHubとかでソースコードを公開しているけども、そのGitHub上のコードをVisual Studioから参照できるようにする設定までは面倒すぎてやってられない

などなど。

あと、さらに、PDBをライブラリ自体に埋め込みたいみたいな話も。

Task-like型を返す非同期メソッド

5/4のブログでちょっと書きましたが、非同期メソッドの戻り値に、Task(System.Threading.Tasks名前空間)以外の型を返せるようにしたいという話が出ています。

で、なんかマージされてる。

Allow async methods to return Task-like types #12518

次のVisual Studio ”15” Preview (次は preview 4になるはず)には入る予定っぽい

この機能なんですけど、割かし最近まで形になってなかったんですよね。なので当然のように、「C# 7/VB 15に入るのかどうか」議論のターゲットにもなってなかった。 4月くらいからLucian (今、VBのPMやってる。元々の専門が非同期処理な方らしく、C# 5.0のasync/awaitにもがっつり関わってたみたい)が作業を開始。この時点でも、「まだLanguage Design Meetingの議題に上げる段階に至ってない」とか言ってたんですが。

その後どうもプロトタイピングがうまくいったようで、気が付いたらLanguage Feature Statusのリストに並び、気が付いたらマージされてました。

結構有用な応用例があるんで、C# 7に間に合いそうなのは結構うれしいかも。次のBuild Insiderオピニオン、この辺りの話にしようかなぁ。


ピックアップRoslyn 8/5

$
0
0

Feature Status: Finishing

C# 7のステータスがいろいろ更新されたみたいですね。

C# 7に入るもの、ほぼ確定したのかなぁ。 C# 7/VB 15には状況が「Finishing(最終作業中)」のものばっかり残して、他は+1(その次)行き。

この感じは、次に出るであろうVS "15" Preview 4が最後のプレビュー版ですかね。

最終作業

最終作業中ってのは、まあ、基本機能は実装済みで、後は実装してみて初めて分かった問題とかバグの修正、みたいな感覚ですかね。 僕が把握してる範囲だと、以下のような作業はしているはず。

まあほんとにこういう細かい作業をする段階という感じです。

見送られた方

じんぐるさんも書いてますけど、元々C# 7/VB 15の欄にいたのに、+1送りになったのもありまして。

まあ、動きがなかったのでうすうす感づいていたものの。以下の3つ。

  • Async Main
  • Source Generation
  • Throw Expressions

Async Main

プログラムのエントリー ポイントとしてTask MainAsync(string[] args)Task<int> MainAsync(string[] args)を認めてほしいという話。

これ、最大の問題は同期コンテキストをどうするかなんですよねぇ。4年近く前にブログに書いたことあるんですけど、GUI (WPFでもUWPでもWinFormsでも)では問題ないのにコンソール アプリで実行すると競合起こす場合がありまして。

これに関する解決案みたいな話が全く見かけなかったんで、今はまだ取り組んでないんだろうなぁと。

ちなみにもう1つの問題として、今、↓みたいな書き方してるコードが結構たくさんあるはずで、これの互換性を崩しかねないからって話もあります。

class Program
{
    static void Main()
    {
        MainAsync().Wait();
    }

    static async Task MainAsync()
    {
        ...
    }
}

こちらはまあ、void Mainの方を優先する、みたいなルールで回避はできるはず。

Source Generation

これは、いろんなところが関係するんで、それが先送りの理由かなぁと。要するに、

  1. C# にコード生成を前提とした新文法を追加する: replace/original キーワードの追加
  2. CodeAnalysis API にSourceGeneratorみたいなクラスを追加して、コンパイラー プラグインとして自作のコード生成処理を挟めるようにする
  3. Visual StudioやVS Codeに、ソースコード書き換え時や、ビルド時にSourceGeneratorを掛ける仕組みを追加する

という3つが必要。

1はあったんですよね。一時期はmasterブランチにもマージされてました。問題もいくつか出てはいたですが:

それよりは、2、3の作業が全然見えてこないなーという感じで。やっぱりC# 7には入れないんだ…

VS "15" Previewのリリース ノートでも、「VS "15"で新たに入る機能はIOperationです」としか書かれてなかったし。 (※IOperationは、C#とVBの両方に対して、単一のプラグインでコード解析・コード修正を掛けれる機能。)

特に今は、Visual Studio、Xamarin Studio、Visual Studio Codeの全部にこの手の仕組みを対応させたがってる節があって、この3者の間で調整してそうな予感が。C#チームの範疇を超えてますし、多少時間掛かりそう。

Throw Expressions

void Method() => throw new NotSupportedException();とか、x ?? throw new ArgumentNullException(nameof(x))とか、int.TryParse(s, out var x) ? x : throw new InvalidOperationException();とか書けるようにしたいって話。

これをやるんだったら、never 型(去年のブログとかBuild Insiderの記事参照)も入れたいとかあるんじゃないかなぁと。

あと、こいつが本格的に必要になるのはmatch式(パターン マッチングのドキュメントのMatch Expressionのところを参照)が入ってからなので、これがC# 7から外れた以上、throw式だけを先に入れる動機付けは弱いはず。

Design Notes 7/15

C# Design Notes for Jul 15, 2016 #12939

で、ちょうど、7/15のDesign Notesが公開されて、ここにOut Varで導入される変数のスコープについて書かれています。

これまで、C#の変数宣言は書ける場所を限定していたのもあって、「宣言しているブロック内がスコープ」というシンプルなルールになっていました。

ところが、C# 7では、

  • Out Var: int.TryParse(s, out var x) ? x : default(int?); みたいに、out引数のところで変数宣言できるようになる
  • Type Switch: obj is int x ? x.ToString() : "unsupported"; みたいに、is演算子で変数宣言できるようになる

という2つの機能が入って、こいつらは、式を掛ける文脈ならどこでも変数宣言できてしまいます。 ここで導入される変数 x のスコープはどの範囲になるべきかという課題がありました。

現状、とりあえず相当厳しい方に倒して実装しています。すなわち、「xは、その式を含むステートメント内でだけ使える」というものです。要するに、以下のコードはコンパイル エラーに。

static void X(string s)
{
    var value = int.TryParse(s, out var x) ? x : 0; // x はこのステートメント内でしか使えない
    Console.WriteLine(x); // もうスコープを外れてる。使えない。コンパイル エラーに
}

いろいろ検討した結果、これはさすがに実際にありそうな用途をカバーしきれないという結論で、制限緩和を考えてるみたいです。

パッと見、「この式を含んでいるブロック内で使える」というのが素直に思えるんですが、if とか for とかが絡むと多少めんどくさく。

例1: if の条件式内で宣言した変数は、else側には伸びてほしくない

if (o is bool b) ...b...; // b はスコープ内
else if (o is byte b) ...b...; // bool の方の b はもうスコープ外。新しいbを作れる
...; // どちらのbもスコープ外

例2: 「含んでいるブロック内」で区切ると、forとかで変になる

for (int i = foo(out int j); ;) ;
// iはスコープ外だけど、jはスコープ内になってしまうのはいい?

例3: ブロックがないifとかどうするの

if (...)
    if (...)
        if (o is int i) ...i...
...; // ブロックを区切りにすると、i はここもスコープになる
     // その手前のifのせいで、iが初期化されている保証できないけどいい?

ってことで、ifとかforとかの場合は「embedded statements内にスコープを限定する」っていうルールにしたいそうです。 embedded statementsって、要するに、ifとかforとかの直後に書けるステートメントのこと。ifとかforとかの後ろには、ブロック、もしくは、何か1つステートメントを書けるわけですが、その部分のことを指すそうです。

余談: embedded statements

この話を見て、embedded statementsって何だっけ?って思って久々にC#の仕様書を眺めてみたんですが…

statement:
    labeled-statement
    declaration-statement
    embedded-statement

embedded-statement:
    block
    empty-statement
    expression-statement
    selection-statement
    iteration-statement
    jump-statement
    try-statement
    checked-statement
    unchecked-statement
    lock-statement
    using-statement 
    yield-statement

つまり、embedded statement = ステートメントのうち、変数宣言とラベル付きステートメント以外。

ということは、こうなる: https://twitter.com/ufcpp/status/761514377543462912

変数宣言はブロックなしでは書けない

まあ、要するに、変数宣言とラベルって、それを囲っているブロックが重要な構文なので、ブロックを省略するとか認めないっていう。言われてみればそうですよねという感じ。

(逆に言うと、そうまでして変数宣言できる位置をちゃんと絞ってたのに、Out VarとType Switchのせいで面倒になったという話。)

ピックアップRoslyn 8/6

$
0
0

Source Generatorが間に合わなかった理由

昨日のブログにある通り、C# 7に入る機能リストが更新されて、入るもの・入らないものに変化があったわけですが。

それと関連して:

https://github.com/dotnet/roslyn/issues/12630#issuecomment-237959967

やっぱり、Source Generatorが外れたのがショックだっていう人が現れて、それに対してC#チームの人が釈明してたりします。

まあ、大体昨日書いたことの通りですね。 IDE側の対応が大変で今回は無理とのこと。

Source Generatorって物自体が、IDE側にとっては結構大きな課題でして。例えば、コード生成によってプロパティなどのメンバーが増えたりするわけですが、その生成されるメンバーをIntelliSenseに出したりしようとすると、文字入力のたびにSource Generatorが走り、構文解析しなおすのかという話になります。IntelliSenseなんかは特にパフォーマンスにシビア(ここがもっさりしているとIDEのユーザーのストレスが非常に大きい)なので、Source Generatorが悩ましい存在になります。

もちろん、間に合ってるC#言語機能(replace/original)だけでも先に入れれないかなんてことも言われています。 けども、今早まって入れても、IDE側も対応できるようになった頃にはもっといいアイディアが出るかもしれない。より良いものがもし入ってしまったら、早まって入れたreplace/original構文が無駄・邪魔になってしまう。そういう状態にはしたくない。という話も。

ってことで、Source Generatorはもう少し成熟を待たないといけないということになります。

Sprint Summary

最近結構、「C#チームの中の様子が見えない。クローズだ。Roslynプロジェクトはオープンソースなんじゃなくて、オープンダンプ(クローズに作ったものをオープンにコピーして出してるだけ)だ」的なことを言い続けてるtroll(荒らし認定される勢いでしつこい人)がいらっしゃったり。

まあ、Roslynプロジェクトは確かに3000件ものIssueがオープンなまま整理されてなったりしますし、C#チーム内で起きたことも定期的には出てこない(ふと思い出したかのように出てくる)んで、あんまり擁護もできないんですが。

ということで、C#チームの人が重い腰をあげて「スプリントごとの作業概要」を公開。

Sprint 104 Summary #12974

まあ確かに、これくらいはちゃんと定期的に出そうよ…

ピックアップRoslyn 8/10

$
0
0

7月のデザインノートが2件ほど。

これ関連の作業がひと段落したところでまとめて清書して表に出したって感じですかねぇ。 この辺りの話、かなりの割合がもう実装されててマージされてたりします。

先週、dots.をお借りしてこんなイベントやってたわけですが

最新のmasterブランチの取ってきてビルドして実行してみると、大体この仕様通りになってる感じ。

さて、どんな感じの仕様かというと…

タプル型のメンバー名は省略・名前付きの混在可能

こんなコードでOKですって。

var t = (1, y: 2); // infers (int, int y)
(int x, int) t = (1, 2);

ちなみに、名前を省略したところは、ValueTupleがたの本来のメンバーである x.Item1 とかの名前で参照できます。

ITuple

タプル型みたいな「単に複数のデータを寄せ集めただけ」な型に対して、インデックスでメンバー参照したくなることがあります。 ValueTuple型はそのために、以下のようなインターフェイスを実装すべきじゃないかという話に。

interface ITuple
{
    int Size;
    object this[int i] { get; }
}

タプル型の分解に使いたいそうで。

ValueTuple型はこれを実装すべきだとは思うものの、名前にはまだ議論の余地あり。 インターフェイス名もIDeconstructableとかがいいかもしれないし。 要素数のプロパティもLengthとかCountとかもあり得るし。

var型がある場合。

C#のvarは、文脈キーワード(特定の文脈でだけキーワード扱いされる)です。varって名前のクラスがあると、クラス名として認識される。

で、タプル型の分解構文で以下のような書き方を認めることになるわけですが、

var (x, y) = e;

ここで、varクラスがあった場合どうなるべきか。

class var {}
var (x, y) = e;

ちなみに、世の中には、わざわざこういうvarクラスを用意しておくことで、型推論のvarを使わせない(コンパイル エラーにさせる)トリッキーな運用をしている人もいるそうで。C#チーム的には「(その良し悪しは置いといて)そういう運用も認めるべきでしょう」という感じ。 そういう背景もあって、タプル型の分解におけるvarでも、varクラスがあったらコンパイル エラーにするみたい。

var メソッド

じゃあ、メソッドの場合はどうか。分解代入の構文、メソッド呼び出しに似ているので、以下のような書き方ができてしまいます。

ref int var(int x, int y);
var(x, y) = e; // deconstruction or call?

参照戻り値なメソッドへの代入(参照先への代入)か、分解代入か、どちらにするべきか。

常に分解代入の方を選ぶそうです。メソッドの方を呼びたい場合は @var って書けばできます。

partialクラスでのインターフェイス

partialクラスの場合、複数の宣言で、同じインターフェイスを継承できたりします。 ここで、じゃあ、メンバー名違いの同じ型のインターフェイスを継承してしまった場合はどうするべきか。

partial class C : IEnumerable<(string name, int age)> { ... }
partial class C : IEnumerable<(string fullname, int)> { ... } 

タプル型は、内部的には全部ValueTuple構造体に変換されます。 名前は属性に残るだけ。 で、じゃあ、上記の名前違いのインターフェイスは別の型なのか同じ型なのかよくわからず。 紛らわしいのでコンパイル エラーにすべきでしょう。 逆に、メンバー名も含めて全一致している場合だけは、複数のpartial宣言に書いても大丈夫。

もう少し面倒なケースは、多重継承(インターフェイスであればC#でも多重継承が可能)。 以下の場合はどうすべきか。

interface I1 : IEnumerable<(int a, int b)> {}
interface I2 : IEnumerable<(int c, int d)> {}
interface I3 : I1, I2 {} // what comes out when you enumerate?
class C : I1 { public IEnumerator<(int e, int f)> GetEnumerator() {} } // what comes out when you enumerate?

現状、これもコンパイル エラーにする案で進めてるみたい。 できてそこまで大きなメリットもなさそうなので、複雑化させない方向に倒すという感じ。 もし、将来的にこれを認めたくなるような重要な利用シナリオが見つかったりした場合、それはその時に考える。

タプル リテラルの分解

null (全ての参照型に代入可能)とか、1 (intshort, byte 辺りのどれか不明瞭)とか、リテラルの場合、型があいまいなものがあります。 その分解はちゃんと働くべきか。

(string x, byte y, var z) = (null, 1, 2);

できるべきだろうとのこと。

各要素ごとに並べて書いた時と同じ挙動になるべき。上記コードであれば、まあ、↓みたいなのと同じ解釈をすべき。

string x = null;
byte y = 1;
var z) = 2;

ただし、これが逐次実行されるわけじゃなくて、一斉に代入が起きる。つまり、swapに使っても差し支えないようなにはなってる。

(x, y) = (y, x); // swap!

タプル型の中のvar

「タプル型の変数宣言」と「分解代入」は非常に似た構文になるわけですが。

(int x, int y) = GetTuple(); // 分解
(int x, int y) t = GetTuple(): // タプル型の変数宣言

じゃあ、以下の構文(これも似て非なるもの)の場合はどうなるべきか。

(var x, var y) = GetTuple(); // これは分解代入時の型推論
(var x, var y) t = GetTuple(): // varなタプル型。これは認めるべき?

で、結論的には、この後者は認めないとのこと。

分解代入の戻り値の型は void?

C#では、代入は式です。どこにでも書けます…

var x = 1;
var y = (x = 2) * x;

まあ、ろくでもないんですが。副作用を伴う式とか割かし害悪。C言語を参考にしすぎたところですね。とはいえ、今更変更できません。

例えばの話、forステートメントの中には式を書くことになっているので、以下のようなコードを書きたければ、タプルの分解代入も式でないといけないそうです。

for (... ;; (current, next) = (next, next.Next)) { ... }

とはいえ、実のところ、「戻り値がvoidの式」という扱いにすれば、forステートメントの中で使えつつ、さっきのろくでもないy = (x = 2) * x みたいなコードをなくせたりします。

ということで、voidであるべき?

まあ、これも、既存の代入式との一貫性がなくなるので、voidではなく、タプル型を返すべきだと思ってるみたいです。 C# 7では実装しなさそうだけど、後々は、分解代入の結果を、再度タプル構築して戻り値に返すべきだと思っているとのこと。

参考までに: Swift

ちなみに、Swiftはほんとに、代入は戻り値がvoidの式みたいです。 y = (x = 2) * x なんていうクソコードは認めません。

その割にインクリメント・デクリメントがあった y = ++x * x とか書けたわけですが。 そりゃ、forステートメントもインクリメントもなくしたくもなります(Swift 3で破壊的変更してまでなくす予定)。

分解を変換として、変換を分解として

分解代入と型変換はある程度似た構文です。分解は、タプル型への変換的な雰囲気があります。似てるのあれば、いっそある程度統一性を持たせるべき?

まあ、そうしない方がよさそう。分解(コンパイル結果的にはDeconstructメソッドの呼び出し)は型変換的に扱われるべきじゃない。

匿名型

匿名型({ X = 1, Y = "a" } みたいなやつ)はDeconstructメソッドやITupleインターフェイス実装を持つべき?

そうでもなさそう。実装しても、今のところ有用な利用シナリオが思い当たらないとのこと。 欲しくなる場面もなくはないけど、そういう場面では大体タプル型を使えば解決しそう。

分解代入時のワイルドカード

ワイルドカードってのは、要するに、要らない部分を読み飛ばす機能。

(var x, var y, *) = (1, 2, 3);

こういうコードで、3を読み飛ばすために使いもしないダミー変数を用意する必要はありません。

C#的に、こういう機能を入れるべきだろうとは思ってるみたい。 ただし、たぶん、C# 8になる(7には入らない。パターン マッチングと同時期に入る予定)。

あと、ワイルドカードのために使う記号はたぶん *。 関数型言語の類だと _ を使うことが多いんですが、C#では _ が有効な識別子になっちゃうので。 既存コードの意味を変えてまではこの記号は使わないみたい(コード解析をきっちりやれば不可能ではないけど、そうまでするかという話)。

double型に対するswitch

パターン マッチングが入った暁には、double型の変数もswitchに使えるわけですが。 ここで問題になるのは、double型の等値判定。

NaN(Not a Number)の扱いどうするの?とか、実は==EqualsでNaNとの比較結果が違ったりするけどどうする?とか。

==Equalsの違いというと、intの1とdoubleの1.0が等値判定とかも。前者はtrueになるけど、後者はfalse。

Equalsの側を使いそう。

ピックアップRoslyn 8/16 Close祭り

$
0
0

実作業が一段落つくと始まるissue整理。

すごい数一気にコメントついたりCloseされたり。以下、そのまとめ。

やらない

1行目: どういうものか

2行目: やらない理由

Proposal: Fixed-size buffers enhancements #126

構造体の内部に固定長配列を持てるようにしたい。

CLRのレベルで対応してもらいたい。C#コンパイラーでのコード生成はメリットの割に複雑すぎる。

infoof / propertyof / methodof (and reflection objects in attributes?) #128

infoof演算子(info-of、typeofのノリでMemberInfoを取得する)が欲しい。

リフレクションを言語に組み込みたくはない。nameofでもかなりのシナリオをカバーできる。

[Proposal] try-catch expressions #132

try-catch を式にしたい。

このページで提案されている構文だと問題がある。再提案があれば、取り組む価値があるかはまだ検討の必要があるかもしれないが、現状そこまでには思っていない。

Language support for the decorator design pattern #124

デコレーター(PythonのデコレーターとかKotlinのclass delgationと同種の機能)が欲しい。

いくらかのメリットがあるのはわかるし、過去に似た機能の提案も見てきた。でも、コンパイラーが暗黙的にコード生成するような機能には心配事が多々ある。 今なら、ソース ジェネレーター (たぶんC# 8)で解決できるかもしれない。

Strongly-typed type aliases #58

同じ型なんだけど、互いに暗黙的型変換はできないエイリアスを作りたい。例えば、EmailAddress型(中身はstring)とstringを区別したい。

ソース ジェネレーター (たぶんC# 8)を使って構造体をコード生成して、同様のことを実現してほしい。ソース ジェネレーターで苦痛は減るはず。

Auto properties with setter blocks #123

プロパティで、getのみ自動実装で、setは手動実装できるようにしてほしい。

良いディスカッションはあった。ディスカッションの中で出てきた副次的な案は別のissueページに移った。 でも、このページで提案されている元々の提案はやらない。バッキング フィールドを参照するような機能は得られるものに対して労力が過剰。

Implement Units of Measure or a broader class of similar contracts #144

F# のUnits of MeasureがC#にもほしい。

非常にクールな機能だと思うものの、同種のものをC#に追加するのはメリットの割にコストが大きい。

訳注: たぶん、上記のStrolngly-typed typle aliasesで十分な利用シナリオも多いと思う。

[Proposal] Better handling of multiple comparisons with the same variable (a > min && a < max) #136

min < x < max みたいな、数学でよく見る多項比較できるようにしてほしい。

C# ではすでに a < b < c(a < b) < c の意味で有効な式になっているし、難しい。 e in (0 ... n) みたいな文法も、e < n なのか e <= n なのか混乱の元。

Allow type parameters to be declared co/contra variant on classes. #171

クラスの型引数にも共変性・反変性を認めてほしい。

CLRレベルでの対応が必須。

Proposal: Virtual arguments in methods #176

引数の実行時の型でメソッドのオーバーロードを呼び分けれるようにしてほしい(いわゆる多重ディスパッチ)。

ちゃんとやるのであればCLRレベルでの対応が必須。 型スイッチ(C# 7で入る)があれば書くのが多少楽になるけども、多重ディスパッチは型スイッチと比べてかなり複雑。

Feature Request: Lighter syntax for doc comments #85

docコメントがXMLなの重たい。JavaDoc/JSDocみたいな文法ほしい。

現状に不服があるのはわかる。でも、新旧文法の混在・競合みたいなのは嫌だし、直近での検討はしない。

重複削除・もうやってる

1行目: どういうものか

2行目: 何と重複してるか

Allow local variables to be static and/or readonly (deeper scoping) #49

ローカル変数にstatic(スコープが関数内に限られた静的フィールドを作る)とかreadonly(書き換え不能)とか付けれるようにしてほしい。

以下の2つに分割できる。

Allow C# to use anonymous iterators. #24

匿名関数でイテレーターを作らせてほしい。

C# 7で入るローカル関数で同じことができる。

Please provide the C# Specification in PDF instead of DOCX #31

C#の仕様書をPDF提供してほしい。

長期作業として、マイクロソフトC#仕様書とECMA標準規格書の統合作業をしている。ECMAの方のはPDFで提供されてる。

訳注: 現体制になってから全然仕様書が表に出てこないんだけども… Long termってどのくらいLongなんだろう…

Lambdas omitting parameters #20

ラムダ式の引数のうち、要らないものは無視できる構文が欲しい。someFunction((_, foo) => fooだけ使う); みたいなの。

パターン マッチング(たぶんC# 8)と一緒にやる。

Extension properties for classes #112

メソッドだけじゃなくて、プロパティも拡張で作れるようにしてほしい。

以下の提案の一部分になる。

UnreachableAfterAttribute and unreachable code detecting #59

常に例外を出すことが分かっていて、呼び出しよりも後ろのコードには絶対に到達しないことが保証できるようなメソッドを作りたい。そのためにUnreachableAfterみたいな属性を作ってほしい。

neverってのを検討中。

引き続き検討

1行目: どういうものか

2行目: どう取り組んでいるか

[Proposal] Object initializers for factory methods #133

new T { X = ... } みたいなのを、new演算子ではなく、ファクトリー メソッドを使った場合でも使えるようにしてほしい。T.Create() { X = ... } みたいな。

with (たぶんC# 8になる)と合わせて考えたい。

Do not require type specification for constructors when the type is known #35

var (右辺値から変数の型を推論)の逆で、T x = new();みたいに、左辺値からnewの型推論をしたい。

分解(deconstruction)の対となる構文として魅力的になりそう。

訳注: 分解の方がvar (x, y) = tuple;みたいに型推論が効くんだから、構築(new)の方にも型推論が効いても良さげ。

IAsyncDisposable, using statements, and async/await #114

非同期Disposeがしたい。usingステートメントで、IAsyncDisposable的な非同期版のインターフェイスを使えるようにしてほしい。

Lucianにタスク割り当て。

訳注: たぶん、非同期シーケンスと一緒に検討。Lucian はTask-likeとか含め、非同期処理がらみの新仕様に取り組んでる人。

Extend LINQ syntax #100

クエリ式の後にToList()とかSingle()とかの、シーケンス自体に対する操作を呼ぶのが、今の構文だとだるい。

バックログに入れておく。from x in items where x.foo, select x.bar do Single() みたいな構文がいいかも。

[Proposal] (Minor) Method groups for constructors #140

コンストラクターをメソッド グループ(デリゲートにメソッドを渡すために、Action a = Methodとか書くやつ)として認めてほしい。

よくある要求だし、よいアイディア。

Visual Studio "15" Preview 4

$
0
0

Preview 4が出たみたいですね。

最近、まあ、Preview の新しいのが出ても、C# vNext の進捗具合だけしか取り上げてなかったりするんですが。 今回も主にその話題で。

インストーラー

1点だけ。新しいインストーラーがだいぶちゃんとしたものになってますね。 細かい機能を選択して入れるんじゃなくて、「UWP開発したい人はこのオプションを選択してください」みたいな感じのUIに(その結果、それに必要な機能に一通りチェックが入る)。

Visual Studioってものすごいインストールに時間が掛かるので有名ですが、結構な割合、エミュレーターとか仮想マシンのインストールに取られてる時間だったりします。 要するに、UWPとかモバイル開発が不要なら、ここ外すだけでかなりインストール早かったり。

C# 7 進捗

C# 7で入るものの予定は以下のページ参照。

これを踏まえて、Preview 4でのC# 7実装の状況まとめ:

ValueTaskは、ちょっと前に中の人が「Preview 4に入る予定のブランチにマージされたぜ」ってtwitterでつぶやいてましたが、 QA通らなかったのか、リバート食らってました。

他は、たぶんあと細かい調整だけですかね。 変数/ローカル関数のスコープとか、クエリ式中でも分解構文使えるようにするとか。

ちなみに、タプルを使うためにはSystem.ValueTupleが、ValueTaskを使うためにはSystem.Threading.Tasks.Extensionsが必要になりますが、 どっちももうNuGet.orgに並んでいます。

ピックアップRoslyn 8/27: What’s New in C# 7.0

$
0
0

ブログ: What’s New in C# 7.0

Roslynリポジトリ内の話じゃないんですけど、Preview 4でのC# 7の動きについて、Madsがブログ書いてました。

7.0

そういや、この文章だと「C# 7.0」。 昔ちょっとブログ書きましたけど、最近って小数点以下のバージョン付けないんですけど。 C# 7はずっと「7」ばっかりみてたけど、久々に「7.0」表記。 この辺り、最近、C#チーム内でも統一してないなぁ…

C# 7.0の内容

このブログの内容的には、 先日僕もブログで書いたのとそんなに差がないんで詳細は省略。

差分というか、僕が書いた方で漏れてるのは、以下の内容:

out varのwildcard

out varと一緒に、*、要するに、受け取る必要のない out 引数を無視するための「wildcard」も入れるかもという話もあるみたい。

*は、 元々、パターン マッチング構文の1機能(パターンの1種)です。 で、out varもパターン マッチングと同列で検討されてた機能です。

今、C# 7で、out varだけは先に入れる方向で実装が進んでいるわけですが、どこまで一緒に前倒すかという問題。 out varに対する*はC# 7で入れるかどうかまだ迷ってるっぽいです(C# 7時点で入らないとしても、たぶんパターン マッチングとは同時に入る)。

More expression bodied members

今、メソッドとget-onlyなプロパティに限り、式が1つだけの実装に => を使えるわけですが。 それを、コンストラクター、デストラクター、getとset両方あるプロパティに対しても使えるようにするやつ、C# 7で入りそうらしい。

この機能、完全にコミュニティ主体の実装なんですよね。 3月にブログ書いてますけど、この機能はずっと「需要がある程度あるのはわかるけど、メリット小さめだし後回しになってる」みたいな状態でおいておかれてたものなんですけど、それに対して、「実装したよ」ってプルリクエスト送ってきた人がいて。 C#チームが「そのプルリクを取り込むことにした」って書き込みはしてたんですが、その後音沙汰なしでした。 久々に見たと思ったら、C# 7のタイミングで取り込むことにしたんですねぇ。

Throw expressions

これ、結局どうなるんだろ…

Language Feature Status」からは、むしろ、つい最近「7じゃなくて7の次で」降格したところなんですけど。 どっちが正しいんだか…

MSDNブログ

そういや、気が付いたら、MSDNブログから「C#チーム ブログ」「VBチーム ブログ」は消えてるんですよね。 Roslynのgithubリポジトリに一本化して、もうブログは書かないのかと思ってたら。

.NET配下でブログ書きますか。

コメント欄

で、なんだかんだ言って、ブログはコメントたくさんつきますね…

いくつか、気になったの拾っておきます。

contract

「design by contract」については何もないの?とのコメントが。

返信ついてて、「今のタイミングでは何もしない。contractはいまいち煩雑で複雑になりがちだし、そんなに大々的な検討もしてない。contractの用途的に、9割がたはnullチェックに関するものだけど、それは型システム指向で『非null参照型』を次のメジャー リリース向けに検討してる。」

VB

VBは?沈みゆく船だからC#に移った方がいい?

に対して、他の人から「頼む、ほんとお願いだからVBの質問しないで。死なせてあげて」とかついてる。

さらにそれに対して、VBのPMの人が「頼む、ほんとお願いだからVBの質問し続けて。フィードバックもらえることがほんとものすごい重要だから」とか返信してるという。

どこの国でも似たような状態ですよねぇ。 VBが死ぬときは、今のVBユーザーが絶滅したときであって、VB使ってない外野がとやかく言うなよっていつも思うんですが。

wildcard? *なの?nullとかでいいんじゃない?

*とかきもくない?↓みたいな、null使う文法の方がよくない?とか言ってる人が。

p.GetCoordinates(out int x, null); // Ignore return
p.GetCoordinates(out int x, out null); // Alternative ignore return

何この、見た印象ぬるぽ起こしそうな構文… 値を受け取るべき場所にnull書くとかちょっと…

そして飛び火して、本題からそれて「nullはこの世から消えるべき」主張をこのコメント内でし始める人まで現れる始末。

タプルとout var両方やるの?

タプルもout varも、多値戻り値を返すために使う(ことが多い)構文なわけですが、 なので当然「どっち使えばいいの?なんで両方やってるの?」という質問も。

それに対する答え:

  • だいたいの場合においてはタプルを使うといいよ
  • ただし、以下の場合においてout引数が残る余地がある
    • 複数のオーバーロードを用意したい場合(タプルというか、戻り値だとオーバーロードできない)
    • Tryパターンなんかだと、単一のboolを返す意味がある。ifステートメントの条件式とかで使えるように

ちなみに、別のコメントに対する返答で、今このタイミングでout引数に対する拡張を入れる意味について「パターン マッチングとout varには相乗効果がある」との回答あり(要するに、パターン マッチングのついで。パターン マッチングをやるならこちらも)。

C#がどんどん汚染されてない?

だいたいの人は「便利!待ち遠しい!」って言ってる中、まあ、毎度必ずいるわけですが「ちょっとトリッキーすぎない?泥沼にはまりそうで嫌だわ」って言い出す人が。 C# 7に限らずこれまでも、C#に限らずありとあらゆる言語でこういう意見絶対出てくるわけですが。

それに対する Mads の回答:

あなたは言語の進化のある種の核心をついてる。我々は機能を足せるけど、絶対に消せない。C#の進化においてもこのことが重くのしかかってる。 Anders (元祖、C#/.NETの父)の発言は有名だけど、「あらゆる機能はマイナス100ポイントから始めろ」と言ってる。 全ての機能について、我々は誰が喜ぶか、今現在だけでなく、今から10年後のことも考えるようにしている。 今現在有用かどうかだけでなく、永久に言語の一部分を占め続けるということを考えるようにしている。

我々はだいたいは正しいが、常に正しいわけではない。わかるだろうが、未来というのは予測が最も難しいものの1つだ。 このことは、言語設計のアプローチに対する異なる側面をもたらす。 我々は大胆でなければならない。 我々は遠い未来への責任に追い詰められてはいけない。 なぜなら、さもなくばその未来も得られないから。 C#は今この時「最高のプログラミング言語」の1つでなければならない。 さもなくば、明日にはそうではなくなってしまう。

なので、我々は少々アグレッシブに機能を追加する。 開発者にとって最も価値ある機能を追加しようと試みる。 さらに、その機能を追加するにあたって、言語の複雑性が増すという観点から不利益が最小になるよう試みる。 たとえばそう、パターン マッチングは新しい機能になるわけだが、しかし、既存の言語要素、例えばisswitch、既存の型の中で動くものだ。 それは(うまくいけば)我々の過去とよく統合されている。 同時に、いくつかのパターンを、将来の発展の余地を残しつつ、今現在の「税負担」にならないように導入している。 それは、我々のもっともらしくあり得そうな未来とよく統合されている。

信じられないかもしれないが、我々が何パターンかあり得そうな未来について、取り組む余地を確実に残せるよう、投機的な設計にどれだけ時間をついやしていることか。 これは、我々が、新機能を追加するにあたって、この言語を長期的にわたって健康で生存力がある状態に保てるよう試みていることの一部分だ。

以上で、我々がC#の長期的な進化についてどのくらい考えているか、その姿を伝える助けになれば幸いだ。

タプル

$
0
0

思い出したかのように「C# 7思い出話」。

タプルがらみも先週末で一通り書き終わったはずかな。

タプル

これ自体はおおむね当初予定通りですかね。 そんなに裏話的な面白話もなく。 文法的に変化が大きいんで実装は大変そうでしたが。

しいていうと、他の提案を差し置いてタプルの優先度が高かった的な話ですかね。 タプルは、レコード型とか、 C# 7のさらに先で入る予定の機能の基礎に使う可能性があって。 依存順的に先にないと行けないという、割かしシンプルな話。

タプルは、(int x, int y)みたいなメンバー名を持った型から、ValueTupleという完全に名前のないデータ構造を生成する機能なわけで、 型に緩い世界と厳しい世界の橋渡しとして期待されます。ロジックを書く上では厳しい型付けを持っている方が楽ですが、View (WPFやUWPのData Bindingみたいな世界)や、Wire Data (通信層でのデータのシリアライズ・デシリアライズ)とかでは緩い型の方が助かったりします。その間をつなぐ基礎に使えそうって意味で、タプルには期待感があります。

そして、レコード型をタプルをベースに作る、あるいは、レコード型からタプルへの変換を用意すれば、型名がある世界とない世界もつなげます。

レコード型をタプルをベースに作るっていうのは、例えば以下のようなコードを書いたとして(これがレコード型って呼ばれる機能)、

class Point(int X, int Y);

以下のようなコードに展開するという案。

class Point
{
    (int x, int y) Value;
    public int X => Value.x;
    public int Y => Value.y;
}

レコード型からタプルへの変換を用意というのだと、以下のような感じ。

class Point
{
    public (int x, int y) Value => (X, Y);

    public int X { get; }
    public int Y { get; }
}

どちらにしろ、依存関係があるなら、依存されている側の方が先に落ち着いていないといけない。

レコード型は、その前身となる primary constructorっていう機能提案から考えるとC# 6よりも前(一瞬、C# 6に入れる想定で話が進んでた)、2014年半ば頃からあります。それが、「もっと洗練してレコード型にしてから取り入れたい。そのためにはC# 6では無理」となり、「タプルやパターン マッチングと併せて考えたい。そのためにはC# 7よりも後」となり今に至ると。

分解

こちらは逆に、前倒し。 前倒しというか、パターン マッチングがらみの仕様のうち、一部分だけが先行してC# 7に入るわけですが、その一部。

パターン マッチングは結構大きな提案です。All or Nothing、「全部入れるか全部入れない」かとかで入れようとすると、たぶん3年くらい先になります。 こないだ短期リリース サイクル化の話とか書きましたが、後への影響残さないなら、先に少しだけリリースする方が助かります。

元々は、以下のような構文で話が進んでいたんで、それからするとずいぶん、分解専用の構文になりました。多少、そこは、今後、パターン マッチングの全体が入るときに衝突しないのかな?とか思ったりはします(たぶん大丈夫という判断が付いたからC# 7で入ることになったはずですが)。

let パターン = 分解したいもの;

Unity 5.5でasync/await使えた話

$
0
0

Unity 5.5 ベータを入れてみたという話。

ブログ曰く、「Mono C# コンパイラー がMono4.4にアップグレードしました。」とのことなので、これ、C# 6使えるはずよね?と。

公式サポートはC# 4

リリースノートを見ると、「C# 4です」って書かれてるわけですが。 Mono 4.4のコンパイラーを使っててC# 6使えないとかお前は何を言っているんだ…

まあ、標準ライブラリの方が .NET Framework 3.5相当のままなので、Taskクラスが使えない。 なので当然、普通にやるとasync/awaitが使えない。 その状況下で「C# 6対応」とか言っちゃったら、問い合わせ押し寄せてやばいでしょうから、公式には「C# 4」と言わざるを得ないのはわかります。

要するに、コンパイラー的にC# 6に対応している状況で、langversionオプションを指定してわざと4に絞ってる。

例えば、以下のようなコードを用意します。

async/await (C# 5)、expression-bodied method (C# 6)とか使ってます。 これをコンパイルしようとすると、 Unity 5.3だと以下のようなエラーに。

Assets/Scripts/NewBehaviourScript.cs(30,13): error CS1519: Unexpected symbol `=>' in class, struct, or interface member declaration
Assets/Scripts/NewBehaviourScript.cs(37,28): error CS1519: Unexpected symbol `XAsync' in class, struct, or interface member declaration

これが、5.4は試してないんですけど、5.5だと以下のように。

Assets/Scripts/NewBehaviourScript.cs(30,12): error CS1644: Feature `expression bodied members' cannot be used because it is not part of the C# 4.0 language specification
Assets/Scripts/NewBehaviourScript.cs(37,16): error CS1644: Feature `asynchronous functions' cannot be used because it is not part of the C# 4.0 language specification

5.3曰く「ちょっとその文法わからない」、5.5曰く「その機能はC# 4じゃないからダメ」。 5.5は解釈自体はできていると。

-langversion:6

てことで、まあ、オプション変更。

プロジェクトの Assets 直下に mcs.rsp っていうファイルを作り、

Unityに対してC#コンパイラー オプション指定

以下の1行を書いて保存。

-langversion:6

これで、C# 6なコードもビルドが通ります。

ただ、Unityが出力する csproj ファイルに余計な行が入るんで、こいつも消さないと、Visual Studio上でC# 6の機能が使えなくなります。

余計な1行

この行も削除が必要。 いったん手作業削除してるんですが、ちゃんとやるならProject File Generationでも使ってフックしてやればよさそう。

Taskクラスが必要なasync/awaitはともかく、ライブラリ依存がほとんどないC# 6の機能とかなら問題なく使えると思います。

async/awaitを使う

で、Taskクラスさえ自前で用意すればasync/awaitも使えるっぽい。 うちは自力でバックポートしたライブラリを持ってるので、それを参照してみたら、あっさり動きました。 ライブラリは↓これ。

実際動いてるプロジェクトがこんな感じ↓。

まあ、Unity Editor上でしか試してないんで、IL2CPPとか実機で動くかはわからないんですが。

プログラミング言語における文字コードの話

$
0
0

世の中がほぼUnicode前提になってめでたしめでたし。とはいかなかった現実の話。

String型でできる文字列処理とか、ソースコード自体、特に識別子で使える文字とか。

軽くおさらい: Unicode

まあいろんなところでいろんな人が書いてると思うのでさらっと概要だけ。

Unicodeは、元々、「65,536文字あれば十分だろ」とかいう幻想の元、2バイト固定長の文字コードとして作られていました。 もちろん足りなくて、ビット数を拡張。基本が2バイトのままでこの拡張した分を取り扱えるようにしたのが今のUTF-16で、拡張分は2文字分(4バイト)を使って表現。 この、2文字分使って1文字を表すやつのことをサロゲートペア(surrogate pair: 代理対)と呼びます。

あと、ASCII文字も2バイトになるのを欧米人が嫌って、ASCII文字はASCIIコードのまま、逆に漢字・ひらがな・カタカナなんかは3バイトになるようなエンコード方式ができて、それがUTF-8。 固定長文字列とか幻想でした。サロゲートペアとか出てくるくらいならUTF-8でいいでしょう…

UTF-8やUTF-16へのエンコード前の状態、Unicode的に1文字として割り当てられているコードを、コードポイント(code point: 符号点、符号位置)と言います。 Unicodeコードポイントは今のところ21ビットの数値。

コードポイントこそが文字の最小単位かというとそうでもなくて、アクセント記号とか濁点とか、他の文字と組み合わせて使うことで、表示上は1文字に見えるけどもコードポイントは複数になる文字があり得ます。 「か」(U+304B)+「 ゙」(U+3099)で「が」1文字に見えたり、 「a」(U+0061)+「 ́」(U+0301)で「á」1文字に見えたり。

その他、漢字の異字体セレクターとか、絵文字の肌トーンとか、複数コードポイントで1文字になるものが結構あります。 こういう、表示上は1文字として扱うべきという単位を書記素クラスター(grapheme cluster)と言います。 UI的に、文字の選択や削除は書記素クラスター単位で行うのが好ましいです。 さもなくば、家族が1人1人消えていくホラーが発生するわけです

まとめ:

  • コードポイント: Unicode的に1文字として割り当てられてる21ビットの数値
  • エンコード
    • UTF-16: 2バイトですべての文字を表せると思っていた時代の名残。16ビットで収まる範囲はコードポイントそのまま、収まらない範囲は2文字分(4バイト)使って表現(サロゲートペア)
    • UTF-8: ASCII文字がASCIIコードのまま扱えるように、1~4バイトになるように符号化する方式
  • 書記素クラスター: 表示上1文字になって、選択や削除の際にはまとめて処理されるべき単位

プログラミング言語のString

Java (1995年リリース)の辺りから、新しめのプログラミング言語は大体Unicodeを前提としています。 ソースコードは、UTF-8で保存しておけば日本語とかも識別子(変数名とかクラス名とか)に使えます。 String型は標準でUnicodeを扱えたりします。

とはいえ、Unicode自体が紆余曲折あったので、プログラミング言語も紆余曲折振り回されます。

JavaやC#はString型がUTF-16だったりします。サロゲートペアの処理が結構面倒。 Unicodeがまだ2バイト固定長を夢見ていたころの世代の言語。 UTF-16文字からUnicodeコードポイントを得るためのAPIはあるんで何とかできはしますが。

Goなんかは文字列はUTF-8だそうです。

Swiftは、最近できただけあって、結構頑張っています。 内部的にUTF-8かUTF-16を問わず(String型が内部に弁別用のフラグをもってて、適宜デコードしてくれてる)。 API的にも、String型が、以下のようなメンバー(正確にはextension)を持っています。

  • characters: 書記素クラスターを列挙
  • unicodeScalars: Unicodeコードポイントを列挙
  • utf16: UTF-16エンコードした2バイトを列挙
  • utf8: UTF-8エンコードしたバイト列を列挙

まあ、ちゃんとしてるように見えて、Unicodeの仕様追加に対してちょっと危ういんですが…(後述)

プログラミング言語の識別子

一方で、ソースコード自体の話。特に、識別子に使える文字の話。

まともな神経をしてる言語は、Unicodeカテゴリーで識別子に使える文字を縛っています。 非常に大まかにいうと、たいていの言語で識別子に使えるのはLetterのみ。 記号は使えないように絞ってあります。 逆に、Letterなら大抵なんでも。日本語だろうが漢字だろうが使えます。

C# とサロゲートペア

ちなみに… 現状の C# はサロゲートペア使えません… 変数名に𩸽とか𠮷とかが使えない状態です。 つい最近まで仕様書に「Unicode Standard, Version 3.0」(サロゲートペアなんてものがなかった時代の仕様)って文字が入ってたくらいでして。 今は、「The Roslyn compilers depend on the underlying platform for their Unicode behavior」(プラットフォームによる。Unicode Standardの変更の影響を受ける)に仕様の文面も変更されることになってるんですが、サロゲートペアへの対応はまだ。

最近、中国人っぽい人から不具合報告を受けています。 C#チームの応答を見るに、修正入って使えるようになるかも。 まあ、仕様書的にサロゲートペアが使えてもおかしくなさそうな記述になってる今、 対応していないのはおかしいはずですんで。

Swiftの攻めっぷり

一方で、Swiftのやんちゃっぷりは凄くて… Swiftリリース時点でも、「絵文字が使える!」ってアピールされてたのは結構皆さん覚えてらっしゃるかと思えますが。 調べてみたら、絵文字どころかほんとに何でも使える。 個人的にツボったのは以下の2つのネタコード。

  • 0~9の和は1023 … 数字に見えますけど、「Mathematical Alphanumeric Symbols」って領域にある記号を使っています
  • 不可視識別子 … これも数式用の記号。変数xとyを、xyと並べたときに、それが何を意味するか(掛け算か、足し算か、関数適用か、添え字を並べただけか)を判別するために使います

バグでも実装ミスでもなく、仕様。 いいのか悪いのか… まあ、Letterに限定してもろくでもないコードは書けるんで、程度の問題なんですが。

Unicodeの変更/OSの対応状況に振り回される

Unicode、今もうバージョン9とかなんですっけ。基本は文字の追加だけなので、そんなに毎度毎度は問題を起こさないんですが…時々…

中黒のカテゴリー

有名な問題として、「・」(U+30FB、中黒)の扱いが変わったというやつがありまして。 中黒って、日本語的にはハイフンのような使い方をします。2つの単語を並べるときに区切り文字として使う。 ところが、間違えて、「ー」長音記号と同じカテゴリー(Pcカテゴリー)を割り当てられていたという過去があります。 これを、Unicode 4.1だか辺りで、ハイフン扱い(Poカテゴリー)に修正したそうです。

長音と同じであれば、たいていの言語で識別子に使えます。JavaやC#では、昔は中黒を識別子に使えていました。 それが、JavaはJava 7で、C#はC# 6で、いきなり識別子として使えなくなります。 破壊的変更に対してかなり保守的な言語であるJavaとC#が立て続けに破壊的変更を起こしたという。 言語仕様には全く変化がなく、ただ、使っているUnicodeのバージョンが上がったというのが原因になります。

絵文字の肌トーンと書記素クラスター

割かし最近の追加と言えば肌トーン。「絵文字とカラーフォントがパンドラの箱開けちゃった」と有名なあれ。

「なぜ白い肌の絵文字しかない」とかいう政治的な地雷を踏みぬいた結果、「Emoji Skin Tone Modifiers」という補助文字が作られて、 絵文字の後ろにくっつけると絵文字の肌色が変化するというもの。

iOS 8.3で対応したんでしたっけ。 それ以前は、肌トーンは「肌色の四角形」で描画されていました。顔文字と並べると、顔文字+四角形の2文字が表示されます。 以降は、ちゃんと1文字の顔文字で肌の色が変わります。

ここで、書記素クラスターの話に戻ります。 表示上1文字として描画されるべきものが書記素クラスター。 肌トーンはどうなるかというと、Unicodeコンソーシアムのレポートによれば「単一の書記素クラスターとして扱うべき」とは書かれています。 が、まあ、あくまでそれは、肌トーンに対応したOS上での話。 iOS 8.3より前なら2文字として表示されているわけだし、以降なら1文字扱いしてほしい。

これ、Swiftにとっては、characters(書記素クラスターを列挙)の結果が変わることになります。 現状は変更はされていなくて、肌色指定付きの顔文字のcharacters.countを調べると、2文字扱いされます。

そして、公式フォーラムでも問題に上がってるみたい。

議題は、要するに、

  • characters.countの数え方はプラットフォームに依るべきか
  • プラットフォームに依るべきとすると、プラットフォームごとにcharactersの挙動が変わることになるけど、それはありか
  • 挙動が変わるのを良しとしない場合、プラットフォーム依存な、ちゃんと表示されてる文字数を取る手段はあるか

という話。

もしかして、書記素クラスターは人類が手を出すにはまだ早すぎたのではないか…

サロゲートペア対応C#

何で今こんなこと調べてたかというと、ちょっと、C#コンパイラーいじってみるのの題材として、C#のサロゲートペア対応とかやろうかと思ってまして。

.NET Fringe Japanで「言語機能の作り方」って感じの話をする予定で、その一環。 実際のセッション内容としては、「うかつに独自機能とか考えるんじゃないぞ」という予防接種が半分、「それでもコンパイラーに手を入れたいっていうならデモくらい見せるけど…」みたいな内容が半分の予定で、この後半の内容になります。

どうせやるなら、多少芽のある話をやろうかと思った結果がサロゲートペア対応。 非ASCII文字の話は、日本人か中国人が率先してやらないと全然実装されないですからねぇ。

で、久々にUnicodeの状況とか、C#以外も含めたプログラミング言語の仕様とかを漁ってみてた結果が本日のブログ。 「絵文字はともかく、𩸽くらいは使えてしかるべきよなぁ」とか考えてたら、 ちょうど同じタイミングでRoslynリポジトリ内でその話題(ただし中国語)が出てたり。 Swiftがどこまで緩いの調べてたら想像をはるかに超えてやんちゃだったり。

ピックアップ Roslyn 9/6: defaultの型推論

$
0
0

なんか1個、気になったプルリクが。

default(T)を、左辺から型が推論できる場合にはdefaultだけで書けるようにしようっていう作業。

ちょっと前に提案があって、前向きっぽい感じではあったんですけど、なんで今実作業やってんだろう。 今からC# 7リリース辺りまでは、C# 7に入る予定の機能のバグ修正・改善ばっかりになると思ってたんですけども。 これくらいの修正ならさっと実装してC# 7に入れれそう?

まあ、これに限らず、今後、左辺から右辺の型推論は今後増えそうな感じです。

左辺と右辺

簡単に言葉の説明をしておくと、 左辺とか右辺ってのは、代入文の = の左右から来てる言葉。 x = yだったら、xが左辺でyが右辺です。

転じて、値を受け取る側が左辺で、値を渡す側が右辺。 変数宣言だと、宣言する変数が左辺で、初期値として渡す値が右辺です。 見た目は左右に分かれませんが、メソッド呼びだしなんかでも、値を受け取る側(仮引数)が左辺で値を渡す側(実引数)が右辺と考えられます。

右辺から左辺の型推論

C#の型推論って、右辺から左辺の推論が多いです。varしかり、ジェネリック型引数しかり。

以下のような感じ。

class Program
{
    static void Main()
    {
        var x = 1; // 1 からの推論で、int x = 1; 扱い。
        F(2); // 2 からの推論で、F<int>(2); 扱い
    }

    static void F<T>(T x) => System.Console.WriteLine(typeof(T).Name);
}

左辺から右辺の型推論

逆に左辺から右辺の型推論なのは、現状ではラムダ式くらい。

using System;

class Program
{
    static void Main()
    {
        Func<int, object> f = x => x.ToString(); // f の型が<int, string>なので、(int x) => (object)x.ToString() 扱い
        X(x => x.ToString()); // Xの引数が<int, string>なので、(int x) => (object)x.ToString() 扱い
    }

    static void X(Func<int, object> f) { }
}

で、今、defaultnewの型推論を増やしたいという話が出ています。特に、varを使えないフィールドやプロパティに対する初期化子で有効そう。

class Sample<T>
    where T : new()
{
    static T newValue = new();       // new T() の T を省略
    static T defaultValue = default; // default(T) の T を省略

    static void F(T x = default) // default(T) の T を省略
    {
    }
}

で、なんか、defaultの方はプロトタイプ実装が始まったと。 ちなみに、newの方は実装の兆し全然なし。 C#チーム的に前向きに検討したいとは言ってるんですけども、実装コストはそこそこ高そう。 引数ありで、new(1, 2)みたいなのもできる予定です。

補足: new()default

余談になりますが、「nullじゃダメなの?」とか、「new()だけじゃダメなの?」とか言ってる人もまあ、います。 C#に不慣れな人には結構わかりにくいですよね…

以下のような差があるので、いずれも必要です。

  • new T()だとインスタンスが作られる。nullにはならない
  • nullは参照型にしか入らない。ジェネリック型引数Tとかだと、Tが参照型なのか値型なのか確定しないからnullを使えない
    • default(T)だと、参照型ならnull、値型なら「全メンバー0/null初期化」
  • 現状、値型であればnew T()default(T)はどちらも「全メンバー0/null初期化」で、同じものになるけど、将来的に変えたい
    • 構造体にも引数なしのコンストラクターを定義できるようにしたい
    • new T()は引数なしのコンストラクター呼び出し
    • default(T)は「全メンバー0/null初期化」

ちなみに、構造体の引数なしコンストラクターの話は、一度C# 6に入りかかったんですけど、 Activator.CreateInstanceの内部に「構造体のnew T()default(T)は同じ意味」っていう前提で最適化を掛けちゃってるコードがあるらしくて、 これを修正してもらわないとダメってことでrevertされました。

参考:

ピックアップ Roslyn 9/9

$
0
0

タプルの分解の最適化

タプル構築と同時に分解(要するに多値代入というか)する場合は、タプルを作らない最適化かけたいって。

要するに、例えば以下みたいなswapコード書いたとして、

var x = 1;
var y = 2;
(x, y) = (y, x);

今だとこうなる。

var x = 1;
var y = 2;
var v = new ValueTuple<int, int>(x, y);
x = v.Item1;
y = v.Item2;

これ、実のところこの時点でタプルの特別扱いが掛かってます。 分解の仕様上は、以下のようなコードになるべきところを、タプルでまでそれをやるのは無駄だってことで、Item1Item2の直参照に。

var x = 1;
var y = 2;
var v = new ValueTuple<int, int>(x, y);
v.Deconstruct(out x, out y);

Deconstructを最適化で消すんだったら、new ValueTupleの方も消していいんじゃない?という感じ。 なのでたぶん、以下のような感じのコードに展開されるのではないかと。 ほぼ、普通のswapコードに。

var x = 1;
var y = 2;
var tempX = x;
var tempY = y;
x = tempY;
y = tempX;

Recap of async streams

Lucian (C#チーム非同期担当の開発者)が夏休みから帰ってきて、現状のまとめを投稿。

まあ、本当にまとめのみ。基本、過去にブログで取り上げた内容なので詳細割愛。

あと、Stephen (.NETチーム内の人。活動を見るに.NETのパフォーマンス改善系の作業をしてるっぽい)がパフォーマンスがらみの懸念を投稿。

現状のIEnumerableの問題でもあるものの、MoveNextCurrentにメソッドが分かれてると、スレッド安全に作りようがなくて困る。 IAsyncEnumerableも同じような構造で作られると、async streamをチャネル的に(Go言語のgoroutine/channelみたいに)使いにくい/使えないので、スレッド安全にできる実装を考えてほしいとのこと。

.NET Fringe Japan 2016で登壇してきました

$
0
0

.NET Fringe Japan 2016やりました。

.NET Fringeっていう本家イベント(アメリカのポートランドで開催)があって、本家の人からそれの日本版をやりませんか?という話が来てそれに応じた形での開催だったりします。

アメリカのイベントって朝からがっつりやるんですよね。本家.NET Fringeは9時開始。日本でもたまにはがっつりやるかって感じで10時開始になりました。 まあ、逆に、アメリカのイベントは17時前に終わるんですが…10時~20時半はさすがにきつかったかなぁ。という感じも。

C#言語機能の作り方

で、僕のセッション。

作り方とかいいつつ、むしろ全力で止めにかかってる予防接種セッション。

⚠使用上の注意

  • 「独自に言語を作ろう・拡張しよう」みたいな話はハシカみたいなもんです
    • 早めに免疫つけといた方がいい
    • 歳食ってから罹ると重篤なことが

という。

とはいえ、やるだけ無駄とかやっちゃダメって話ではなく。むしろ、早めに罹っとけって話です。予防接種も、弱毒化したウィルスにあらかじめ罹ることで免疫付ける手法ですしね。

良し悪しとかどのくらい大変かとかはある程度の勘所がないと、いざ責任ある立場になって大きなことやれるようになった時に下手打つと命に係わるぞと。 この辺りは別にプログラミング言語作りに限らず一般論ですね。 いろいろ試しにやってみて、いろいろ勘をつかんでおくのは大事です。

しいて言うなら、なんかプログラミング言語作りでは、この「どのくらい大変か」を過少見積もりして重篤化する人がなんか一定割合いるっぽく、予防接種スライド作っとくか、と。 わざわざ大変なものに手を出さなくても罹った気分になっておくためのものが、僕が今回やったようなセッションですかね。

「ハシカみたいなもんだから」ってセリフ、いろんな方面に結構ぐさぐさ刺さったみたいですがw @kekyo さん、「新しい opcode 作るよ」って話(スライドニコ生放送の8時間目くらいから)をしてて「ハシカですね…」って言ってましたけども、 これは割かし良い具合にワクチン(弱毒化したウィルス)なんじゃないですかね。 最小限に手を入れてみるってのは勘所つかむためのかなり良い題材。 まあ、セッションの後、「予防接種ってのはそれなりに流行る可能性のあるものだから必要なんであって、これはハシカと言えるのか(そんなにやりたがる人いるのか)」とかいう鋭い突っ込みもあったんですが。

あと、試しにやってみているだけでも、@neueccさん(スライドニコ生放送の7時間目くらいから)が言ってたみたいに、公開するつもりでやる/実際に公開する方が身につく度合いが高いと思います。 まず、人に見せる/人に見られることを意識した方が断然勉強になります。 それにどうせお試しで実用性皆無なものであっても、公開していると「予防接種済み」の査証になりますし。結構価値あるんじゃないかと。

ピックアップRoslyn 10/30: 分解、宣言式、ワイルドカードの内部仕様変更

$
0
0

Roslynリポジトリのブランチやマイルストーンの動きを見るに、次のVisual Studio 15 PreviewはもうRCなようですが。 RC (リリース候補)の段階で大きな変更をするわけもなく、最近のC#チームの動きは大体「ただひたすらバグ修正」なわけです。

そんな中、仕様変更の話が。

まあ、内部挙動的な話なので、C# 7の見栄え上はほとんど変わらないはず。分解出力変数宣言で、(A < B, C > D)みたいな解釈の難しい書き方がどう解釈されるか、みたいな細かい挙動が変わるだけです。

ただ、プログラミング言語としてのC# 7的には大した変化じゃないですが、CodeAnalysis API(いわゆる、Compiler as a Service。C#コンパイラーの中身を誰でも見れるようにするって意味でのC# 7 API)的にはもちろん目立つ変更になります。要は、SyntaxNodeの種類が変わります。

ワイルドカード

ワイルドカード(wildcard)は、仮引数、分解、出力変数などの値を受け取る側で、別にその値を使わない場合に無視する記法がほしいというやつ。 以下の、*みたいなやつ。

using System;
using System.Diagnostics;

class Base
{
    public event EventHandler<string> E;
    protected virtual void M(int x, int y) { }
    public void Deconstruct(out int x, out int y)
    {
        x = 1;
        y = 2;
    }
}

class X : Base
{
    public X()
    {
        E += (*, *) => Debug.WriteLine("log message");

        Deconstruct(out *, out var y);
    }

    protected override void M(int *, int *)
    {
    }
}

案としては _*? なんかが出ていました。これまで、* が最有力だったんですが、_ の方にするかも、とのこと。

  • _
    • 今現在、_ 1文字はC#の識別子として有効なものなので、_ をワイルドカードに使うのは破壊的変更になりかねない
    • 一応、_を使っている場所があれば識別子として、使ってなければワイルドカードとして扱うみたいな処理を入れれば破壊的変更にはならない
    • ただ、それやるとユーザー的には混乱しないかという話はある。まあ、現状すでに、多くのユーザーが_を「使わない引数」として使ってるので大丈夫ではないか
  • *?
    • 破壊的変更は起こしにくい
    • ただ、int *みたいな書き方が、「int型をワイルドカードで無視」なのか、「ポインター型int*」なのかという不明瞭さがある
    • ?も、null許容型のint?との不明瞭さがある
    • この不明瞭さの解決の方が、_の方の問題よりも難しそうという意見に傾いてきた

宣言式

元々の計画としては、式の途中のどこにでも変数宣言を書ける「宣言式」(declaration expressions)という機能が検討されていました。 現在でも、将来的には宣言式を追加する見込みは非常に高いですが、C# 7では入りません。

一方、C# 7に入る出力変数宣言は、この宣言式のサブセットみたいなものです。 なので、内部的には(CodeAnalysis API的には)、出力変数宣言は最初から「declaration expression」という名前になっています。

分解

一方、現状では、分解は、分解専用のステートメントとして実装されています。 これが、将来的に宣言式を入れたときに邪魔になりそうなわけです。 分解も、宣言式を使って表現した方がいいだろうということで、C# 8の際に破壊的変更にならないように、今から変えてしまいたいとのこと。

例えば、現状、C# 7では書けないものの、将来的には認められそうな書き方として、以下のようなものがあります。

var tuple = (1, 2);

// タプル構築 + 分解(しかも、新しい変数 x, y を宣言しながら)
var t = (int x, int y) = tuple;

この、int xint yの部分は、まさに、宣言式であるべきだろうという話です。

付随して、タプル、分解、出力変数宣言などでモデルをそろえておかないと、細かい挙動で差が出て気持ち悪いというのもあります。 例えば、以下のようなコードはどう評価されるべきか。

(A < B, C > D) = F(A < B, C > D = value);

以下の2つの解釈ができます。

  • A < BC > Dという2つの式を持つ
  • A<B, C>というジェネリック型の変数Dを宣言式で宣言している

これが、現状の、宣言式と分解で異なる実装をしてる状態だと、それぞれ逆の解釈になるそうで、まずい。 今のうちに統一挙動にしてしまいたいということに。

foreach の変更

C# 7的に、分解が書けるのは以下の3カ所。

  • 分解ステートメント: var (x, y) = t;
  • forステートメント: for (var (x, y) = t; cond; iter)
  • foreachステートメント: foreach (var (x, y) in list)

このうち、forは現在の文法構造で、この「分解も宣言式で定義する」という変更に耐えれるそうです。

一方、foreachには変更が必要そう。

まとめ

宣言式とワイルドカードっていう、C# 7では入らない/入らなさそうな機能に関する話ではあるけども、 C# 7の時点からちゃんと考えて作っておかないと後から困りそう。 なので今から、宣言式とワイルドカードを前提とした作りにしておきたい、という話。

Roslynに提案issueを立てた話: nullの扱いに関して

$
0
0

MVP Global Summit (グロサミ)に行ってきてたわけですが。

なんか、行きの飛行機内で思いついてしまって、そのまま向こうで頑張って issue 投稿、 せっかくだからグロサミ中に Mads (C# のPM)を捕まえて「こんな問題見つけちゃって、昨日ちょうどissue立てたんだけどどうしよう?」みたいな話を振ってきたり。

(先に具体例を書いておけば、どれだけ英語がつたなくても結構意図は伝えられる。)

(ちなみに、元々は帰国後にゆっくりページ書くつもりだったんだけど、なんかグロサミ参加の妙なテンションに任せて、 kekyoさん藤原さん室星さんとかと部屋の飲みの最中にこの方々も巻き込んでissue書きました。)

勢いで立てたものなので整理できてないんですが… たぶん2パートに分けた方が良さそう。 以下、改めて要約した内容(英語に訳して再投稿予定)。

2つの提案

nullの取り扱いと関連して、以下の2つを提案する。

  • 非default値型: default(T)の状態を認めない値型が必要ではないか
  • nullable-like型: 参照型とNullable<T>以外にも、?.??が使える型をユーザー定義できるようにすべきではないか

非default値型

Method Contracts、特に参照型の非null保証を入れようと思うと、確実な初期化処理が必須になる。

しかし、構造体の場合、defautl(T)など、既定値によって初期化処理を通らない「0/nullクリア」が発生する。 既定値の「0/nullクリア」のせいで、Contractsや非null保証のフロー解析が狂う可能性がある。

例1: 非null保証

パフォーマンスのために、参照型を1つだけ持つようなラッパーを構造体で作ることがある。 (例: ImmutableArray、) 今、パフォーマンスはC#の1つの大きなテーマであり、こういうケースは今後より一層増えるだろう。

例として以下のような構造体を考える。

struct Wrapper<T> where T : class
{
    public T Value { get; }
    public Wrapper(T value)
    {
        Value = value ?? throw new ArgumentNullException(nameof(value));
    }
}

レコード型非null保証が入れば、単に以下のように書けるだろう。

struct Wrapper<T>(T Value) whereT : class

単にTと書けば非nullとなり、nullを受け付けたければT?と書くようになる。 問題は、この構造体をdefault(Wrapper<T>)で作ると、T Value (本来は非nullであるはず)がnullになってしまうことである。

例2: 値の制約付きの構造体

以下のような、値に制約の入った構造体を考える。この例は、正の数しか受け取れない数値型である。

struct PositiveInt
{
    public int Value { get; }
    public PositiveInt(int value)
    {
        if (value <= 0) throw new ArgumentOutOfRangeException(nameof(value));
        Value = value;
    }
}

C#にレコード型Method Contractsが入ると、この構造体はおそらく以下のように書ける。

struct PositiveInt(int Value) requires Value > 0;

これでValueプロパティが常に0より大きいことが保証できているように見えるが、default(PositiveInt)のせいで、Valueに0が入ることがあり得る。この値は無効なはずである。

提案: 非defaultフロー解析

現在提案されている参照型の非null保証は、フロー解析に基づいている。 値型が既定値でないことも、同じフロー解析で行えるはずである。

そこで、non-nullable reference typesに対して、non-defaultable value typesを提案したい。 何らかのアノテーション、例えば[NonDefault]属性を付けた構造体は既定値を取ってはいけないとするのはどうだろうか。

[NonDefault]
struct Wrapper<T>(T Value) whereT : class

[NonDefault]
struct PositiveInt(int Value) requires Value > 0;

このとき、non-nullable reference typesに倣って、以下のように警告を出す。

PositiveInt x = default(PositiveInt); // warning
PositiveInt? y = default(PositiveInt); // OK
PositiveInt z = y; // warning
PositiveInt w = y ?? new PositiveInt(1); // OK

non-defaultable value typesに対するT?Nullable<T>を必要としない。 何故なら、default(T)は無効であり、x.HasValueを確かめなくても、x == default(T)で値を持っていないことが確認できるからである。 non-defaultable value typesに対してはnulldefault(T)と同一視してもいいかもしれない。

また、通常の構造体の中では、non-nullable reference typesのメンバーを持てないようにすべきだろう。 non-nullable reference typesを持てるのは、参照型か、non-defaultable value typesのみである。

nullable-like型

現在、参照型とNullable<T>構造体はC#コンパイラーによって特別な地位を与えられている。 すなわち、null条件演算子(?.)とnull合体演算子(??)の利用である。

しかし、これらの型以外にも、無効なインスタンスを?.??で伝搬/差し替えしたいことがある。

この挙動はmonad的であり、LINQTask-likeを使って無理やり解決しようとしている例もある。 しかし、悪用・乱用の類であり、決して読みやすいコードにはならないだろう。 無効なインスタンスの伝搬/差し替えは、やはり、?.??を使うべきである。

例1: UnityEngine.Object

Unityのゲーム中のオブジェクトの共通基底クラスとなるUnityEngine.Objectoperator ==をオーバーロードしていて、オブジェクトが持っているネイティブ リソースがすでに破棄されているとき、オブジェクトをnull扱いする(x == nullが真になる)。

しかし、参照型に対する?.??では、オーバーロードした==は呼ばれない(brtrue命令によるnullチェックに展開される)。 そのため、以下のように、?.を使ったコードが正しく動かない。

int? X(UnityEngine.Object obj)
{
    // OK
    if (obj == null) return null;
    return obj.GetInstanceID();
}

// runtime exception
int? Y(UnityEngine.Object obj) => obj?.GetInstanceID();

これまではUnityがC# 3.0にしか対応していなかったので問題にならなかったが、 Unity 5.5でC# 6.0に対応しようとしている。 この?.の挙動がはまるポイントになるだろう。

例2: Expected

無効な値としてnullを使うことを嫌う人が一定数いるが、その理由の1つが、「なぜ無効な値を返す必要があったのか」という原因に関する情報が消えることである。 そのため、Nullable<T>の代わりに、Tと例外のunion型にあたるExpected<T>のような型を作って使おうとする人がいる。 例えば、C++でそういう動きがみられる

struct Expected<T>
{
    public T Value { get; }
    public Exception Exception { get; }
}

もしC#でもそういう型を作るのであれば、?.を使った例外の伝搬や、??を使った例外からの復帰があってもいいのではないだろうか。

Expected<string> x = new Expected<string>(new Exception());
Expected<int> y = x?.Length;
string z = x ?? "";

提案: ユーザー定義のnullable-like型

所定のパターンを実装した型であれば?.および??を使えるようにすることを提案する。 Task-likeに倣ってこれをnullable-likeと呼ぼう。

例えば、以下のようなパターンはどうだろうか。

struct NullableLike<T>
{
    public T Value { get; }
    public bool HasValue { get; }
    // propagate a valid value
    public NullableLike<U> Propagate<U>(U value);
    // propagate an invalid value
    public NullableLike<T> Propagate();
}

これで、先ほどのExpected<T>の例であれば、以下のように展開する。

Expected<string> x = new Expected<string>(new Exception());
Expected<int> y = x.HasValue ? x.Propagate(x.Value.Length) : x.Propagate<int>();
string z = x.HasValue ? x.Value : "";

ちなみに、このパターンに沿ったExpected<T>の実装は以下のようになる。

struct Expected<T>
{
    public T Value { get; }
    public Exception Exception { get; }

    public Expected(T value)
    {
        Value = value;
        Exception = null;
    }
    public Expected(Exception exception)
    {
        Value = default(T);
        Exception = exception;
    }

    public bool HasValue => Exception == null;
    public Expected<U> Propagate<U>() => new Expected<U>(Exception);
    public Expected<U> Propagate<U>(U value) => new Expected<U>(value);
}

Visual Studio 2017 Release Candidate

$
0
0

来てた。

(Connect前のリーク騒動ありましたが… Connectをもって)

もう今、クロスプラットフォームを推す状況なので、Visual Studio for Mac (Xamarin Studioのリブランディング)と、モバイル開発、Azure辺りなんかが前面に出てますが。

とりあえず、「C#な人」的に言えることは、RCになって、C# 7として予定されている機能は全部載ったはず。うちのサイトのC# 7の新機能のページに書かれている内容は全部試せるようになっているはずです。

(ソースコード取ってきてビルドして最新の状況を試していたりすると、1か月前くらいからC# 7の全機能使えていたので、自分の中ではそこまでニュース性が大きくなかったりはするんですが。やっぱりインストーラーで入れれる製品で試せるようになると嬉しい。)

ちなみに… リポジトリのマイルストーンを覗いてしまうと、 この後RC 2, RC 3が控えていることが見えてしまっているわけですが。 RC 3かー… たぶんまだ結構変更入るんだろうなー… とはいえ、今回のやつでRC (リリース候補)の名前を冠してgo-live (自己責任でプロダクション環境で使ってOK)になりました。 使うか、仕事で…

C#小ネタ集: C#をWeb上で試す

$
0
0

小ネタ

ここのところ、twitterで脊髄反射的に飛びついたきり、gistとかに書き散らかして終わりにしちゃってるネタがちらほら溜まってきたので、ここらでブログかしようかなと思います。 C#小ネタ集とか銘打って。

C#小ネタ集と言いつつ、あんまりC#が関係ない物もありますが。 結構溜まってるんで、数日は連続で書くことになるかなと思われます。

Web上で試したい

初日のネタは、C#をWeb上で触ってみようという話。

C#の入門記事とか書いてるとよく言われ続けていたのが、「Visual Studioをインストールするのがめんどくさい」って話です。 まあ、めんどくさいですよね。

そこで、今はC#にもWeb上でさらっと試すことができる場所があるので、今日はその紹介をします。

もちろん、じっくり腰を据えてコードを書くときにはもちろんVisual Studioを起動しといた方が楽なんですが。 (日々の仕事がC#とかだったりするとどうせ常時Visual Studioが起動してる状態にあるので別に立ち上げも大した面倒はありません。) とはいえ、入門向けにいきなり「Visual Studioを入れてください」は、今時ちょっとないよなぁ。

こういうのは、もうVisual Studioなどの統合開発環境に馴れてる人は忘れてる感覚です。 でも、自分が普段使わないプログラミング言語をちょっと試してみたいときのことを想像してみてください。 実務で使うわけでもなし、本腰入れてがっつり勉強するでもなし、ほんのちょっとしたネタを確認したいだけ。 そのためにわざわざ環境構築をしたいですかと言われると、まあ僕だったら嫌です。 実際、GoとかSwiftとかの挙動を見たいときは、まずWeb上で試します。 コピペ程度のコードしか書けませんが、試してみるだけならそれで十分なことが多いです。

C#をWeb上で

ということで、まずは公式サイト。

前にも1度ブログで取り上げていますが、 今は、公式サイト(dot.net!)で、試しにその場でC#を書いて実行結果が見れる機能が付いています。

dot.net内のStart codingコーナー

まあもちろん、paiza.ioとかideoneみたいなコード共有サイトを使ってもいいんですけども。 やっぱり公式サイトでやってないと、今時不格好ですよね… ということで、dot.netというドメイン取ったのと、その中でオンラインC#実行ができるようになってたことに気づいたときはうれしかったものです。

コード共有しつつ、C#をWeb上で

dot.netのオンライン エディターは保存してURLで他の人と共有したりはできません。

最近だと、適当に書き捨てのつもりで書いたコードも、 Gistとかに置いておいて、twitterとかで垂れ流したりもします。 そういう用途では、さすがに公式サイトのオンライン エディターは使えません。

そこで、ちょうどこれをやってくれるサイトがあります。 要するに、Gistと連携して、Gistに保存したC#コードを編集、実行してくれるサイト。 そういうサイトも作ってくている方がいらっしゃいます。

最新のC#をWeb上で

dot.net上にあるオンライン エディター中のC#は、「リリースされている最新版」です。 当然と言えば当然。 現時点ではC# 6ということになります。

dot.net内のStart codingはC# 6

GitHubリポジトリで開発途中の最新版を試したければ、まあ、通常、ソースコードをcloneしてきて、ビルドして、実行してみたり、そのデイリー ビルド結果を取ってきて、Visual Studio拡張などをインストールしてみる必要があります。

ですがここで、これも前にもブログで触れましたが、オンラインで割と最近のバージョンのC#を試せるサイトを作ってくれている人がいます。

RoslynのGitHubリポジトリからいろんなブランチを定期的に取ってきて、それぞれでどういう挙動をしているか確認できるサイトです。 実行結果というか、コンパイル結果のILとか、それを古いバージョンのC#で書くにはどうすればいいかという逆コンパイル結果を表示してくれます。

TryRoslyn

GitHub上のC#コードを解析

Visual Studioが常時起動しているとしても、 GitHubで見かけたC#コードを、わざわざCloneしてきて手元で見ようとはなかなか思わないですよね。 そのままブラウザーで読みたいです。

その一方で、GitHub上でコードを読んでいると、 クラス名などのシンタックス ハイライトがいまいちだったり、 「定義に移動」したくなるけど、当然GitHub上でそんなことできるはずなくイラッとしたりします。

ブラウザー中で、正しくハイライトされ、「定義に移動」とか「すべての参照を検索」ができるページがほしいです。

マイクロソフトが公式に提供している参照コードがちょうどそんな感じです。

これらのサイト、Roslynを使ったWebページ生成ツールで作ってるそうです。 ツール自体もオープンソースになっていて、以下のリポジトリで見れます。

で、要は、このツールを任意のGitHubリポジトリに対してかけてくれるサービスがあるとすごく幸せなわけですが… これがまた、そういうサービスを作ってくれてる大変親切な方がいらっしゃいます。

小ネタ コンパイル結果を覗いてみよう

$
0
0

めったにはないんですが、パフォーマンス チューニングとかを始めると、C#のコンパイル結果がどうなっているかを覗きたくなることがあります。C#の場合は、C#コード → IL (.NETの中間コード) → ネイティブ コードという2段階の変換が掛かります。

ということで、その極々まれにやりたくなることをやってみましょう。ILとネイティブ コードをそれぞれ覗いてみます。

例として、以下のようなC#コードを考えます。単純にvirtualなメソッドを呼び出すだけのコードです。主に、Mainメソッドの中身を見ていきます。

using System;

class Base { public virtual void M() => Console.WriteLine("Base.M"); }
class Derived : Base { public override void M() => Console.WriteLine("Derived.M"); }

class Program
{
    static void Main()
    {
        Base b = new Derived();
        b.M();
    }
}

IL逆アセンブル

まずはILがどうなっているかを覗いてみましょう。ildasm というツールを使います。ildasmは、以下の場所にあります。

  • "C:\Program Files (x86)\Microsoft SDKs\Windows\v10.0A\bin\NETFX 4.6.1 Tools\x64\ildasm.exe"

ちなみに、ildasmは IL disassembler (IL逆アセンブラー)の略です。

10.0Aや4.6.1という数字の部分はバージョンによって変わります。あと、x64フォルダーの中と、直上の両方にildasm.exeがあります。ildasmはバージョン違い、x86かx64か違いでいくつか見つかりますが、まあ、大体どれも同じです。

これを使って先ほどのC#コードのコンパイル結果を覗いてみると、Mainメソッドは以下のようになっています。

.method private hidebysig static void  Main() cil managed
{
  .entrypoint
  // コード サイズ       11 (0xb)
  .maxstack  8
  IL_0000:  newobj     instance void Derived::.ctor()
  IL_0005:  callvirt   instance void Base::M()
  IL_000a:  ret
} // end of method Program::Main

ILは、インスタンス生成や仮想メソッド呼び出し用の命令を持っています。

  • newobj: インスタンス生成
    • new objectの略
    • 引数には、コンストラクターを参照するためのIDを渡します
    • 命令1バイト + オペランド4バイト
  • callvirt: 仮想メソッド呼び出し
    • call virtualの略
    • 引数には、メソッドを参照するためのIDを渡します
    • 命令1バイト + オペランド4バイト
  • ret: メソッドを抜ける
    • return

ILは、アセンブリ言語ではあるものの、かなり高級言語なことがわかります。仮想メソッド呼び出しのようなオブジェクト指向プログラミングの基本操作が1バイト・1命令で書けるので、ILにコンパイルされた命令列はサイズがかなり小さくなります。

JIT結果の逆アセンブル

ILからネイティブ コードへの変換はJIT (Just-in-Time)、すなわち実行時に行われます。そこで、ILから生成されるネイティブ コードを覗くためには、実際に実行してみる必要があります。Visual Studioには、実行時にJIT生成されたネイティブ コードを覗く機能が備わっているので、これを使って覗いてみましょう。

場所は以下の通りです。メニューを [Debug][Windows][Disassembly] とたどったところにあります。ただし、このウィンドウは、デバッグ実行中しか表示されません。

クイック起動から逆アセンブルを表示

メニューから逆アセンブルを表示

これで、先ほどのプログラムを実行して、Mainメソッドの辺りでブレイクして中身を見てみると、以下のような状態になります。

逆アセンブル結果

ブレイク ポイント

この画面で、黄色い矢印は、ブレイク ポイントを仕掛けて止めた位置になります。ネイティブ コードの中から、コードを読み進めつつ自分が調べたいコードがどこかを探し出すのは至難のわざなので、こうやって、見たい場所にブレイク ポイントを仕掛けて止めるのがいいでしょう。

ただ、Releaseビルド(というか、最適化オプションを付けてコンパイル)すると、ブレイク ポイントの位置がずれて、自分が狙った位置でプログラムが止まってくれなくなります。最適化された結果のネイティブ コードを確認したいときにはちょっと困ります。

そこで、ちゃんと狙った場所でプログラムを止めるためには、Debuggerクラス(System.Diagnostics名前空間)のBreakメソッドを使うといいでしょう。このメソッドを書いた位置で必ずプログラムが止まってくれます。

        System.Diagnostics.Debugger.Break();
        Base b = new Derived();
        b.M();
        System.Diagnostics.Debugger.Break();

生成されたネイティブ コード

さて、先ほどのMainメソッドからどういうネイティブ コードが生成されるかを改めてみてみましょう。 x64環境で実行すると以下のようになります。

01960450  mov         ecx,1874E58h  
01960455  call        018630F4  
0196045A  mov         ecx,eax  
0196045C  mov         eax,dword ptr [ecx]  
0196045E  mov         eax,dword ptr [eax+28h]  
01960461  call        dword ptr [eax+10h]  

それぞれの命令がどういう意味かというと、以下の通りです。

Mainメソッドから生成されるネイティブ コード

仮想メソッドの仕組みが分かってもらえるでしょうか。2段ほど間接参照が挟まります。

この例でいうと、コンストラクター呼び出しは通常のメソッド呼び出しになるわけですが、こちらは最初からメソッドのアドレスがわかっているので直接呼び出しています(01960455の行)。

一方、メソッドMの呼び出しは、オブジェクトのヘッダー → 型情報テーブル → メソッドのアドレスと参照してからのメソッド呼び出しになります(0196045A~01960461の行)。

実行が必要

ここで話したように、JIT結果を覗くためには実際に実行してみる必要があるわけですが…

開発機上での実行だと、Visual Studio = Windowsデスクトップ = Intel CPUなわけで、x86かx64のコードを見ることになります。 一応、UWPプロジェクトを作って、ARMなWindows 10 Mobile実機をPCにつないで実行することで、ARMコードの逆アセンブルも見れることは確認しています(もっと楽な方法ないかな?)。Xamarin.Androidではダメでした。

小ネタ corefxlabとSystem.Memory

$
0
0

corefxlab

.NETチームは以下のリポジトリの1つに、corefxlabというものがあります。 名前通り、corefx (.NET の標準ライブラリ)入りを目指して試験的なコードを入れておく場所で、 安定したものからcorefxに移転になったりします。

corefxの方にいきなり入らず、一度「lab」を経ているわけで、 それなりに「これまでの.NETではあまり取り組んでなくてこなれていないもの」が含まれています。

その最たるものがパフォーマンス。 .NETは、これまでどちらかというと生産性や安全性が優先だったわけで、 「そんなコード書いてまでパフォーマンス求める?」みたいなものは少なかったんですが、 最近ではそういうコードが増えてきています。

もちろんこれまでもパフォーマンスは必要だったわけですが、 そういう場合はすぐにネイティブ コードに逃げていました。 .NETはネイティブ コードとの相互運用もしっかりしているわけで、それも1つの解決策です。

ですが、.NETもこなれてきて、.NET単体でのパフォーマンスに目を向ける余裕も出てきました。 それになんだかんだ言って、ネイティブ側、.NET側の双方で配慮しないとパフォーマンスが出ないことも多々あります。 それに取り組んでいるのがcorefxlabです。

といっても、そういう「そんなの書いてまで」的なコードは、型の中に閉じ込めています。 corefxlab中のライブラリの中身は結構な頑張り具合になっていますが、 単にビルド済みのライブラリを使うだけならそこまで変なライブラリでもないでしょう。

System.Memory (旧 System.Slices)

そんなcorefxlab出身のライブラリの中から例として1つのライブラリを紹介します。

昔はSystem.Slicesという名前でcorefxlabにありました。 というか、まあ、さらに追加で試験的なコードを書く場所として、corefxlab側にもSystem.Slicesが残ってはいます。(本当にやばそうなコードはcorefxlab側に取り残されています。Castとか。)

Slices (切れ端、断片)という名前は歴史的経緯のようです。 現在Span (区間)と呼ばれている構造体も、昔はSliceという名前だったみたいです。 今となってはどこにもSliceという名前は残っていないし、corefxに移る際にはSystem.Memoryというパッケージ名に変わりました。

Span<T>構造体の持つ機能を単純化して書くと、以下のようなコードと同類の機能を提供するものです。

struct Span
{
    public byte[] _data;
    public int _offset;
    // 本来は範囲チェックも必要なので length も必要

    public Span(byte[] data, int offset = 0)
    {
        _data = data;
        _offset = offset;
    }

    // C# 7登場に合わせて ref uint に変更予定
    public unsafe uint this[int index]
    {
        get
        {
            fixed (byte* p = _data)
            {
                var q = (uint*)(p + _offset);
                return q[index];
            }
        }
        set
        {
            fixed (byte* p = _data)
            {
                var q = (uint*)(p + _offset);
                q[index] = value;
            }
        }
    }

    public Span Slice(int startIndex) => new Span(_data, startIndex + _offset);
}

この単純化して示せている部分で言うと、Spanの機能は以下の通りです。

  • 配列の一部分だけを参照する
  • 元の型(この例だとbyte)とは違う型(同 int)の値を、配列に対して直接読み書きする

これに対してさらに、本物の Span<T> は以下のようなものです。

  • 型を自由に変えれる
    • 任意の型の配列を受け取れる
      • ジェネリックを使える
    • Cast<T, U>()で、他の型に強制変換できる
      • T型の配列に、Uの値を直接書き込める
  • ヒープ(配列)、スタック(stackalloc)、ネイティブ バッファー(ポインター)を統一的に扱える
  • fixed (それなりに負担がある操作)が不要でパフォーマンスがいい

たったこれだけのことなんですが、通常のC#では書けなかったりします(後述)。

まあ、とにかく、以下の例のようなことが、そこそこ高パフォーマンスで実行できます。

using System;

struct Data
{
    public byte A;
    public byte B;
    public short C;
    public int D;
}

class Program
{
    static void Main(string[] args)
    {
        // データ書き込み用のバッファーを byte 配列で用意
        var data = new byte[24];

        // byte のまま書き込み
        var b = new Span<byte>(data);
        b[0] = 1;
        b[1] = 2;

        // short を書き込み
        var s = b.Slice(2).Cast<byte, ushort>();
        s[0] = 0x0403;
        s[1] = 0x0605;
        s[2] = 0x0807;

        // int
        var i = s.Slice(3).Cast<ushort, uint>();
        i[0] = 0x0C0B0A09;
        i[1] = 0x100F0E0D;

        // 構造体
        var l = i.Slice(2).Cast<uint, Data>();
        l[0] = new Data
        {
            A = 0x11,
            B = 0x12,
            C = 0x1413,
            D = 0x18171615,
        };

        // 1~24が並ぶはず
        Console.WriteLine(string.Join(" ", data));
    }
}

構造体の読み書き

特に、構造体を直接読み書きできるのは、使い方次第では非常に強力です。 構造体内部のレイアウトを気にしなければ、きわめて低コストなシリアライズが実現できる可能性があります。 (データ構造を値型のみの固定長にしないといけなかったり、 文字列を直接持てなかったり、 ARMで使うならバイト境界をまたげないはずだし、制限はきつい。 可能性があると言っても設計はそこそこ難しそう。)

レイアウトに関しては、以下の点に注意が必要です。

  • エンディアン
    • サーバーもクライアントもIntel系かARM系のCPUを使う状況下ではリトル エンディアン固定で考えても特に問題は出ない
    • .NETが移植されてる先の大部分がリトル エンディアン(XBox?知らない子ですね…)
    • というか、ビッグ エンディアン自体がそこまでもう残ってない
      • 「ネットワーク エンディアン」とか言われてTCPとかの世代ではビッグ エンディアンが使われてるけども、最近だとQUICとかはリトル エンディアン
      • XBoxもXBox Oneで、PSもPS4でリトル エンディアンなCPUに変更
      • Java がビッグ エンディアンなのが唯一つらいところ…
  • バイト境界
    • 32ビットCPUか64ビットCPUかで4バイト境界か8バイト境界かが変わるので注意
    • 心配ならStructLayout属性を付ければどの環境でも同じレイアウトにできる

C# で書けないコード

「通常の C# では書けない」と言いましたが、理由は、ポインターに対する制限が強いせい:

  • ジェネリック型引数に対してポインターを作れない
  • ポインターと参照を相互変換できない
  • ポインターだけではGC的に安全に参照を持てない

じゃあ、どうやってSpan<T>構造体を書いているかというと… ILアセンブラーです。 ILが必要な部分は別の、System.Runtime.CompilerServices.Unsafeというライブラリに追い出していますが、こちらの中身はほぼ全部ILです。

  • IL: Unsafeクラス
    • C#でコンパイル → ildasm で逆アセンブル → 属性中のコードでメソッドの中身を上書きするツール(ILSub)を通す → ilasm でアセンブル という黒魔術

IL を書くのは大変だし、ビルド手順も面倒になるしでなかなか保守がしんどそう。

まあ、こういうコードをC#だけで書けるようにしたいという提案は出ているんですが、 採用されるかどうかは不透明(少なくとも直近の作業には載ってない)。 参考: Compiler intrinsics

小ネタ 式の評価順序

$
0
0

C#小ネタと言いつつ、IL小ネタになりがちだったので、今日はC#小ネタらしく。

最初にちょっとしたクイズ。 まず、中身は何でもいいんですが適当な2引数のメソッドを用意します。 例として、単純な足し算でも用意しておきましょう。

static int F(int x, int y) => x + y;

以下の2つのコードの挙動は同じでしょうか?違うでしょうか?

1つ目: 一時変数を使用

var temp = F(2, 3);
var result = F(1, temp);

2つ目: 1つの式で計算

var result = F(1, F(2, 3));

まあ、同じですね。副作用を残さない限りは。

オペランドの評価順序

ということで、今日はオペランドの評価順の話です。 上記の2つのコードを、わざと副作用付きに書き換えてみます。

そのためにとりあえず、副作用を起こすメソッドを追加。 Consoleにログ出力した後、引数を素通しするだけのメソッドです。 値渡し版と参照渡し版を用意。

// WriteLine + 素通し
static T Log<T>(T x) { Console.WriteLine(x); return x; }
static ref T Log<T>(ref T x) { Console.WriteLine(x); return ref x; }

1つ目(一時変数を使用)を改めて:

var temp = F(Log(2), Log(3));
var result = F(Log(1), temp);
2
3
1

2つ目(1つの式で計算)を改めて:

var result = F(Log(1), F(Log(2), Log(3)));
1
2
3

C#では、式の評価は、上から下へ、左から右へ逐次実行です。 なので、一時変数を導入すると結果が変わります。

1つの式の中でも、演算子の優先順位や結合方向とは無関係に、評価は一律左から右です。 代入(優先度が低い上に右から左に結合)が混ざっていようと、そのオペランドの評価は左から右です。

例えば以下の通り。

bool x = false, y = true;
Log(ref x) = Log(ref y) = Log(1) + Log(2) * Log(3) > Log(4) & Log(5) <= Log(6) - Log(7) | Log(8) == Log(9);
False
True
1
2
3
4
5
6
7
8
9

名前付き引数のオペランド評価

前節の結果はそんなに不思議なことないでしょう。コードから挙動を予想しやすいって意味では書かれてる順番通りが一番です。それに、パフォーマンス的にも悪い選択ではありません。例えば、

var result = F(1, F(2, 3));

というようなコードであれば、コンパイル結果は以下のような感じになります(必要なところを抜粋)。

ldc.i4.1
ldc.i4.2
ldc.i4.3
call       F
call       F

元のC#のオペランドと同じ、1, 2, 3の順でldc (load constant)しています。 副作用を起こすためにLogメソッドを挟む場合、このldcのところが数命令に置き換わりますが、命令の並ぶ順序はこの場合と同じです。

ということで、素直に実装すればいいだけ…

でもなくて、まあ、ほとんどの式は素直に実装していいんですが、一部めんどくさい奴がいます。例えば、名前付き引数。 以下のようなコードを書いたとします。 x, yを逆に並べています。

F(1, F(y: 2, x: 3));

すると、コンパイル結果は以下の通り。C#ソースコード上は1, 2, 3だったものが、IL的には1, 3, 2になります。

ldc.i4.1
ldc.i4.3
ldc.i4.2
call       F
call       F

この結果は副作用がないからこそ、C#コンパイラーの最適化が掛かってこうなっています。 副作用がないことがわかっている場合、評価順を並べ替えを行います。

一方で、副作用があるとそうはいきません。あくまで、C#では左から右への評価が必要です。

例えば以下のようなコードをでは、ちゃんと、1, 2, 3の順での評価が必要です。

F(Log(1), F(y: Log(2), x: Log(3)));

コンパイル結果は以下の通りです。これまでは必要のなかった一時変数(stloc: ローカル変数へのストア)が必要になります。

ldc.i4.1
call       Log
ldc.i4.2
call       Log
stloc.0    // 一時変数!
ldc.i4.3
call       Log
ldloc.0
call       F
call       F

ちなみに、これ、C# 4.0の時にはバグってて評価順が狂ってた(逆順になってた)そうです。 C# 5.0でバグ修正した結果、破壊的変更になっていたり(めったにこんなコード書かない上に、バグの修正なので特に問題にはならず)。

タプルの要素の評価順序

もう1個変な例を挙げておきましょう。C# 7で導入されるタプルと分解で、以下のように、swapコードを書けるようになりました。

var x = 1;
var y = 2;
(x, y) = (y, x);
Console.WriteLine($"{x}, {y}");

これ、同じ処理をタプルを使わず書くとすると、まあ、以下のようにしますよね。

var temp = x;
x = y;
y = temp;

こいつらにも副作用を加えてみましょう。

まず、タプルを使うもの。

var x = 1;
var y = 2;
(Log(ref x), Log(ref y)) = (Log(y), Log(x));

ちゃんと、これも左から右に順に評価されます。すなわち、ref x, ref y, y, xの順。なので結果は以下の通り。

1
2
2
1

で、これをタプルなしで副作用も込みで全く同じ挙動にするためにはどうするか。

先ほどの類推で以下のように書いてしまうと、副作用の順序が変わります。

var temp = Log(x);
Log(ref x) = Log(y);
Log(ref y) = temp;
1
1
2
2

正しくは、以下のように書かないと同じにはなりません。

ref var rx = ref Log(ref x);
ref var ry = ref Log(ref y);
var vy = Log(y);
var vx = Log(x);
rx = vy;
ry = vx;

まとめ

副作用があっても常に一定の結果になるように、C#では、オペランドの評価順が常に左から右、書かれている通りの順序で行われます(まあ、割かし最近のプログラミング言語では大体同じで順序保証があります)。

ただ、順序保証がない場合に比べて、保証のためのコストがちょっとだけかかります(なので、古いプログラミング言語では「コンパイラーの実装ごとに変えていい」となっているものも結構あります)。

まあ、これだけ書いておいて身もふたもない結論で締めますが、副作用起こすような式を書くやつが悪い。

Viewing all 482 articles
Browse latest View live