F# WebSharperで関数型的ウェブ開発

F# Advent Calendar 2011 の4日目の参加エントリーです。
詳しくは http://partake.in/events/1c24993a-c475-4fc2-bca4-7a1cd2f81869 を。

本当は F# 3.0 の TypeProvider 機能を使って WPF のカスタムプロバイダーを紹介しようと思ったんですが、
微妙にまだ未完成なのでやめときます。

ということで今日は WebSharper やります。
※ 3日目担当の liner_lock さんの記事見たときは、かぶってるかと思って心臓止まるかと思いました。事実ちょっとかぶってますけど・・・。

WebSharperとは?

通常、.NET で Web アプリケーションを作成するとしたら
ASP.NETASP.NET MVC を使うかと思います。
ASP.NET(MVC) でもF#は使えないこともないですが、
View は結局のところ HTML や Javascript を使わなければいけないんですよね。
MVC3からサポートされている Razor でも通常ではF#は使えません。

今日紹介する WebSharper は Markup-less でクライアントベースのWebサイトを作成することができます。

さらに下記のような優れた特徴を備えています。

まずはインストール

WebShaper はここからダウンロードできるのでVisual Studio 2010が入った環境で適当にインストールしてください。
http://www.websharper.com/

ここで一つ注意点です。
現時点の WebSharper は Visual Studio 2012 および .NET 4.5 がインストールしている環境では使えません。
すでに入れちゃったって人は頑張ってアンインストールしてから楽しんでください。

Hello World の HTML を生成してみよう

VS2012 をアンスコして WebSharper をインストールできたでしょうか?
ではまず Hello World を作ってみましょう。

まずWebSharperのプロジェクトは次の項目を選択してください。

  • IntelliFactory > WebSharper 2.3 HTML Application (Sitelets)

このプロジェクトはサーバーサイドなしの HTML を生成するプロジェクトです。
※ サーバーサイド付のWebアプリの場合は WebSharper の Rpc、またはASP.NET, ASP.NET MVCを使うこともできます。

プロジェクトを作成すると最初に Main.fs というファイルが生成されるので下記のコードに書き換えてください。


namespace HelloWorld

open IntelliFactory.WebSharper.Sitelets

module Site =
open IntelliFactory.WebSharper
open IntelliFactory.Html

type Action = | Index

let Index =
PageContent <| fun context ->
{ Page.Default with
Title = Some "Index"
Body = [Text "Hello"]
}
let MyActions =
[
Action.Index
]

type HelloWorldWebsite() =
interface IWebsite with
member this.Sitelet =
Sitelet.Content "/" Action.Index Index
member this.Actions = [ Action.Index ]

[)>]
do ()

これをF5で実行させると
index.htmlというファイルが出力されるので確認してみてください。
以降は、このコードのBodyを書き換えて説明していきます。

WebSharper による Web アプリケーション開発

具体的に WebSharper でどのように Web アプリケーションを開発していくかを説明していきます。

静的な HTML の生成

HTML の要素と属性は IntelliFactory.Html.Tags と IntelliFactory.Html.Attributes モジュールの関数として提供されており、
たとえば下記の式は div 要素 を表しています。


Div []

Tags, Attributes の関数は INode< a > を継承しており、また Tags の関数は引数として INode< a > の seq を取るので
下記のように入れ子で HTML を表すことができます。
オフサイドルールやパイプライン演算子を上手く使ってね。

Div [
Div [ H1 <| [Text "H1の要素だよ"] ]
Div [ H2 <| [Text "H2の要素だよ"] ]
]

ですが属性を与えるためにはちょっと注意が必要です。
F#には暗黙の型変換が存在しないので Attributes の関数と Elements 関数の戻り値が 同じ INode< a >だからといって
下記のように一緒に引数に渡そうとするとエラーになってしまいます。

Div [
Attributes.Id "h1-div"
H1 [Text "H1の要素だよ"]
]

もちろんキャスト演算子を使ったり型注釈を加えればいい話なのですが、
それではさすがにめんどくさ過ぎるので、それを回避するための ( -< ) という コンビネータが用意されていますのでそれを使って下記のように直しましょう。
ちなみに Haskell の Arrow 記法 とは多分関係ありません。

Div [ Attributes.Id "h1-div" ] -<
[ H1 [Text "H1の要素だよ"] ]

さてこれで OnClick 属性で javascript 関数名を指定できるようになりましたが
単純に OnClick "MyJavascriptFunction" みたいに普通の javascriptを指定できてもあまりうれしくないですよね。
ここからは、お待ちかねの JavascriptからF#の関数呼び出し
再帰関数でもパターンマッチでもできますよ〜

javascript関数

まずは次のようなモジュールを作成します。


module Controls =
open IntelliFactory.WebSharper
open IntelliFactory.WebSharper.Html

type HelloButton() =
inherit Web.Control ()

[]
override this.Body =
let result = Div []
Div [
Button [Text "Click"]
|>! OnClick (fun e args -> Text "add text" |> result.Append)
result
]
:> _


ここで open されている IntelliFactory.WebSharper.Html 名前空間 は IntelliFactory.Html 空間 とは別物で、
さらに Button 関数などの HTML 要素関数も 前者と後者の名前空間では別の関数であるということに気をつけてください。
(|>!)は let ( |>! ) x f = f x; x のように定義されていて、OnClick 関数のような unit を返す関数を使用する際に便利です。

次は今作った HelloButton を Body に埋め込みます。


[ Div [ new Controls.HelloButton ()] ]

このように Web.Control 型を継承して作成した型は インスタンシエートすると 静的な HTML に埋め込むことができます。
これを実行して生成された HTML を見てみると ボタンが一つだけあって クリックすると "add text" という文字列が DOM に追加されたかと思います。
ソースを見てみるとわかるとおり IntelliFactory.Html 空間のものはそのまま HTML になって IntelliFactory.WebSharper.Html 名前空間のものは WebSharper の javascript で生成されていることがわかりますね。
ここまでの内容でクライアントサイドの基礎はばっちりです。

便利機能

WebSharper は Control を簡単に生成するための機能を標準で提供しています。
その一つが Formlet です。
Webアプリで Form を作るのって非常にめんどくさいですよねー。
整列させて、ラベルつけて、検証機能をつけて・・・。
WebSharper では次のように簡単に Form をつくることができます。


module Controls =
open IntelliFactory.WebSharper
open IntelliFactory.WebSharper.Html
open IntelliFactory.WebSharper.Formlet
open IntelliFactory.WebSharper.Formlet.Controls
open IntelliFactory.WebSharper.JavaScript

[]
let form =
Formlet.Yield id
<*> Input ""
|> Enhance.WithSubmitButton

type HelloForm() =
inherit Web.Control ()

[]
override this.Body =
form.Run Alert


この Form には input と submit があって submit すると alert に入力された内容を alert で表示するだけの簡単なものです。
Formlet.Yield 関数の第一引数には、合成する input の値を複数個、受け取って一つの値にする関数を指定します。
そこで返された値が Run メソッドの関数の引数となります。
ここでは単に string -> string なので id 関数を指定しています。
もちろん レコード型の値を返す関数を指定することができます。

もう少し複雑な Form を作成してみます。


module Controls =
open IntelliFactory.WebSharper
open IntelliFactory.WebSharper.Html
open IntelliFactory.WebSharper.Formlet
open IntelliFactory.WebSharper.Formlet.Controls
open IntelliFactory.WebSharper.JavaScript

type PersonalInfo = {
Name : string
Age : int
}

[]
let form =
Formlet.Yield (fun name age -> { Name = name; Age = System.Int32.Parse age })

<*> (Input "佐藤祐介"
|> Enhance.WithTextLabel "名前"
|> Validator.IsNotEmpty "is not empty")

<*> (Input "100"
|> Enhance.WithTextLabel "年齢"
|> Validator.IsNotEmpty "is not empty"
|> Validator.IsInt "is int")

|> Enhance.WithSubmitButton

type HelloForm() =
inherit Web.Control ()

[]
override this.Body =
form.Run (fun person -> person.Name + "さんは" + person.Age.ToString () + "才です" |> Alert)


これで レコード型の Form を作成できました。
しかも検証機能もつきました。
Enhance, Validator モジュール超便利ですね!

Formlet には コンピュテーション式ビルダーが用意されていますが、
みんな大好き Applicative スタイルもサポートされているので安心ですね!

RPC

サーバーサイドでの処理を実行させたいときは RPC という機能を使うことができます。
ただしこれをやるときは当たり前ではあるが IIS でホスティングしないといけなく
今日はめんどくさいので、また今度詳しく書こうかと思います。
もちろん WebSharper のRPCを使わなくても ASP.NET や ASP.NET MVC との連携でサーバーサイド処理をすることも可能です。

不満など

  • 動的に生成される HTML がひどすぎる。Tableがネストしまくり。
  • Control の中にうっかり override 以外の関数を作ったりすると、ビルド時に意味のわからないエラーを吐いてくれる。
  • しょうがないけどビルドが超遅い
  • まだまだ実績が少なく情報も少ない。日本語の情報は皆無。

最後に

いままで記事を書くと、「端折りすぎだ!」っていつも言われてたので
今回は割りと丁寧に書いたつもりです。


あと今日はもう疲れたので F# Type Provider で WPFはまた今度書きます。
bleis先生ごめんなさい。
というか、ソースを載せておくので誰か治し方おしえてください。 → https://gist.github.com/1428096


次の F# Advent Calendar は まえしー こと @maeda_ です。
ではでは〜

F# 3.0 Type Provider 正規表現のCompile-time構文チェック

アローハ マハロー


さてType Provider第一弾として紹介するのは
正規表現のCompile-time構文チェック」です。

このように第一型引数で文字列をパターン文字列を渡すことでコンパイル時にパターン文字列の構文チェックを行えます。

type CheckRegex= Regex<".*">

もし不正なパターン文字列が指定された場合は、下記のようなユーザーライクなエラーメッセージが表示されます。



下記がRegex Type Providerのコードです。

namespace TypeProviders.DesignTime

open System
open System.Collections.Generic
open System.Linq.Expressions
open System.Globalization
open System.Reflection
open System.Diagnostics
open Microsoft.FSharp.Core.CompilerServices
open Internal.Utilities.TypeProvider.Emit
open System.IO
open System.Text.RegularExpressions

[<assembly: TypeProviderAssembly>]
do()

type ProvidedType(name, ns, assembly) =
  inherit Type()
  let mutable IsErasedFlag = 1073741824
  let mutable attributes = (TypeAttributes.Public ||| TypeAttributes.Sealed ||| enum IsErasedFlag) 
  override x.GetConstructorImpl(flags: BindingFlags, binder: Binder, cc: CallingConventions, paramTypes: Type[], modifiers: ParameterModifier[]) =  null
  override x.GetMethodImpl(name: string, flags: BindingFlags, binder: Binder, cc: CallingConventions, paramTypes: Type[], modifiers: ParameterModifier[]) = null
  override x.GetPropertyImpl(name: string, flags: BindingFlags, binder: Binder, t: Type, paramTypes: Type[], modifiers: ParameterModifier[]) = null
  override x.GetAttributeFlagsImpl() = attributes
  override x.IsArrayImpl() = false
  override x.IsByRefImpl() = false
  override x.IsPointerImpl() = false
  override x.IsPrimitiveImpl() = false
  override x.IsCOMObjectImpl() = false
  override x.HasElementTypeImpl() = false
  override x.GetMembers(flags: BindingFlags) = [||]
  override x.GUID = Guid.Empty
  override x.InvokeMember(name: string, flags: BindingFlags, binder: Binder, instance: obj, parameters: obj[], modifiers: ParameterModifier[], culture: CultureInfo, names: string[]) = null
  override x.Module = null: Module
  override x.Assembly = assembly
  override x.FullName = sprintf "%s.%s" ns name
  override x.Namespace = ns
  override x.AssemblyQualifiedName = sprintf "%s.%s, %s" ns name assembly.FullName
  override x.BaseType = null
  override x.GetConstructors(flags: BindingFlags) = [||]
  override x.GetMethods(flags: BindingFlags) = [||]
  override x.GetField(name: string, flags: BindingFlags) = null
  override x.GetFields(flags: BindingFlags) = [||]
  override x.GetInterface(name: string, inherited: Boolean) =  null
  override x.GetInterfaces() = [||]
  override x.GetEvent(name: string, flags: BindingFlags) = null
  override x.GetEvents(flags: BindingFlags) =  [||]
  override x.GetProperties(flags: BindingFlags) = [||]
  override x.GetNestedTypes(flags: BindingFlags) = [||]
  override x.GetNestedType(name: string, flags: BindingFlags) = null
  override x.GetElementType() = null
  override x.UnderlyingSystemType = null
  override x.Name = name
  override x.GetCustomAttributes(inherited: Boolean) = [||]
  override x.GetCustomAttributes(t: Type, inherited: Boolean) = [||]
  override x.GetCustomAttributesData() = new ResizeArray<_>() :> IList<_>
  override x.IsDefined(t: Type, inherited: Boolean) = false
  override x.IsSecurityCritical with get() = false
  override x.IsSecuritySafeCritical with get() = false
  override x.IsSecurityTransparent with get() = false
  override x.GetDefaultMembers() = [||]
  override x.GetEnumValues() = [||] :> Array
  member x.IsErased
    with get() = (attributes &&& enum IsErasedFlag) <> TypeAttributes.NotPublic
    and set value =
      if value then
        attributes <- attributes ||| (enum IsErasedFlag)
      else
        attributes <- attributes &&& ~~~ (enum IsErasedFlag)

[<TypeProvider>]
type TypeProviderRoot() =
  let theAssembly = typeof<TypeProviderRoot>.Assembly
  let invalidate = new Event<EventHandler,EventArgs>()
  let ns = "TypeProviders"
  let regexType = ProvidedType ("Regex" , ns, theAssembly, IsErased = true)

  interface ITypeProvider with
    [<CLIEvent>]
    member x.Invalidate = invalidate.Publish
    member x.GetNamespaces() = [| x |] 
    member x.GetStaticParameters(typeWithoutArguments: Type) = 
      [| new ProvidedParameter("pattern", typeof<string>) |]
    member x.ApplyStaticArguments(typeWithoutArguments: Type, typeNameWithArguments: string,  staticArguments: obj[]) = 
      new Regex(staticArguments.[0] :?> string) |> ignore
      ProvidedType (typeNameWithArguments , ns, theAssembly, IsErased = true) :> Type
    member x.GetInvokerExpression(syntheticMethodBase: MethodBase, parameters: ParameterExpression[]) = null: Expression 
    member x.Dispose() = ()
  interface IProvidedNamespace with
    member x.NamespaceName with get() = ns
    member x.GetNestedNamespaces() = [||]
    member x.GetTypes() = [| regexType |]
    member x.ResolveTypeName(typeName: string) = regexType:>Type

今回のコードはbleisとの合作です。
Enjoy F# 3.0 !

F# 3.0 Type Provider 妄想は確信に近づく

アローハ グッモーニン


さて、F# 3.0の新機能 Type Providerについての新報です。
目下調査中ですが、まだType Providerの作成には成功していません。


現在わかる限りの情報を書きたいと思います。
Type Providerの作り方です。

  1. FSharp.Data.TypeProviders.dllを参照する。
  2. カスタム属性 TypeProviderAssembly をつける
  3. ITypeProviderを実装する型はカスタム属性TypeProviderをつける
  4. System.Typeを継承した型を作成する。
  5. TypeProviderは4の型を使って実装する。

これで基本的にはいいはずですが、まだ具体的なところはわかりません。


これまででわかったことですが、どうやらType Providerで受け取れる型引数は、任意長でかつ任意の型みたいです。
これが意味していることは型レベルプログラミングができるということです。
Type Providerで実現できそうなことを列挙してみました。

  1. compile-timeな数値計算(簡単な四則演算からcheck-sumや暗号化まで)
  2. 正規表現のcompile-time構文評価。またMatchのメンバーを生成する。
  3. WPFでViewModelを生成(ViewからBindingのためのプロパティを生成する など)
  4. Compile-Time Sleepメタ関数を作る



Type Converterはこれまでのコードの自動生成に代わるさらにエレガントな手段です。
C++0xD言語を凌ぐ新たなメタプログラミング時代の幕開けになることは間違いないでしょう。


現在、bleisと調査中なので新たなことがわかり次第報告します。
続報を待て

F#3.0の新機能

一部方憶測で書きますので、承知ください。


F# 3.0では Type Provider という機能が追加されます。
これはどういったものかというと、型パラメータを持つ任意の型を提供できる機能です。


例えば下記のような型があったとします。


type Netflix = ODataService<"http://odata.netflix.com">

この型はそのURIのODataスキーマがdesign-timeで認識してその型が、定義されます。
これまでCodeGeneratorやCustomToolで自動生成していたものが
それらの呼び出しをdesign-timeで行えるようになったということです。

下記のようなinterfaceから型が提供されます。


public interface ITypeProvider
{
Type GetType(string name,
BindingFlags bindingAttr);
Expression GetInvokerExpression(
MethodBase syntheticMethodBase,
ParameterExpression parameters);
event System.EventHandler Invalidate;
Type
GetTypes();
}

型の生成はGetTypeメソッドでそれに対するメソッド呼び出しの変換がGetInvokerExpression
と推測しています。
あらかた、GetInvokerExpressionがnullを返したらコンパイルエラーになるとかってことじゃないでしょうか。



まったく正しい使い方ではないとは思うが、
Regex<".*">
のようにしてコンパイル時に正規表現の構文チェックができたりするんじゃないかと予想。

1. http://research.microsoft.com/en-us/um/redmond/events/ss2011/slides/thursday/don_syme.pdf
2. http://channel9.msdn.com/Events/PDC/PDC10/FT12

プログラマの素質

プログラマの素質ってなんだと思いますか?


数学ができること?
努力家であること?
IQが高いこと?


全部違います。


プログラマの素質って実は、感性なんです!
たとえば好きな音楽やアニメや映画を見て面白い!って思ったりすることです。
それさえあれば実はプログラマって誰でもなれちゃうんです。


なぜなら、プログラミングのための基礎知識や努力というのは
全部感性さえあれば、自然に身に付くものだからなんです!


何か自分の作りたいものを作って
自分で「おぉーすげー」って思う


最初は 1 + 1 って入力したら 2 が出てきて
それですごい!って思うのでいいんです。
誰でもそこからステップアップするんです。


だから僕が、プログラミングを人に教えるときも驚きを重視します。
これは、その人の感性に合わせないとできないことです。


ある人からしてみれば、”Excelのマス目で動画を再生する”ことがすごいことであって
またある人からしてみれば "行列の演算を一瞬で行う"ことがすごいことだったりするからです。


僕も感性は相手に合わせます。
教える側だけが すごい と思えることを教えられる側に押し付けても
絶対にプログラミングは上達しません。
僕は相手の感性を最大限に重視したプログラミング教育を行っています

さぁあなたも今日からプログラマを目指してみませんか?

ゲームクリエーターとドリームキラー

一言目には、「君では無理」二言目にも「君では無理」
そんなネガティブワードばかりを吐き続けるドリームキラーについて考察してみましょう。

たとえ話をしましょう。
あるゲームクリエーター歴3年の人間がいたとします。
その人の元に「ゲームが好きだからゲームクリエーターになりたい」と相談した人がいます。
ゲームクリエーターはこう言います。
「ゲームが好きなのと、ゲームを作るのが好きなのは違う」
それを聞いて相談者は、すっかり鼻を折られゲームクリエーターの道をあきらめてしまいました。

"ライオンは. 子を自立させるために. 崖から突き落とす"

彼らはこんなような言葉を建前にネガティブワードを吐き続けます。
まずこの言葉のように現実にライオンはそんなことはしません。
この言葉は、ただのことわざでしょう。
実際このようなことをして何のためになるのか考えてみましょう。

モチベーションが高いがスキルがない人がいたとして、彼の自信だけを失わせて
自信が失ったあとは誰もその人を助けたりはしません。

誰が得するんですか?
ドリームキラーが「俺はすごいからここまでできたのだ」と自尊心を助長するだけなのです。

・ゲームがやりたいのとゲームがつくりたいのは違う。
モチベーションの高さはどんな方向であれ平等です。
本当の意味でゲームが作りたくてゲーム業界に来た人ってなんでしょう?
実際のところのモチベーションなんて何でもいいんです。
どんな方向のことでも好きと言えるものがあるなら
それは立派なモチベーションです。

ところであなたはどうやってこの記事にたどり着きましたか?
きっとゲームクリエーターのことを調べていてたまたまたどり着いた人もいると思います。
そうであれば、あなたは間違いなくゲームクリエーターに向いています。
自分で調べるぐらいの興味を示しているからです。
自主性もあり、インターネットで情報収集をするという時代に即した方法論も持っています。
もっと自信をもってください。

佐藤祐介はゲームクリエーターを目指す人間を応援しています。

Objective-Cの感想

最近、お仕事で Objective-C という言語を使う機会があったので
その感想を書きたいと思います。

Objective-C とは

Objective-C は、"初心者が苦労はすれども、挫折はさせず"をコンセプトにして設計された動的型言語です。
ベースとなるC言語Smalltalk というオブジェクト指向言語を載せた最新鋭の言語です。
Objective-C が登場するまでは、パソコンを触ったこともないような人はプログラミングする機会なんてありませんでした。
Apple はパソコンを触ったこともない初心者にプログラミング体験を提供することでマーケティング戦略に成功しました。
また、Objective-C は動的型による特徴と、C言語の伝統的な signal を用いることによって極力、ユーザーにエラー内容を表示させることを抑えているのでエラー恐怖症になってしまっている人でも不可なくプログラミングを楽しむことができます。
もうプログラマになるためには勉強が必要だ、なんて言わせません。

動的型の世界

巷では、静的関数型界隈で型推論などという言葉がささやかれていますが、Objective-C ではよりエレガントなソリューションを提供しています。
それが id 型です。
この型は、どんな型にも適合します。型推論より優れているところは、コンパイル時のオーバーヘッドが少ないというところです。
もう型推論は必要ありません。

メソッド呼び出し

ベースは C言語ですがメソッド呼び出しは、いわゆる C言語風の f() ではありません。
下記のような構文で記述します。


[self MyMethod]
[self MyMethod : [ [HogeViewControler alloc] init] ]
この構文の素晴らしいところは vim力が鍛えられるということです。
C言語風の呼び出しでは関数呼び出しをネストするときでも、() で囲う範囲が Objective-C より狭いので横着ができてしまいます。
その点、Objective-C は、メソッド呼び出し式の両端を [ と ] で囲う必要があるので、vim力が向上します。

統合開発環境との親和性

「言語だけ洗練されていればそれでいい」
というような甘い考えは、現代のソフトウェア業界には通用しません。
Objective-C は、Xcode というApple 謹製の統合開発環境との親和性を非常に強く意識して作られています。
例えば、画面上のある UILabel をコードから参照する場合は次のように書きます。


IBOutlet UILabel* label;
このように書いて Interface Bulder から接続するだけで UILabel をコード上から参照することができます。
※ IBOutlet の IB は Interface Builderの略です。