Successfully reported this slideshow.

C++コミュニティーの中心でC++をDISる

25

Share

Upcoming SlideShare
Pfi Seminar 2010 1 7
Pfi Seminar 2010 1 7
Loading in …3
×
1 of 66
1 of 66

More Related Content

Related Books

Free with a 14 day trial from Scribd

See all

C++コミュニティーの中心でC++をDISる

  1. 1. C++コミュニティの中心で C++をDISる 田中英行
  2. 2. 自己紹介 • 田中 英行 (@tanakh, id:tanakh) • (株)Preferred Infrastructure 勤務 • Haskell好き/C++嫌い • C++歴13年 – 主にWindowsでゲームを作ったりしていました – 最近は仕事でコンソールアプリ書いてます
  3. 3. あらかじめおことわり • Boost.勉強会ということですが – Boost出てきません – むしろBoost良くわからないので教えてください!
  4. 4. 突然ですが • 今日はC++をDISる機会をいただきました! • (通称)C++闇の軍団なる皆様方に於かれまし ては釈迦に説法以外の何物でもありません が… • 僭越ながら、C++をDISらせて頂きます
  5. 5. ご存知でしたか? C++が今年の “Discriminating Hackersが選ぶ言語” に選ばれていたという事実を・・・
  6. 6. Discriminating Hackersの選ぶ言語 とは? • ICFP という関数プログラミングの学会が • 毎年プログラミングコンテストを開催しています • 優勝チームの使っていた言語が、 “Discriminating Hackersの選ぶ言語” として讃えられ、無制限に自慢できる権利を与 えられます
  7. 7. 前回の優勝者&言語
  8. 8. よりにもよって、 手続き型の、 それもC++が、 関数プログラミングの学会主催の コンテストで、 Discriminating 言語だなんて!
  9. 9. しかし、ルールはルール • C++に逆らえない日々… • 何故か仕事でもC++ばかり… • 何とかせねば!
  10. 10. そして今年(去年)のICFP Contest 私の参加した チーム
  11. 11. やりました! • 優勝者になって、好きな言語を選べる! – しかし、ものすごくモメる • とりあえず使った言語列挙する – C++、Haskell、Python – すごくオーソドックスな組み合わせですね! • ということで、今年(9月頃まで?)はくれぐれ もこれらの言語の悪口は謹んで頂くように!
  12. 12. しかし。 • 私はC++が好きではない – (C++を使ったのは主に私なんですが…) • 私が選んだのだから(Discriminating Hackerな のだから)その効力は私には及ぶまい(と勝 手にルールを解釈) • そこで・・・
  13. 13. 今年唯一(?)大手を振って C++の悪口を言える私が 思う存分C++をDISる
  14. 14. という • ごく個人的な発表です。
  15. 15. 概要 • 大きく3つのカテゴリに分けて、C++をDISる –型 – テンプレート – 関数プログラミング • お手柔らかにお願いします
  16. 16. Part 1: Type System その1:型
  17. 17. C++の型システム • Very Irritating! • C++のストレスの約8割はこいつの所為
  18. 18. 同じ型を何度も書かされる • (int, int) -> int なる map を扱うとき map<int, map<int, int> > mm; for (map<int, map<int, int> >::iterator p = mm.begin(); p != mm.end(); p++){ map<int, int> &r = p->second; for (map<int, int>::iterator q = r.begin(); q != r.end(); q++){ .... } }
  19. 19. これが良くないところ • 単純に面倒 – Do not repeat yourself! • 変更に弱い – 何箇所も変更しないといけない
  20. 20. 例 • int -> double に変更 map<int, map<int, double> > mm; for (map<int, map<int, double> >::iterator p = mm.begin(); p != mm.end(); p++){ map<int, double> &r = p->second; for (map<int, double>::iterator q = r.begin(); q != r.end(); q++){ .... } }
  21. 21. typedef 使え typedef map<int, double> ogram; typedef map<int, ogram> tgram; tgram mm; for (tgram::iterator p = mm.begin(); p != mm.end(); p++){ ogram &r = p->second; for (ogram::iterator q = r.begin(); q != r.end(); q++){ .... } }
  22. 22. だが断る • 付けるべきではない名前を付けないといけない – しかも今回の場合2つ • なぜ名前をつけるべきではないのか? – 1.名前に意味が無い – 2.意味のない名前を考えるのは大変 – 3.(今回の場合)本質的には繰り返しではない
  23. 23. 0x使え map<int, map<int, double> > mm; for (auto p = mm.begin();p != mm.end(); p++){ auto r = p->second; for (auto q = r.begin();q != r.end(); q++){ .... } }
  24. 24. 0x使わせて (´;ω;`)
  25. 25. 読ませる気のないコンパイルエラー • 行番号しか参考になりません! • gcc 4.5でデフォルト型引数の省略などが入っ たが… – そもそもの型が大きければ意味がない – これはC++ライブラリの構造上の問題か
  26. 26. 型推論が非力 • そもそも型推論とは何ぞや? – 型なしプログラムから型ありプログラムの再構築 (Type Reconstruction) • C++0xでは、すべての型を推論できるわけではな い – ○ローカル変数(auto) – ○ラムダ式の返り値 – ×関数の引数 – ×クラスメンバ • Hindly-Milner型の型推論ができないものか
  27. 27. Part 2: template in C++ その2:テンプレート
  28. 28. テンプレート • みんな大好きテンプレート • だがしかし! • テンプレートおかしいよテンプレート • C++のストレスの約8割はこいつの所為
  29. 29. テンプレートの型 • templateの型パラメータは、forallではない • たとえばこれは? template <class T> T id(T v){ return v; } – ∀a => a -> a だろうか? – 実は違う
  30. 30. テンプレートの型 • なぜなら、引数に対して任意の操作が可能だ から template <class T> void id(T v){ v.foo(); } – どういう型が付くのが妥当なのだろうか? – 実際にはC++では、テンプレート関数(クラス) 自体には型はつかない(つけない)
  31. 31. そもそもC++テンプレートとは…? • C++のテンプレートは何なの? – 見た目StructualなSub-Typingを実現している – Sub-Type relationの解決をインスタンス時に各々 行う – コンパイル時に型レベルでコードを実行するよう なもの
  32. 32. その代償 • C++では、テンプレート関数を宣言時ではなく、 使用時にインスタンス化し、それに対して型 付けを行う • これはどういう事を招くか? – テンプレート関数(クラス)を分割コンパイルでき ない – テンプレート関数自体に対しては型が付かない – (得られるコンパイルエラーが往々にして不可解)
  33. 33. 分割コンパイルできない • テンプレート関数を使用している箇所すべて に対して、その完全な定義が必要 – 基本、ヘッダファイルに全部書くことになる • これは何を招くのか? – コンパイル速度の低下 – ファイル間の依存の増加
  34. 34. コンパイル速度 • 最悪の場合、コンパイル時間がファイル数の 2乗のオーダに ヘッダファイル ソースファイル
  35. 35. ファイル間の依存性 • インターフェースが変わらなくても、実装が変 わればコンパイルし直さなければならない • pimpl のようなバッドノウハウ – 実行速度低下 – コードがまどろっこしく
  36. 36. ヘッダに書くことによる問題 • namespace地獄 std::map<int, std::map<int, int> > mm; for (std::map<int,std::map<int, int> >::iterator p = mm.begin(); p != mm.end(); p++){ std::map<int, int> &r = p->second; for (std::map<int, int>::iterator q = r.begin(); q != r.end(); q++){ .... } } – using namespace させて…
  37. 37. テンプレートに型がつかない • テンプレート関数は定義のみではコンパイル されない – つまり型がつかない • 一方、型は非常に良いドキュメントになる – 型の重要なメリットの一つ • C++のテンプレートからは、これがすべて失わ れている
  38. 38. 例 • std::transform() template <class A, class B, class C> B transform(A, A, B, C); – これでは何のことやら
  39. 39. 例(2) • もちろん、人がある程度注釈することはできる template <class InputIterator, class OutputIterator, class UnaryOperator> OutputIterator transform(InputIterator first1, InputIterator last1, OutputIterator result, UnaryOperator op); • しかし、これでは不十分 – Input/OutputIteratorが満たす要件は? – UnaryOperatorとはなんなのか? – これらはドキュメントに書くしかない • それは何の保証もない
  40. 40. パラメトリック多相の場合 • 何を渡さなければならないか一目瞭然 • どういう処理がされるかすら分かる場合も多い map :: (a -> b) -> [a] -> [b] map f xs = ...
  41. 41. 今は亡きコンセプトさん /二二ヽ | 今 | | 瀬 | | 腑 | | 戸 | | 之 | __| 墓 |__ / └──┘ \ |´ ̄ ̄ ̄ ̄ ̄ ̄| ソ ソ::::::::::::::::::::::::::::::::ソソ / ソ ̄|;;;;;;;lll;;;;;;;| ̄ソ \ |´ ̄ ̄ |. [廿] .|´ ̄ ̄.| |:::::::::::::::| |::::::::::::::| |:::::::::::::::| ̄ ̄ ̄|::::::::::::::| . ̄ ̄ ̄|_______|´ ̄ ̄゛
  42. 42. コンセプトがあれば… • テンプレート引数への要件の明示による ドキュメント性の改善 • インスタンス化→型チェックによらずに型 チェックができるようになり、コンパイルエラー の改善 • コンセプトさえあれば…
  43. 43. Part 3: Functional Programming その3:関数プログラミング
  44. 44. Haskell愛好家としては… • C++/C++0xでの関数プログラミングには 我慢ならない…!
  45. 45. オブジェクト指向+関数型 • すべてのものはオブジェクトであるべき • (すべてのものは関数であるべき) • 関数がオブジェクトであるということ – 関数に付く方がすごく歪になる • 関数型ならシンプルなのに…
  46. 46. ラムダ式 • Haskell – ¥x -> x+1 • OCaml – fun x -> x • Scheme – (lambda (x) x) • C++ – [](int x){ return x+1; }
  47. 47. ラムダ式 • とにかくキモい。 int main() { [](){}(); }
  48. 48. ラムダ式 • とにかくキモすぎる。 int main() { [](){[](){[](){[](){[](){[](){}();[](){}();}(); [](){[](){}();[](){}();}();}();}();[](){[](){[]() {[](){}();[](){}();}();[](){[](){}();[](){[](){[] (){[](){}();[](){}();}();[](){[](){}();[](){}();} ();}();}();}();}();}();}();[](){[](){[](){[](){[] (){}();[](){[](){[](){[](){[](){[](){[](){}();[]( ){}();}();[](){[](){cout<<"やぁ(´・ω・`)"<<endl;}(); [](){}();}();}();}();[](){}();}();[](){[](){}();[ ](){}();}();}();}();}();[](){[](){}();[](){}();}( );}();}();[](){[](){[](){[](){}();[](){}();}();[] (){[](){}();[](){}();}();}();}();}();}(); }
  49. 49. ラムダ式 • どうしてこうなった – 予約語、後方互換… • まあ、それはおいといて。
  50. 50. ラムダ式の型 • ラムダ式の型がイケてない cout<<demangle(typeid([](int x){return x+1;}).name())<<endl; $ g++ -std=c++0x test.cpp $ ./a.out main::{lambda(int)#1} ???
  51. 51. ラムダ式の型 • 引数と返り値の型がラムダ式の型に現れない • そのままでは、引数や返り値の型を利用する のが困難
  52. 52. 関数の型 • 関数の型を扱いたい時、functionクラスを用 いると簡単 – しかし、パフォーマンスの低下 • インライン展開されないなど – 単なるテンプレートパラメータが好まれる • 結局ドキュメントとしての型が得られない template <class A> template <class F, class A> A twice(function<A(A)> f, A v) A twice(F f, A v) { { return f(f(v)); return f(f(v)); } }
  53. 53. 多相関数 • C++0xのラムダ式は多相にならない – 例えば id x = x, id :: forall a . a -> a – のようなものが書けない – template <class A>[](A x){ return x; } – ↑ こういうモノが書けない
  54. 54. テンプレート関数の値 • C++ではテンプレート関数、それ自体は値とし て扱えない – 具体的な型でインスタンス化しないと値にならない – つまり、そのような式に付く型がない template <class T> T id(T x){ return x; } int main() { // auto f = id; // <- これにつく型は? auto g = id<int>; // <- これは int->int }
  55. 55. 総称型がない • C++の型には総称型がない – すべての型は具体的な型の組み合わせ – つまり型パラメータがない – 多相関数に付けられる型がないのもまあ頷ける • ところが、Boost.Lambdaでは多相関数が扱え るらしい? – 一体どういう事なのか?
  56. 56. Boost.Lambdaでのラムダ式 • 何故かできてしまう int main() { using namespace boost::lambda; auto f = _1; // ¥x -> x cout<<f(1)<<endl; // -> 1 cout<<f(3.14)<<endl; // -> 3.14 } • fの型は何なのか? – forall a . a -> a をどうやって表現している?
  57. 57. Boost.Lambdaの型 • 出力してみる cout<<demangle(typeid(f).name())<<endl; boost::lambda::lambda_functor<boost::la mbda::placeholder<1> > • ??
  58. 58. Boost.Lambdaの型(2) • もう少し複雑な例 int main() { using namespace boost::lambda; auto f = cout << _1 << '¥n'; cout<<demangle(typeid(f).name())<<endl; } boost::lambda::lambda_functor<boost::lambda::lambda_functor_base<boost::lambda::bitwise_action<boost:: lambda::leftshift_action>, boost::tuples::tuple<boost::lambda::lambda_functor<boost::lambda::lambda_functor_base<boost::lambda::b itwise_action<boost::lambda::leftshift_action>, boost::tuples::tuple<std::ostream&, boost::lambda::lambda_functor<boost::lambda::placeholder<1> >, boost::tuples::null_type, boost::tuples::null_type, boost::tuples::null_type, boost::tuples::null_type, boost::tuples::null_type, boost::tuples::null_type, boost::tuples::null_type, boost::tuples::null_type> > >, char const, boost::tuples::null_type, boost::tuples::null_type, boost::tuples::null_type, boost::tuples::null_type, boost::tuples::null_type, boost::tuples::null_type, boost::tuples::null_type, boost::tuples::null_type> > >
  59. 59. Boost.Lambdaの型(3) • 型が式を丸ごと持ってた! – Expression Template • 確かにこれで型は付くのだけどインターフェー スとしては… • 型の型を要求する!
  60. 60. パターンマッチ • オブジェクト指向言語に関数型が取り込まれ るときに無視されるものの筆頭その1 • なんでない? – なんでない言語が多い? • なんでない? – 絶対必要だと思うんですけども
  61. 61. パターンマッチのような物…? • なんか提案されているらしい – さすがにこれは… variant< int , pair<int,double> , pair<int,int> , pair<double,int> > var; switch_(var) |= case_< pair<_1,_1> >(...) // matches pair<int,int> |= case_< pair<int,_> >(...) // matches pair<int,*> |= case_< pair<_,_> >(...) // matches pair<*,*> |= case_< int >(...) ;
  62. 62. カリー化 • オブジェクト指向言語に関数型が取り込まれ るときに無視されるものの筆頭その2 • もしかしてカリー化軽視されている?
  63. 63. カリー化の旨み • 純粋関数的に状態を持ちまわるというのが とてもやりやすくなる cnt acc i = (acc, cnt (acc+i)) let a = cnt 0 in let (v, b) = a 1 in –- v = 0 let (w, c) = b 2 in –- w = 1 let (x, d) = c 3 in –- x = 3 ... • オブジェクト vs 関数 • 関数プログラミングでは、次の状態として 関数を返すことがよくある(e.g. Iteratee)
  64. 64. まとめ
  65. 65. まとめ • まだまだ言いたいことはありますが… – iteratorはクソ – 例外周りがいろいろクソ – ADLはクソ – moveはクソ – 右辺値参照はクソ – GCないとかクソ – スマポはクソ – … • C++もいいところはあると思います – 適当に書いてもそこそこ速いコードが書ける – 0xでそれなりに改善されてる
  66. 66. おわりに • 私がC++から足を洗えるようになるのは いつになりますことやら • せめて0xを・・・!

×