DIコンテナ使ってますか
海外のStack Overflowなどを見ていると、
"DI vs (Abstract) Factory Pattern"
"DI vs Service Locator Pattern"
という類の議論(質問)が多数見受けられる。
( 例えば: http://stackoverflow.com/questions/557742/dependency-injection-vs-factory-pattern )
回答の内容はごもっともである。(と思う)
結局のところ、DIコンテナ(ツール/ライブラリ)は使わずFactory等で解決している人が多いのかな。
かく言う自分も、どちらにすべきか度々迷っている・・・。
F# Quiz : プロパティ
F# Advent Calender 2015の4日目の、ぐるぐるさんの記事を拝見していました。
僕はお仕事で F# + ASP.NET MVC の組み合わせを使うことが多いのですが、やはりC#/V○前提のフレームワークを用いる時はどうしてもクラスをモリモリ使うことになってしまいます。その辺が少しもどかしい。
さて、記事の中ではメンバーの定義についてはあまり触れられていませんでしたが、F#にはプロパティの実装方法が沢山あります。そして厄介なことに、アクセスした時の振る舞いなどが異なります。でも、手のかかる子ほど可愛い。(?)
そこで、ちょっとF#クイズを用意してみました。
以下の4つのプロパティ*1を評価した時に、それぞれどのような結果となるか考えてみてください。
まずは脳内のF# Interactiveで実行してみてね。
type Quiz() =
let count =
let counter = ref 0
fun () -> incr counter ; !counterlet lazyCount = lazy count ()
member this.PropA = count ()
member val PropB = count ()
member this.PropC with get() = count ()
member this.PropD with get() = lazyCount.Force()
答えはfsiが教えてくれます。(手抜き)
それぞれ複数回評価してみると違いが分かるかもしれません。
現場からは以上です。
おまけ
はてなユーザの皆さん、彩りの存在も思い出してあげてください。(白目
http://irodori.azurewebsites.net/
*1:PropDはおまけです
恐怖のおやすみエラー
F# Advent Calender 2015の2日目の記事を拝見していました。
同名の型などを定義することによって、既存の型の使用を制限する方法が紹介されていました。
ふと、
「そういえば、CompileMessage属性なんてものがあったなぁ」
と思い出したので、別パターンを書いてみました。
以下のようにすると、(1)XmlDocumentクラスがインテリセンスに表示されなくなり、(2)それでも自力でインスタンスを生成しようとするとコンパイルエラーが発生するようになります。*1
namespace System.Xmlmodule private AA =
[<Literal>]
let oyasumi = """
_____
(^o^)ノ | < おやすみー |
\⌒⌒⌒ \ |
\|⌒⌒⌒⌒|
 ̄ ̄ ̄ ̄ ̄
"""
// インテリセンスに表示させないための属性
[<CompilerMessage("隠蔽のための属性", 12345, IsHidden = true)>]
type XmlDocument =
// コンストラクタにアクセスした時にエラーを発生させる
[<CompilerMessage(AA.oyasumi, 12345, IsError = true)>]
new() = {}
おしまい。
*1:IsErrorプロパティをfalseにすることによって、エラーを警告にすることも可能
Singletonパターンの「だらしない」実装
Singletonパターンに限らず、「初回アクセス時に初期化するプロパティ」などは比較的よく実装しますよね。
これを在り来りな方法で書けば、およそ以下のようになることと思います。
type Singleton private () =
do printfn "生成されました"static let mutable instance = None
static member Instance =
match instance with
| Some x -> x
| None -> let x = Singleton() in instance <- Some x ; x
> Singleton.Instance ;;これで、実際にアクセスされるまでインスタンス化を遅らせることができました。
生成されました
val it : Singleton = FSI_0007+Singleton
> Singleton.Instance ;;
val it : Singleton = FSI_0007+Singleton
今行っていたことは正しく遅延評価です。
然すれば、lazyを使って以下のようにだらしなく書くこともできます。
type LazySingleton private () =
do printfn "生成されました!"static let instance = lazy LazySingleton()
static member Instance = instance.Force()
> LazySingleton.Instance ;;
生成されました!
val it : LazySingleton = FSI_0007+LazySingleton
> LazySingleton.Instance ;;
val it : LazySingleton = FSI_0007+LazySingleton
結果はLazy
誰得ボールZ
F#には、独自サフィックスを定義する機能がある。
今が旬の、誰にも使われない超マイナー機能。
例えば、以下のように定義すると「Z」というサフィックスが有効になる。
どこか懐かしい、胸がパチパチするようなサフィックス。
ちなみに、サフィックスとして使用できる文字は「Q, R, Z, I, N, G」のみです。
モジュールも関数も名前が重要だそうです。
module NumericLiteralZ =
let FromZero () = "(´・_・`) バイバイ、天さん・・・"let FromOne () = "◯"
let FromInt32 = function
| n when (n >= 7) -> "いでよ神龍!🐲"
| n -> String.replicate (max 1 n) "◯"
> 1Z ;;
val it : string = "◯"
> 6Z ;;
val it : string = "◯◯◯◯◯◯"
> 7Z ;;
val it : string = "いでよ神龍!🐲"
ちょっと前に仕事でサフィックスの話が出ていたので、思い出したついでに書いてみました。
まとめ
使い道を教えて下さい。(衝撃
F#とIDisposable
F#におけるIDisposableの実装方法って、なんだか情報が少ないような気がします。
洋書や海外のサイトなどでは見かけますけどね。日本人F#erはシャイなので仕方ないです。(?
ということで、載せてしまおう。
以下のような感じで大丈夫だと思います。多分。
C#の実装とは所々異なりますので、その点はご注意ください。
cleanupManagedResources 関数と
cleanupUnmanagedResources 関数をカスタマイズしてお使いください。
type FSharpClass() =
(* ... *)member val private Disposed = false with get, set
member private this.Cleanup(disposing) =
let cleanupManagedResources () =
((* マネージド・リソースの後処理を書く *))
let cleanupUnmanagedResources () =
((* アンマネージド・リソースの後処理を書く *))if not this.Disposed then
this.Disposed <- true
if disposing then
cleanupManagedResources ()
cleanupUnmanagedResources ()
//base.Dispose(disposing) // 親クラスのDisposeを呼び出す場合interface IDisposable with
override this.Dispose() =
this.Cleanup(true)
GC.SuppressFinalize(this)override this.Finalize() = this.Cleanup(false)
本日のまとめ
> 社畜.Dispose() ;;
不完全なパターンマッチを絶対に許さない!方法
「パターンマッチが不完全な場合に、警告ではなくコンパイルエラーにする方法はあるのですか?」と個人的にご質問を頂いたので、回答のついでにブログにも書いてみることにしました。随分と久しぶりの記事になってしまいました。。
結論から言うと、コンパイラオプションを指定する方法があります。
まず、以下のようなコードがあったとします。
type Foo = A | B | C let foo = function | A -> "A" | B -> "B"
このコードをコンパイルしてみると、以下のように警告されるものの、一応コンパイルは成功します。
警告 FS0025: この式のパターン マッチが不完全です たとえば、値 'C' はパターンに含まれないケースを示す可能性があります。この場合、foo C として評価すると例外(Microsoft.FSharp.Core.MatchFailureException)が発生してしまいます。
以下、コンパイラオプションの指定方法です。
Visual Studio の場合
まず、ソリューションエクスプローラーからF#プロジェクトのプロパティを開きます。
そして、[ビルド]タブを見てみると[警告をエラーとして扱う]という、
それっぽい雰囲気が出ている欄があります。
胸の高鳴りを抑えつつ[特定の警告]を選択してみると、入力欄が出現します。
そこに、対象となる警告番号の25という値を入力し、プロパティを保存します。
この状態でビルドしてみると、今度はコンパイルエラーが発生するようになります。
エラー FS0025: この式のパターン マッチが不完全です たとえば、値 'C' はパターンに含まれないケースを示す可能性があります。
まとめ
北海道寒い。