Hatena::ブログ(Diary)

猫とC#について書くmatarilloの雑記 Twitter

2014年08月28日

トラックバック - http://d.hatena.ne.jp/matarillo/20140828

2014年08月13日

matarillo.comの翻訳ネタバレ パート2

| matarillo.comの翻訳ネタバレ パート2を含むブックマーク matarillo.comの翻訳ネタバレ パート2のブックマークコメント

翻訳に仕込んだ冗談の解説という野暮な記事です。パート1(31日間ReSharper一周)はこちら。

モナド

FAIC モナド、パート1
単なる自己関手の圏におけるモノイド対象だよ。何か問題でも?(monoids in the category endofunctors)
脚注にも書いたけど、「不完全にしておよそ正しくないプログラミング言語小史」より引用
FAIC モナド、パート4
今回は3、4歩前進して/3月4日だけにね(キリッ)だっておwwwwwwwwwwバカスwwwwwww(Let's march forth/HA HA HA HA HA HA I crack myself up)
これはねえ、原文が公開されたのが3月4日(March 4th)だったというしょうもないボケでねえ……
FAIC モナド、パート5
ここでマサカリを投げさせてほしい(let me throw a spanner into the works here)
「throw a spanner into the works(作ったものにスパナを投げ込む)」は「邪魔をする」といった意味慣用句らしい。ここでは日本の技術者コミュニティでよく使われる「マサカリ」に置き換えてみた。マサカリは邪魔するときに投げるわけじゃないけどね。
Nullable<T> はモナド大将の中でも最弱(Nullable<T> is one of the simplest monads)
漫画『ギャグマンガ日和』の作中作『ソードマスターヤマト』に出てきた「奴は四天王の中でも最弱……」というセリフより。
FAIC モナド、パート6
これで終わり?まだだ、まだ終わらんよ。(Is that it? Not quite.)
アニメ『機動戦士Zガンダム』のクワトロ・バジーナのセリフ「まだだ、まだ終わらんよ」より。
FAIC モナド、パート10
※ただし偶数に限る(where the integer is odd)
あ、偶数と奇数を訳し間違えてた。ごめんなさい。翻訳はネットスラング「※ただしイケメンに限る」より。
FAIC モナド、パート12
やばいな……これで終わりにすると、キッパリ言ったばかりなのに……スマン、ありゃウソだった(OK, I was wrong when I said that I'd be wrapping up here)
『ジョジョの奇妙な冒険』第5部の主人公、ジョルノ・ジョバァーナのセリフ「やばいな…… 一般人を巻き込まないとキッパリ言ったばかりなのに……スマン ありゃ ウソだった」より。
トラックバック - http://d.hatena.ne.jp/matarillo/20140813

2014年08月09日

トラックバック - http://d.hatena.ne.jp/matarillo/20140809

2014年07月27日

猫と洗濯物

| 猫と洗濯物を含むブックマーク 猫と洗濯物のブックマークコメント

枕にするなよ。

2014-07-27 09.42.13

トラックバック - http://d.hatena.ne.jp/matarillo/20140727

2014年07月09日

ASP.NET MVC 5.2のメモ(Linux)

| ASP.NET MVC 5.2のメモ(Linux)を含むブックマーク ASP.NET MVC 5.2のメモ(Linux)のブックマークコメント

MonoDevelopは4.0Monoは3.2.8をインストール。

デフォルトではMVC 3のプロジェクトテンプレートが入っているから、まずはそれを選んでプロジェクトを作る。

f:id:matarillo:20140709233247p:image

ところがプロジェクトを作るときになぜかエラーが。

System.IO.FileNotFoundException: Could not find file "/usr/lib/monodevelop/AddIns/MonoDevelop.AspNet.Mvc/Templates/Common/Index.cshtml".

同じ現象が起きてる人を見つけた。回答には「MonoDevelopをソースからビルドしろ」とあったが、面倒だったのでGitHubの当該ブランチから足りてなさげなテンプレートファイルを取ってきた。そしたらプロジェクトの作成時にはエラーが出なくなった。

しかし問題はまだあり、ビルドすると警告が出るし、実行するとFileNotFoundExceptionになる。

f:id:matarillo:20140709233249p:image

プロジェクトをよく見たら、参照アセンブリが見つかっていない。

f:id:matarillo:20140709233248p:image

こちらも同じ現象が起きてる人を見つけた。こっちの回答も自分でビルドしろとか言ってる。えー。

そこで慌てず騒がずNuGet Addinをインストールする

そしてもういっそのことASP.NET MVC 5.2を入れることにする。

f:id:matarillo:20140709233251p:image

そしたらアセンブリ参照の警告はなくなったが、実行するとまだエラーになる。今度はInvalidOperationExceptionだ。

f:id:matarillo:20140709233252p:image

Web.configを見ると、Web Pagesのバージョンが1.0になっていたので、3.0に手で直す。

  <appSettings>
    <!-- add key="webpages:Version" value="1.0.0.0" / -->
    <add key="webpages:Version" value="3.0.0.0" />
    <add key="ClientValidationEnabled" value="true" />
    <add key="UnobtrusiveJavaScriptEnabled" value="true" />
  </appSettings>

これでもまだInvalidOperationExceptionが出る。ただしエラーメッセージはさっきとは違う。

f:id:matarillo:20140709233253p:image

Views/Web.configを見ると、MVC 3の指定が残っていたので、5.2に変える。

	<system.web.webPages.razor>
		<!-- host factoryType="System.Web.Mvc.MvcWebRazorHostFactory, System.Web.Mvc,
                          Version=3.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" / -->
		<host factoryType="System.Web.Mvc.MvcWebRazorHostFactory, System.Web.Mvc,
                          Version=5.2.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
		<pages pageBaseType="System.Web.Mvc.WebViewPage">
			<namespaces>
				<add namespace="System.Web.Mvc" />
				<add namespace="System.Web.Mvc.Ajax" />
				<add namespace="System.Web.Mvc.Html" />
				<add namespace="System.Web.Routing" />
			</namespaces>
		</pages>
	</system.web.webPages.razor>

これでやっとASP.NET MVCが動いた。

f:id:matarillo:20140709233254p:image

トラックバック - http://d.hatena.ne.jp/matarillo/20140709

2014年07月07日

Entity Framework 6とMySQLのメモ(Windows)

| Entity Framework 6とMySQLのメモ(Windows)を含むブックマーク Entity Framework 6とMySQLのメモ(Windows)のブックマークコメント

nugetで「MySQL.ConnectorNET.Entity」をインストール。他のは無視。

f:id:matarillo:20140707154804p:image

f:id:matarillo:20140707154801p:image

うまくいけば、アセンブリの参照はこんな感じになる。

f:id:matarillo:20140707154803p:image

App.configはこう。

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <configSections>
    <!-- For more information on Entity Framework configuration, visit http://go.microsoft.com/fwlink/?LinkID=237468 -->
    <section name="entityFramework"
             type="System.Data.Entity.Internal.ConfigFile.EntityFrameworkSection, EntityFramework, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" requirePermission="false" />
  </configSections>
  <startup>
    <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
  </startup>
  <entityFramework>
    <defaultConnectionFactory type="System.Data.Entity.Infrastructure.SqlConnectionFactory, EntityFramework" />
    <providers>
      <provider invariantName="System.Data.SqlClient"
                type="System.Data.Entity.SqlServer.SqlProviderServices, EntityFramework.SqlServer" />
      <provider invariantName="MySql.Data.MySqlClient"
                type="MySql.Data.MySqlClient.MySqlProviderServices, MySql.Data.Entity.EF6, Version=6.8.3.0, Culture=neutral, PublicKeyToken=c5687fc88969c44d"></provider>
    </providers>
  </entityFramework>
  <system.data>
    <DbProviderFactories>
      <remove name="MySQL Data Provider"
              invariant="MySql.Data.MySqlClient" />
      <add name="MySQL Data Provider"
           invariant="MySql.Data.MySqlClient"
           description=".Net Framework Data Provider for MySQL"
           type="MySql.Data.MySqlClient.MySqlClientFactory, MySql.Data, Version=6.8.3.0, Culture=neutral, PublicKeyToken=c5687fc88969c44d" />
    </DbProviderFactories>
  </system.data>
</configuration>

そこで、EF6 Code Firstのコードを書く。

using System;
using System.Data.Entity;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            Database.SetInitializer(new DropCreateDatabaseAlways<BlogContext>());

            using (var db = new BlogContext())
            {
                db.Blogs.Add(new Blog { Name = "aaa" });
                db.SaveChanges();

                foreach (var blog in db.Blogs)
                {
                    Console.WriteLine("BlogId={0},Value={1}", blog.BlogId, blog.Name);
                }
            }
        }
    }

    public class BlogContext : DbContext
    {
        public DbSet<Blog> Blogs { get; set; }
    }

    public class Blog
    {
        public int BlogId { get; set; }
        public string Name { get; set; }
    }
}

App.configには接続文字列を足す。

  <connectionStrings>
    <add name="BlogContext"
         providerName="MySql.Data.MySqlClient"
         connectionString="Server=localhost;Uid=root;Pwd=XXXX;Database=testdb;"/>
  </connectionStrings>

これで実行しても例外(ConfigurationErrorsException)が発生する。

f:id:matarillo:20140707154802p:image

答えはMySQL Connector/Net Developer Guideに書いてあった。

MySQL用の新しいDbConfigurationクラスを設定しましょう。このステップは省略することもできますが、省略せず設定することを強く推奨します。なぜなら、MySQLのすべての依存関係解決モジュール(dependency resolvers)が追加されるからです。以下の3つの方法があります。

  • コンテキストクラスに DbConfigurationTypeAttribute カスタム属性を付ける。
    [DbConfigurationType(typeof(MySqlEFConfiguration))]
  • アプリケーションのスタートアップコードで DbConfiguration.SetConfiguration(new MySqlEFConfiguration()) を呼び出す。
  • 構成ファイルDbConfiguration 型を設定する。
    <entityFramework codeConfigurationType="MySql.Data.Entity.MySqlEFConfiguration, MySql.Data.Entity.EF6">

カスタムのDbConfigurationクラスを作れば、依存関係解決モジュールを必要なものだけ追加することもできます。

ためしに構成ファイルでやってみた。

  <entityFramework codeConfigurationType="MySql.Data.Entity.MySqlEFConfiguration, MySql.Data.Entity.EF6">
    <defaultConnectionFactory type="System.Data.Entity.Infrastructure.SqlConnectionFactory, EntityFramework" />
    <providers>
      <provider invariantName="System.Data.SqlClient"
                type="System.Data.Entity.SqlServer.SqlProviderServices, EntityFramework.SqlServer" />
      <provider invariantName="MySql.Data.MySqlClient"
                type="MySql.Data.MySqlClient.MySqlProviderServices, MySql.Data.Entity.EF6, Version=6.8.3.0, Culture=neutral, PublicKeyToken=c5687fc88969c44d"></provider>
    </providers>
  </entityFramework>

なるほど動いた。

f:id:matarillo:20140707154800p:image

Entity Framework 6とMySQLのメモ(Linux)

| Entity Framework 6とMySQLのメモ(Linux)を含むブックマーク Entity Framework 6とMySQLのメモ(Linux)のブックマークコメント

UbuntuのMonoDevelopが4.0.12で、NuGetアドインを自分で追加しないといけないけど、後の手順は同じでよかった。

(土日に苦労したのは何だったんや……)

f:id:matarillo:20140707191613p:image

トラックバック - http://d.hatena.ne.jp/matarillo/20140707

2014年07月03日

トラックバック - http://d.hatena.ne.jp/matarillo/20140703

2014年06月03日

C#から見たApple Swift

| C#から見たApple Swiftを含むブックマーク C#から見たApple Swiftのブックマークコメント

The Swift Programming Language

C#erによる濁った眼で見たSwiftです。間違いや不足などあればつど修正予定。

C#にもSwiftにもある
C#にもSwiftにもあるがSwiftのほうが強力
SwiftにはあるがC#にはない
  • パターンマッチ
  • タプル(C#のタプルは言語の機能ではない)
  • 代数的データ型(enumが該当)
  • ARC(言語の機能ではないけど)
  • デイニシャライザー(これってC++のデストラクタ―かな)
  • 委譲(Obj-Cのデリゲートと同じもので、C#のデリゲートとは違う)
  • 範囲演算子
  • エイリアス
  • immutableサポート
  • ユーザー定義演算子
C#にはあるがSwiftにはなさそう
  • 名前空間
  • 構造化例外
  • デリゲート(C#のデリゲート。もうあまり使わない機能だしどうでもいいが……)
  • イベント(Swiftでは、クロージャを渡してコールバックしてもらうとか、PropertyChangedイベントについてはプロパティオブザーバー対応するとか、そんな感じ)
  • ジェネレータ(yield return)
  • 非同期サポート(Obj-CやSwiftではGCDを使うようだ)
  • 変性
  • リフレクション(ランタイムの性質上しょうがない)
  • GC(言語の機能ではないけど)
C#にもSwiftにもなさそう
トラックバック - http://d.hatena.ne.jp/matarillo/20140603

2014年05月26日

ネネの反復運動

| ネネの反復運動を含むブックマーク ネネの反復運動のブックマークコメント

腹を見せるだけじゃ飽き足らず、左右に向きを変えてくるのは何なんだ。

D

トラックバック - http://d.hatena.ne.jp/matarillo/20140526

2014年05月17日

続・C#のジェネリクスで型クラスを真似る

| 続・C#のジェネリクスで型クラスを真似るを含むブックマーク 続・C#のジェネリクスで型クラスを真似るのブックマークコメント

前回のエントリ

前回のコード(GitHub Gist)は、値を入れるValue<TImpl, T>クラスが単なるDictionary<string, object>のラッパーでしかなく、汚れ仕事押し付けた形となっていた。

https://cacoo.com/diagrams/pVKta8cnZB9JPLye

そこを何とかしようと思うと、今回のコード(GitHub Gist)みたいになる。

https://cacoo.com/diagrams/RL7zrWc6yyc46wa9

とはいえ、型クラスもどきの方は型引数TImplに実装クラスMaybeを入れられるが、値クラスの方は型引数を使ったとしても実装クラスMaybe<T>ジェネリックのため指定できない。(型引数を取る型引数が不可能、というのにやられているわけだ。)

なのでMaybe<T>を引き回すことができない。型クラスもどきのメソッドを呼び出すたびにMonad<Maybe, T>Functor<Maybe, T>にアップキャストされてしまうのだ。そこで仕方なく、明示的なダウンキャストでMaybe<T>に戻さないといけなくなる。

トラックバック - http://d.hatena.ne.jp/matarillo/20140517

2014年05月07日

MonoDevelopとXamarin Studio

| MonoDevelopとXamarin Studioを含むブックマーク MonoDevelopとXamarin Studioのブックマークコメント

http://monodevelop.com/

Xamarinの中の人から教えてもらった上で私が理解したこと:

というわけで改名したというのはまちがいだが、MonoDevelopの公式サイトをXamarin社が管理している限りは、バイナリパッケージはXamarin Studioの名で配布されるのではないだろうか。

トラックバック - http://d.hatena.ne.jp/matarillo/20140507

2014年04月29日

C#のジェネリクスで型クラスを真似る

| C#のジェネリクスで型クラスを真似るを含むブックマーク C#のジェネリクスで型クラスを真似るのブックマークコメント

id:Nagise さんのJavaジェネリクス記事を読みながら、C#ではどうかな、とあれこれ考えているわけです。

そんな時に、 ジェネリクスのカリー化 - プログラマーの脳みその話を振られて。

真の高階型引数、つまり、型パラメータを取る型パラメータは実現できないです。が、型クラス的なものであればエミュレートできるらしい。

もちろんCLRにもILにも型クラス的なものなんかないわけで、オダスキー教授が言うところの「貧者の型クラス(pdf)」を使う、つまり型に適合するメソッド群をインスタンスとして渡してやるしか方法はないわけですが(ちなみにScalaでは、そのインスタンスを暗黙的に生成したり渡したりできる仕掛けがある)。

というわけで、最初に書いたのがこっちなんだけど…

https://gist.github.com/matarillo/11392331

(追記)ん?型クラス(もどきインスタンス)、new()制約をつけられるんじゃないの?

ということは?

public static class MonadEx
{
    public static Value<TMonad, TResult> SelectMany<TMonad, TSource, TCollection, TResult>(
        this Value<TMonad, TSource> source,
        Func<TSource, Value<TMonad, TCollection>> collectionSelector,
        Func<TSource, TCollection, TResult> resultSelector)
        where TMonad : Monad<TMonad>, new()
    {
        var monad = new TMonad();
        return
            monad.Bind(source, x=>
                monad.Bind(collectionSelector(x), y=>
                    monad.Unit(resultSelector(x,y))));
    }
}

まさか?

internal class Program
{
    private static void Main(string[] args)
    {
        var m =
            from x in Maybe.Instance.Unit(12)
            from y in Maybe.Instance.Unit(34)
            select x + y;
        var message =
            m.HasValue()
                ? m.Value().ToString()
                : "Nothing";
        Console.WriteLine(message);
    }
}

いけるじゃないか!!


(追記)new制約つけてnewしましたが、よーく考えると、Valueクラスの中に型クラスもどきインスタンスを持つようにするとか、あるいはタプルで型クラスインスタンスとValueクラスのインスタンスをまとめて扱うようにする方法もありますね。そしたら型クラスもどきインスタンスを使いたい側でnewしなくても、Valueと一緒に受け取れるはず。


https://gist.github.com/matarillo/11393891

using System;
using System.Collections.Generic;

internal class Program
{
    private static void Main(string[] args)
    {
        var m =
            from x in Maybe.Instance.Unit(12)
            from y in Maybe.Instance.Unit(34)
            select x + y;
        var message =
            m.HasValue()
                ? m.Value().ToString()
                : "Nothing";
        Console.WriteLine(message);
    }
}

public abstract class TypeClass<TImpl>
{
}

public abstract class Functor<TImpl> : TypeClass<TImpl>
{
    public abstract Value<TImpl, TResult> Map<T, TResult>(
        Value<TImpl, T> m,
        Func<T, TResult> f);
}

public abstract class Applicative<TImpl> : Functor<TImpl>
{
    public abstract Value<TImpl, T> Pure<T>(T value);

    public abstract Value<TImpl, TResult> Ap<T, TResult>(
        Value<TImpl, T> m,
        Value<TImpl, Func<T, TResult>> mf);

    public override Value<TImpl, TResult> Map<T, TResult>(
        Value<TImpl, T> m,
        Func<T, TResult> f)
    {
        return Ap(m, Pure(f));
    }
}

public abstract class Monad<TImpl> : Applicative<TImpl>
{
    public abstract Value<TImpl, T> Unit<T>(T value);

    public abstract Value<TImpl, TResult> Bind<T, TResult>(
        Value<TImpl, T> m,
        Func<T, Value<TImpl, TResult>> f);

    public override Value<TImpl, T> Pure<T>(T value)
    {
        return Unit(value);
    }

    public override Value<TImpl, TResult> Ap<T, TResult>(
        Value<TImpl, T> m,
        Value<TImpl, Func<T, TResult>> mf)
    {
        return Bind(m, x => Bind(mf, f => Unit(f(x))));
    }
}

public class Maybe : Monad<Maybe>
{
    public static readonly Maybe Instance = new Maybe();

    public static Value<Maybe, T> Nothing<T>()
    {
        return new Value<Maybe, T>();
    }

    public override Value<Maybe, T> Unit<T>(T value)
    {
        return new Value<Maybe, T>().SetValue(value);
    }

    public override Value<Maybe, TResult> Bind<T, TResult>(
        Value<Maybe, T> m,
        Func<T, Value<Maybe, TResult>> f)
    {
        return (m.HasValue()) ? f(m.Value()) : Nothing<TResult>();
    }
}


public class Value<TImpl, T>
{
    private readonly Dictionary<string, object> _fields
        = new Dictionary<string, object>();

    public void Set(string name, object value)
    {
        _fields[name] = value;
    }

    public TField Get<TField>(string name)
    {
        object o;
        if (_fields.TryGetValue(name, out o))
        {
            return (TField)o;
        }
        return default(TField);
    }
}

public static class MonadEx
{
    public static Value<TMonad, TResult> SelectMany<TMonad, TSource, TCollection, TResult>(
        this Value<TMonad, TSource> source,
        Func<TSource, Value<TMonad, TCollection>> collectionSelector,
        Func<TSource, TCollection, TResult> resultSelector)
        where TMonad : Monad<TMonad>, new()
    {
        var monad = new TMonad();
        return
            monad.Bind(source, x=>
                monad.Bind(collectionSelector(x), y=>
                    monad.Unit(resultSelector(x,y))));
    }
}

public static class MaybeEx
{
    public static Value<Maybe, T> SetValue<T>(this Value<Maybe, T> m, T value)
    {
        m.Set("HasValue", true);
        m.Set("Value", value);
        return m;
    }

    public static bool HasValue<T>(this Value<Maybe, T> m)
    {
        return m.Get<bool>("HasValue");
    }

    public static T Value<T>(this Value<Maybe, T> m)
    {
        if (m.HasValue())
        {
            return m.Get<T>("Value");
        }
        throw new InvalidOperationException();
    }
}

xuweiさんにもコメントもらいたいなあ。

2014年04月22日

YコンビネータっていうかZコンビネータでラムダ式が再帰関数に変わるからフィボナッチ数も計算できる、Java8版

| YコンビネータっていうかZコンビネータでラムダ式が再帰関数に変わるからフィボナッチ数も計算できる、Java8版を含むブックマーク YコンビネータっていうかZコンビネータでラムダ式が再帰関数に変わるからフィボナッチ数も計算できる、Java8版のブックマークコメント

Java8も出たことだし、ラムダ式で頑張ってみました。

たぶんこういうことだと思いますが、詳しくはきしださんに聞いた方がいいのでしょう。

おとうさん、ぼくにもYコンビネータがわかりましたよ! - きしだのはてな

下のコードQiitaにも置きました

(追記)rec.applyRec(rec)自分自身に名前を付けて呼び出してるからレギュレーション違反、と言われそうな気がしたので、ちょっとだけ修正変数で受けてるのは、キャストするのがアレだな、1行が長くなるのが嫌だな、と思っただけのこと。変数で受けずに1行で書くことは可能です。

package example;

import java.util.function.*;

public class Program {
  public static void main(String[] args) {
    Function<Integer, Integer> fib
      = Y(f -> n -> n > 1 ? f.apply(n - 1) + f.apply(n - 2) : n);
    System.out.println(fib.apply(8));
  }

  static <A, R> Function<A, R> Y(Function<Function<A, R>, Function<A, R>> f) {
    Recursive<A, R> rec1 = r -> a -> f.apply(r.applyRec(r)).apply(a);
    Recursive<A, R> rec2 = r -> a -> f.apply(r.applyRec(r)).apply(a);
    return rec1.applyRec(rec2);
  }
}

@FunctionalInterface
interface Recursive<A, R> {
  public Function<A, R> applyRec(Recursive<A, R> f);
}
トラックバック - http://d.hatena.ne.jp/matarillo/20140422

2014年04月18日

オープンソースは報われない仕事。でもやるんだよ。

| オープンソースは報われない仕事。でもやるんだよ。を含むブックマーク オープンソースは報われない仕事。でもやるんだよ。のブックマークコメント

Microsoftの中の人で、OSSとWeb技術を推進するScott Hanselmanが書いたブログ記事 "Open Source is a thankless job. We do it anyway." を勝手翻訳

オープンソースは報われない仕事。でもやるんだよ。

オープンソースは難しい。

セキュリティは難しい

OpenSSLの最近の "Heartbleed" バグに関する記事がたくさん出回っている。技術的な分析をすべて読んだら丸一日つぶれてしまうだろうが、 その中にこれはと思う見出しがあった。『OpenSSLはオープンソースの大きな問題を示している。資金不足、人員不足』だ。インターネットを織りなす基本の部分は、ほとんどの場合、たった一人と多くのボランティアによるものだ。

猝ハ播で気が遠くなるような事実とは、ネットワークインフラストラクチャにはこのように重大な部品存在していて、インターネットの大部分で実際に動いているのだけれど、基本的にはフルタイムで作業しているのはたった一人しかいないということだ。

さらに、あるソフトウェアが動作している間は、私たちは貢献者たちの努力成功を褒め称えたりはしない。その代わり、たった一行(重要な行の一つではあったが)が期待に応えなくなるのを待っている。このいまいましい無料の、おおむね動作する、世界規模で接続されたネットワークを動かしているソフトウェアめ。使えねーな。

オープンソースとは、おおむね報われない仕事だ。Microsoft .NETコミュニティの場合、時々むなしくなったりもする。ボランティアが全然見つからないことがあるのだ。多くの人々が、デフォルトのものやVisual Studioに同梱されているものを使う。RailsとかNodeの場合は、企業支援もあるが、プロジェクトはコミュニティが駆動しているという認識がある。現実は中間なのだが、Microsoftのスタック上に構築されたオープンソースプロジェクトでは、ボランティアが「我々は、出荷されたものをただ使うのみだ」なんて言うことがある。

マイクロソフトの過去の行動に対しては「怒り」が存在するけれども、前にも言ったように彼らは「長い」道のりを越えてきた。私はこれからもMicrosoftでオープンソースを推進していく。もう押すのは終わり!これ以上押せない!と思うまでは。内部では地殻変動が進んでいる。ミスはあったが正しい方向に進んでいる。誰もが学びの途中だ。

注目されるのは難しい

Jeremy Millerのチームは”FubuMVC”という.NETのオープンソースフレームワークのアクティブな開発を最近停止した。彼の最終的なブログ記事には、NETオープンソースの存続可能性に関する問題が書かれていた。

犲造リアルな疑問、つまり.NETのOSSとは見込みがある計画なのかという疑問を置いておくとしても(答えはおおむね否だ、いくらScott Hanselmanが声を枯らしてそんなことはないと言おうともね)、FubuMVCは失敗した。なぜなら我々は――多分ほとんどは私のせいなのだ、なぜならこれまでのところ最も目立っていたのは私だからだ――自分自身を売り込み、ブログ記事やドキュメントカンファレンスでの発表を通じてコミュニティを構築する努力が足りなかったからだ。

これはまさに真実をついている。多くのオープンソースプロジェクトにとっては、広い意味での可視性が存続可能性の元となっているのだ。Jeremyのふりかえりは素晴らしいのでぜひ読んでみてほしい。

大規模なフレームワークが既に存在しているところに、代替手段となるような大規模フレームワークのプロジェクトを立ち上げるのは難しいことだと思う。多くの人にとってはデフォルトを使用する方が簡単だからだ。FubuMVC、OpenRasta、ServiceStack、Nancyなどのようなフレームワークはすべて「新たにデフォルトを考え直した」ものだ。それらは(いい意味で)独断を行った大規模フレームワークなのであり、現状に甘んじないものたちだ。しかし、大規模フレームワークが支持者を育てるのは、HumanizerJSON.NET のような小さなライブラリよりもはるかに難しいことだ。

それでも、こういったプロジェクトがなければ我々はみんなデフォルトを使用したままだっただろうし、コミュニティとして新しいアイデアを探求したり限界ギリギリを攻めたりすることはなかっただろう。F#のFAKEビルドシステムや、Chocolateyや、Boxstarterみたいには。

MicrosoftはOSSプロジェクトを今よりよい方法でサポートできる。単にライセンスお金だけではなく、可視性においても。Microsoftの全カンファレンスで、オープンソースにトラック提供するよう提案したい。オープンソースコミュニティのメンバーがしゃべる枠を作るのだ。DotNetConfが手始めだが、拡大していけるはずだ。

組織化は難しい

OWINが良い例だ。小さいけれどとても重要なプロジェクトで、.NET界に影響を与えている。しかし組織化に苦労している。それをうまくやることは将来のために重要になってきている。コミュニティの中に小さいけれども影響力を持っているグループがあり、彼らは妥協点を見つけて技術的な問題にまつわるコンセンサスを構築しようとする努力を数か月間続けてきている。

ASP.NET Web APIとSignalRはどちらもOWIN (Open Web Interface in .NET) というオープンソースプロジェクトの上に構築されている。OWINはサーバ、フレームワーク、およびミドルウェアの密結合を分離することが目的だ。

GitHub上に開かれたままのissueがある。その問題は曖昧だがOWINに関して重要な点をついているように思われる。OWINの仕様にはIAppBuilderというインタフェースは含まれていないが、Microsoftのサンプルコードのほとんどにおいて、IAppBuilderはデフォルトで使用されている。根底を支えるOWINフレームワークは中立を保つことができるのか?このissueは長く、何度か横道に脱線している。複雑な問題であり、完全に理解しているのはおそらく20人ぐらいだろう。

Scott KoonはOWINのガバナンスドキュメントに関して頑張っていたが、建設的な動きを目にすることはなかった。彼は不満をTwitterで漏らした。当然のことではある。よく使われる「怠惰なコンセンサス」の技では、人々が沈黙しているか72時間以内に返信しなかった時は、事実上同意したものと見なし、プロジェクトの方向性を変更することができる。積極的な関与こそが重要なのだ。

オープンソースの華はプルリクエストコーディングだけれども、コードを作る前に合意を形成する必要がある。このプロセスの中で最も論争を生む部分は所有権だ。所有権とはコントロールのことだ。方向をコントロールすること。所有権の問題を通じて落としどころを見つけて作業するための鍵は、全員の異なる目標を徹底的に理解して共通のビジョンを見つけることだ。共通のビジョンによって、コミュニティが結集し前進できる。

このソーセージ製造工程は退屈な汚れ仕事だが、必要なのだ。OSSにおいてはコードが占めるよりも多くの部分をこれらの議論が占めている。押しと引きは同程度が必要だ。

関わることは難しい

私は毎週、何十通もの電子メールを受け取る。それらはすべて「オープンソースに関わるにはどうすればいいのでしょうか?」と尋ねるものだ。どうやら誰もが、私の回答は「コードを書け」とか「プルリクエストを送れ」とか、場合によっては「ドキュメント書きを手伝え」だろうと予想しているようなのだ。

実際には、やれることはそれがすべてではない。みんながやるべきことは、読むこと。吸収すること。理解すること。歓迎し、受け入れ、親切にすること。思慮深い分析を提供し、質問をすること。誇張や炎上を招くような言葉を避けること。issueにコメントする際はコード例を示すこと。人の手助けをすること。

ブログ記事はコミュニティを動かすエンジンなのである。オープンソースのコミットも、ドキュメント書きも、プロモーションも、サンプルも、講演も、コード片も、確かに重要なことだ。しかし、オープンソースに関わるということは、「プロジェクトをフォークして、あなたの世界観を反映する巨大なプルリクエストを送信する」ことを必ずしも意味しない。地味な作業が重要なことも時にはある。ガバナンスドキュメントを書いたり電話会議をまとめたり、GitHubの巨大なissueのスレッドで質問をする前に徹底的に読んだりすることが。

なぜこんなことをするのか?モテるためでも金のためでもない。我々が構築者だからだ。誰でもぜひ参加してほしい。やるべきことはたくさんある。

2014年03月21日

朝飯をねだるウナ

| 朝飯をねだるウナを含むブックマーク 朝飯をねだるウナのブックマークコメント

気づくと枕元にいる。そして噛む。噛むなよ。

photo