かずきのBlog@hatena

すきな言語は C# + XAML の組み合わせ。Azure Functions も好き。最近は Go 言語勉強中。日本マイクロソフトで働いていますが、ここに書いていることは個人的なメモなので会社の公式見解ではありません。

F#で思ったことを徒然なるままに書いてみた

F#という言語を最近遊び感覚で使ってますが、まだまだIDEのインテリセンスもイマイチだったりするところがあって、開発環境的には不満が割とあります。でも、それ以上にLL言語を彷彿とさせる(特にPython)ライトっぷりには脱帽です。

メインパラダイム

F#は、オブジェクト指向型の言語でもあり関数型の言語でもあります。最近のC#オブジェクト指向型の言語でもあり、一部関数型言語のいいところを取り入れる感じで進化していってます。この2つの言語は何が違うのか?

C#オブジェクト指向に主軸を置いていて、F#は関数型に主軸を置いているところだと思います。メインターゲットとするパラダイムが違います。F#は、関数型言語が持つ多くの強力な機能をバックのロジックに使用しつつ、現在主流のオブジェクト指向言語からも使えるように関数型言語固有のインターフェースをオブジェクト指向言語で扱いやすいクラスという形で表現することが出来ます。

F#でOOP

これは個人的な感覚ですが、関数型言語固有の表現をオブジェクト指向型の言語から使えるように表現する程度にとどめるのが現状ではいいように思います。何故なら、F#がベースクラスとなるようなクラスを作成するときに必須になるであろうprotectedの可視性をサポートしていないからです。

ただし、F#でprotectedの可視性を定義できないだけでC#などで作られたクラスを継承してprotectedなメソッドを使ったりオーバーライドしたりといったことは普通に出来ます。あくまでオブジェクト指向プログラミングのベースとなるようなクラスの定義に向かないということです。

使い分け

先日、F#の簡潔な記述と非同期ワークフローを見て思いついたのでKinkuma Framework(C#で作成)をベースにF#でMVVMパターンのViewModelとModelを組むためのクラスライブラリを作りました。小さなライブラリですが1つのアセンブリではなく2つのアセンブリで構成されています。(本当は1つにまとめたかった・・・)

何故かというと、ViewModelの基本クラスにはprotectedなメソッドが欲しかったけど上記で説明した通りF#ではprotectedなメソッドが使えません。なので、F#で使うことを意識しながらC#でコードを書くということをやっています。例えば以下のようなコードです。

namespace Okazuki.MVVM.PrismSupport.FSharp
{
    using System;
    using System.Collections.Generic;
    using System.Windows.Input;
    using Microsoft.FSharp.Quotations;
    using Microsoft.Practices.Prism.Commands;
    using Microsoft.Practices.Prism.ViewModel;
    using System.Diagnostics;

    public abstract class FsViewModelBase : NotificationObject
    {
        // 省略...

        protected void set<T>(ref T field, T value, FSharpExpr<T> expression)
        {
            var r = PatternsModule.PropertyGetPattern(expression);
            try
            {
                var propertyName = r.Value.Item2.Name;
                this.set(ref field, value, propertyName);
            }
            catch (NullReferenceException ex)
            {
                throw new ArgumentException("expressionにはプロパティ構文しか指定できません", ex);
            }
        }

        protected virtual void set<T>(ref T field, T value, string propertyName)
        {
            if (Equals(field, value))
            {
                return;
            }

            field = value;
            this.RaisePropertyChanged(propertyName);
        }

        // 省略...
    }
}

setメソッドの引数にFSharpExprを受け取るようにしています。そして内部でPatternsModule.PropertyGetPattern(expression)というF#のモジュールの関数を呼び出しています。これはF#で書くと以下のような感じになります。

// 同じ名前の関数は定義できないので別名で定義
let setProperty (field : 'T byref) value propertyName =
    if field <> value then
        field <- value
        propertyChangedEvent.Trigger(PropertyChangedEventArgs(propertyName))

let set (field : 'T byref) value expression =
    // F#の機能をつかってすっきり!!
    match expression with
    | Quotations.Patterns.PropertyGet(_, p, _) -> 
        setProperty &field value p.Name
    | _ -> raise (System.ArgumentException("expressionにはプロパティ構文しか指定できません"))

ただ、これもprotectedなメソッドを提供したいという要求からC#で実装しました。しかし、非同期ワークフローのようなF#固有の機能を使うような箇所はF#で定義しています。こんな風に、どんなものを作りたいのか必要に応じて使い分ける必要があると思います。

結局何がいいたいの

F#は関数型メイン。C#オブジェクト指向メイン。この2つ適材適所で使うのが今の所向かう所敵なしだと思うのです!
F#もC#もステキな言語ですね!!