使いたくない型を見えなくする #FsAdvent

この記事はF# Advent Calendar 2015 - connpassの2日目です。

同僚へのイタズラネタを紹介したいと思います。 AutoOpenと組み合わせて混乱に陥れてやりましょう。 標準ライブラリの中には、古くなったり、挙動が紛らわしいという理由で使うべきではないクラスや関数があります。 どこかのサイトから当時は適切だった古いコードを参考にしてきて、うっかり使ってしまう人もいるでしょう。

こんな困った人のコードはコンパイルエラーにしてやる!

一部のメンバのみ制限する

例えば、System.Text.Encoding.UTF8プロパティとSystem.Text.UTF8Encodingクラスの2つで、BOMがどう扱われるか直ぐに言えますか? System.Text.Encoding.UTF8はBOMが付きます。一方、System.Text.UTF8EncodingコンストラクタでBOMの有無を指定できます。 BOMを扱わないシステムを扱っている場合は、System.Text.Encoding.UTF8をなるべく使ってほしくないですね。

module System.Text.Encoding
let UTF8 = ()

このようなモジュールを定義すると、System.Text.Encoding.UTF8の定義を上書きできます。 そして、System.Text.Encoding.UTF8を使おうとすると型が合わずコンパイルエラーになってしまいます。

コンパイルエラーがやり過ぎだと思ったら、ObsoleteAttributeを付けて警告にもできます。

module System.Text.Encoding
let [<System.Obsolete>]UTF8 = System.Text.Encoding.UTF8

この方法はUTF8プロパティのみ上書きされ、他のメンバは普段どおり使用できます。

使ってほしくないクラス、モジュールを制限する

次の例は、.NetにはXMLを扱うには2つの方法があります。 古くからあるSystem.Xml名前空間と、Linq to XmlSystem.Xml.Linq名前空間の2つです。 System.Xml名前空間にあるクラスは使いづらいので、プロジェクト全体で使わせたくない上に、何も指示しないとSystem.Xmlのクラスを使う人までいます。

使わせたくない!コードレビューの時では遅いのだ!

先程は同名のモジュールを定義して、同名のメンバを定義しましたが、今度は同名のクラスを定義します。

namespace System.Xml
type XmlDocument private() = class end

もともとのSystem.Xml.XmlDocumentにアクセスしようと思っても、上記の定義が邪魔をして使えなくなってしまいます。

open System.Xml
let x = XmlDocument() (* この型にアクセスできるオブジェクト コンストラクターはありません *)

めでたしめでたし。

まとめ

こういうのはLintの仕事だと思いました。