静的Duck Typingでプロパティにアクセスする

F#には静的Duck Typingを行うための(非常にマイナーな)(そして難解な)仕組みがありますが、
プロパティ(setter)にアクセスする方法は少し特殊です。
マイナーすぎて多分ググってもほとんどヒットしないと思うので、一応サンプルを載せておきます。


結論から言うと、setterのシグネチャを指定する際には

when ^a : (member set_MyProperty : int -> unit)

のようにします。



これを応用して、色んな型の"Value"にアクセスするサンプルコードを書いてみました。


// 普通のプロパティを持つ普通のクラス
type MyClass(value) =
member val Value : int = value with get, set

// 変更可能なラベルを持つレコード型
type MyRecord = { mutable Value : int }

// 仕様が異なる(という想定の)クラス
type YourClass(value) =
member val StringValue : string = value with get, set

// 型拡張を変換アダプタ的に使って仕様を合わせる
type YourClass with
member this.Value with get() = int this.StringValue
and set(x:int) = this.StringValue <- string x


// Valueプロパティのgetterから値を取得する関数
let inline getValue (x:^a when ^a : (member Value : int)) =
(^a : (member Value : int) x)

// 上記のgetValueの引数xは型推論可能なので以下のようにも定義できる
let inline getValue' x =
(^a : (member Value : int) x)

// Valueプロパティのsetterを呼び出す関数(今回の主役)
let inline setValue x value =
(^a : (member set_Value : int -> unit) x, value)

let inline increment x = getValue x + 1 |> setValue x

let inline (++) x y = x + getValue y

let inline print x = getValue x |> printfn "Value = %d"


let test () =
let myClass = MyClass(9)
let myRecord = {Value = 99}
let myRef = ref 999
let yourClass = YourClass("9999")

increment myClass
increment myRecord
increment myRef
increment yourClass

print myClass
print myRecord
print myRef
print yourClass

1 ++ myClass ++ myRecord ++ myRef ++ yourClass

> test () ;;
Value = 10
Value = 100
Value = 1000
Value = 10000
val it : int = 11111


レコード型のmutableなラベルに対しても使用することができました。
また、参照セルRef<'T>にはもともとValueというメンバが定義されているのでmyRefにも適用可能です。


YourClassは、「他と若干仕様の異なるクラス」をイメージしたもので、型拡張を変換コネクタ的に使ってincrement関数が適用できる形にしています。(Adaptorパターンの簡易版)


正直あまり利用シーンは思い浮かばないですが、フレームワーク等の制約/ルールによってシグネチャが強制されるようなメンバに関しては使えるかもしれません。(継承関係には無いが、リフレクションを使って動的にメンバを特定しなきゃいけないような場面)

全角・半角・ひらがな・カタカナ

アルファベットの大文字/小文字を区別せずに文字列を比較するのはよくあることですが、
全角・半角・ひらがな・カタカナに対応する方法もあるようです。


以前せっかく調べたので忘れないようにメモしておこう。


open System.Globalization

let contains (value:string) source =
let compareInfo = CompareInfo.GetCompareInfo("ja-JP")
let options =
CompareOptions.IgnoreCase // 大文字・小文字区別なし
||| CompareOptions.IgnoreKanaType // ひらがな・カタカナ区別なし
||| CompareOptions.IgnoreWidth // 全角・半角区別なし

compareInfo.IndexOf(source, value, options) > -1

let test () =
"雨ニモマケズ" |> contains "まけず" |> printfn "%b" // true
"全角でF#" |> contains "F#" |> printfn "%b" // true
"F♯" |> contains "F#" |> printfn "%b" // false

F#とAzure Functions

Azure Functionsがなかなか便利で、すでに仕事で何度か利用しています。
ご紹介も兼ねて簡単なサンプルを載せておきたいと思います。
ここではHTTPリクエストに対して自分自身を送り返すQuine的な関数を作ってみます。

前提知識やメモ

  • run.fsxに処理を記述する
  • Azureポータル上のエディタが意外と高機能なので簡単な関数ならブラウザだけで書けちゃう
  • run関数のシグネチャは変更できない(引数名を変えただけで怒られた)
  • Azure Functionsが参照しているFSharp.Coreのバージョンがちょっと古いんじゃないか疑惑
  • グローバル変数を定義すると実行時に例外が発生(原因がよく分かっていない)
  • 初回アクセス時は反応が遅い
  • cron的なタイマー付き関数も簡単に実装できる
  • Async.Parallelが使えた(検証は不十分だが結構うれしい)

関数の下準備

Azureポータル上で、[HTTP trigger][F#][Name:Quine][Authorization level:Anonymous]という設定で関数を作成します。

実装

次のようなスクリプトを書きます。
__SOURCE_DIRECTORY__以下にあるrun.fsxファイル(自身)を読み込んでレスポンスとして返しています。


#r "System.Net.Http"

open System.IO
open System.Text
open System.Net

let run (req:HttpRequestMessage, log:TraceWriter) =
async {
let code =
Path.Combine(__SOURCE_DIRECTORY__, "run.fsx")
|> File.ReadAllText

let response = req.CreateResponse(HttpStatusCode.OK)
response.Content <- new StringContent(code, Encoding.UTF8, "text/plain")
return response
}
|> Async.RunSynchronously

動作確認

Azureポータル上でも動作テストが行えますが、関数のURLを取得すると実際にアクセスすることができます。
今回作成した関数のサンプルがこちら。
https://hellofsharp.azurewebsites.net/api/Quine

はじめての Azure "F#" Notebooks

Azure Notebooksを試してみました。*1
Azure Notebooks(Jupyter)は本来、検証や実験の結果を共有したり、試行錯誤の記録を残す目的で使われることが多いのではないかなと思いますが、技術的なドキュメントを対話的に作成する際にも便利そうな予感がビシビシバシバシしています。


ということで、試しにF#の列挙体の解説ノートを作成してみました。*2
https://notebooks.azure.com/Nobuhisa/libraries/fsharp の中にあるenum.ipynbというノートがそれです。(ノート単位での共有の仕方が分からない。。)
Azure NotebooksもまだPreview段階ですので突発的にUIや仕様が変わったりすることもありましたが、やはりドキュメント作成はとてもラクでした。


HTMLやMarkdownでの出力もできます。PDFで出力しようとしたらエラーになってしまいました。祈ります。
出力したHTMLをGitHub Pagesで公開してみました。
https://nobuhisa.github.io/FSharp-Notebooks/Jupyter/enum.html
なかなかいい感じ。

*1:試したのはずいぶん前なのですが、ブログに投稿するのを忘れていた...

*2:列挙体を選んだのは気分です。

実行制限付きの並列計算

個人的にご質問をいただいたので、簡単なサンプルを書いてみました。
「たくさんの処理を並列に実行したいのだけど、同時に走るスレッド数は制限したい」というプログラムです。



let worker (id: int) = async {
System.Console.WriteLine("Worker:{0}", id) // 並列環境ではprintfnは使用しない
do! Async.Sleep(1500)
}

[<EntryPoint>]
let main argv =
List.init 15 worker // 15個の処理をしたい
|> List.chunkBySize 3 // 3つずつ並列化したい
|> List.iter
(Async.Parallel >> Async.Ignore >> Async.RunSynchronously)

printfn "completed"
0


実行すると、1.5秒ごとに3行ずつ表示されます。

Worker:1
Worker:0
Worker:2
Worker:3
Worker:5
Worker:4
Worker:6
Worker:7
Worker:8
Worker:9
Worker:11
Worker:10
Worker:12
Worker:14
Worker:13
completed


F#ではasync式によって構築された計算はオブジェクトのように扱うことができます。
サンプルでは、計算15個のリストを生成し、それを3つずつに分割して並列実行しています。
3つの式がほぼ同時に実行されますが、RunSynchronouslyメソッドで結果を待機しているので、その3つの計算が完了してから残りの式が実行されることになります。(以降はその繰り返し)

ワーカープロセスとstatic変数に関してのメモ

asp.net - IIS app pools, worker processes, app domains - Stack Overflowより:

In a server you can have many asp.net sites that runs together. Each one site is an app domain.


You must assign to each of them one application pool. Many application domains (sites) can have the same application pool, and because they have the same application pool they run under the same processes, and under the same account - and they have the same settings of the pool. If this pool restarts, then all sites under that pools restarts.


Now each pool can have one or more worker process. Each worker process is a different program that's run your site, have their alone static variables, they different start stop calls etc. Different worker process are not communicate together, and the only way to exchange data is from common files or a common database. If you have more than one worker process and one of them make long time calculations, then the other can take care to handle the internet calls and show content.


When you assign many worker process to a single pool then you make the called web garden and your site is like to be run from more than one computer if a computer is one processing machine.


Each worker process can have many threads.


How the more worker process affect you:
When you have one worker process everything is more simple, among your application all static variables are the same, and you use the lock to synchronize them.
When you assign more than one worker process then you still continue to use the lock for static variables, static variables are not different among the many runs of your site, and if you have some common resource (e.g. the creation of a thumbnail on the disk) then you need to synchronize your worker process with Mutex.


RazorEngineでstatic変数を当たり前のように使用していたので、ASP.NET(MVC)に乗せた際にどうなるのだろうと思って調べていました。
static変数はワーカープロセス内で共通なのですね。安易に使うのはちょっとこわい。
そういえば、HttpContext.Currentもstatic変数(プロパティ)だけど、あれは一体どういう扱いになってるんだろう。プロパティがstaticだというだけで、値が使い回されるわけではないですよね。(たぶん)


ASP.NETの内部についてもっと勉強しないとなぁ。
Web上で拾える情報に少し限界を感じたので、やはり書籍が必要になりそう。

ASP.NET Web API でファイルをアップロード

Web API経由でファイルをアップロードするサンプルをGitHubにpushしました。
自分用の備忘録も兼ねて。。(たまにやり方を忘れる)
https://github.com/Nobuhisa/FsWebApiSample


メインとなるファイルは以下の2つ。
サーバ : FileController.fs
クライアント : Program.fs


今回、サーバサイドではファイルを保存する際に
MultipartFormDataStreamProviderを使用していますが、
例えばAzure Storageに保存したい場合は
MultipartFormDataRemoteStreamProvider を継承して
GetRemoteStream メソッドをオーバーライドします。
※ BlobのストリームをRemoteStreamInfoで包んで返してやる感じ


クライアントのほうは、
MultipartFormDataContentにファイル等を登録した後に、
HttpClientのPostAsyncを使ってアップロードしています。


おしまい。