他人様のブログを和訳・"A Simple AppSettings Type Provider"

味を占めたんだ!!!!

他人様のブログを和訳したら、なかなか面白かったので今日もやります!!!!
味を占めたんだ!!!!

今日もDaniel Mohl さんのブログから、2011年9月29日のエントリです

2011/09/29 シンプルな AppSetting Type Provider


F# 3.0 Developer Preview が公開され、私は自由時間の多くをType Providerで遊んで過ごしています。

Type Providerを使うための導入ガイドとサンプルの発表もあって、
(食事は言うまでもなく)寝るための時間を確保するのも苦労しています。


かなりの量の実験をした後、私は最初のType Providerを書いてみました。
本当に単純でプロダクト品質と呼べるものではありませんが、とても楽しいエクササイズとなりました。
基本的なアイデアはconfigファイルからappSetting要素を読み出し、値を適切にパースし、型安全な方法でそれらと対応させることです。


configファイルのサンプルは以下のような感じです:
    <?xml version="1.0" encoding="utf-8" ?>  
    <configuration>  
        <appSettings>  
            <add key="test2" value="Some Test Value 5"/>  
            <add key="TestInt" value="102"/>  
            <add key="TestBool" value="True"/>  
            <add key="TestDouble" value="10.01"/>  
        </appSettings>  
    </configuration>  
Type Provider のコードは以下の通りです:
namespace AppSettingsTypeProvider

open System
open System.IO
open System.Reflection
open Samples.FSharpPreviewRelease2011.ProvidedTypes
open Microsoft.FSharp.Core.CompilerServices
open System.Configuration

[<TypeProvider>]
type public AppSettingsProvider(config:TypeProviderConfig) as this =
    inherit TypeProviderForNamespaces()

    let namespaceName = "AppSettingsTypeProvider"
    let currentAssembly = Assembly.GetExecutingAssembly()
    let tySettings = ProvidedTypeDefinition(currentAssembly, namespaceName, "AppSettings", Some typeof<obj>)

    let addSettings (configFileName:string) (tyDef:ProvidedTypeDefinition) =
        tyDef.AddXmlDocDelayed(fun () -> sprintf "Provides a strongly typed representation of the appSettings in a provided config file.")

        let filePath = Path.Combine(config.ResolutionFolder, configFileName)
        let fileMap = ExeConfigurationFileMap(ExeConfigFilename=filePath)
        let appSettings = ConfigurationManager.OpenMappedExeConfiguration(fileMap, ConfigurationUserLevel.None).AppSettings.Settings
        
        let tryParseWith func = func >> function
            | true, value -> Some value
            | false, _ -> None

        let (|Bool|_|) = tryParseWith Boolean.TryParse
        let (|Int|_|) = tryParseWith Int32.TryParse
        let (|Double|_|) = tryParseWith Double.TryParse

        do Seq.iter (fun (key) ->
            let keyElement = match (appSettings.Item key).Value with
                             | Int fieldValue -> ProvidedLiteralField(key, typeof<int>, fieldValue)
                             | Bool fieldValue -> ProvidedLiteralField(key, typeof<bool>, fieldValue)
                             | Double fieldValue -> ProvidedLiteralField(key, typeof<Double>, fieldValue)
                             | fieldValue -> ProvidedLiteralField(key, typeof<string>, fieldValue.ToString())

            do keyElement.AddXmlDocDelayed(fun () -> sprintf "Returns the value from the appSetting with key %s" key)
            do tyDef.AddMember keyElement ) appSettings.AllKeys
    
    let staticParams = [ProvidedStaticParameter("configFilePath", typeof<string>)]

    do tySettings.DefineStaticParameters(staticParams,
        fun typeName args ->
            match args with
            | [| :? string as configFileName |] ->
                let ty = ProvidedTypeDefinition(assembly = currentAssembly,
                            namespaceName = namespaceName, typeName = typeName,
                            baseType = Some typeof<obj>, HideObjectMethods = true)
                addSettings configFileName ty
                ty
            | _ -> failwith "unexpected parameter values")
 
    do this.AddNamespace(namespaceName, [tySettings])
                           
[<assembly:TypeProviderAssembly>]
do()
最後にどのように使うかのサンプルを示します:
#r "System"  
#r @"bin\debug\AppSettingsTypeProvider.dll"  
  
open System  
open AppSettingsTypeProvider  
  
type settings = AppSettings<"App.config">  
printfn "Test2 String: %s" settings.test2   
printfn "Test Int: %i" settings.TestInt   
printfn "Test Bool: %b" settings.TestBool    

感想

F#3.0Betaが発表(Introducing Visual F# 3.0 Beta! | Visual Studio F# Team Blog)された今、少し古いかなと思いましたが、
Type Provider をどのように使えば良いのか全然分からなかったので翻訳しました。
やっぱり難しいですねType Provider...
便利なのか否かの判断はまだ保留中です。