Docker で F# アプリケーションを作成

Microsoft による .NET Core の公式 Docker イメージを使って、単純な F# のコンソールアプリケーションを作成してみました。

今回のソースは http://github.com/fits/try_samples/tree/master/blog/20170619/

(1) Docker コンテナの実行

まずは .NET Core の Docker コンテナを実行しておきます。

.NET Core コンテナの実行
$ docker run -it --rm microsoft/dotnet /bin/bash
root@・・・:/# 

現時点では 1.1.2-sdk が latest となっており、デフォルトで netcoreapp1.1 のアプリケーションを作成する事になります。

(2) F# コンソールプロジェクトの作成

.NET Core SDK では dotnet コマンド (.NET Command Line Tools) を使ってアプリケーションを開発できます。

dotnet new コマンドで様々なプロジェクトの雛型を作成でき、デフォルトでは以下のようなプロジェクトに対応しています。

プロジェクトの雛型一覧
root@・・・# dotnet new -all

・・・
Templates                 Short Name       Language      Tags
-----------------------------------------------------------------------
Console Application       console          [C#], F#      Common/Console
Class library             classlib         [C#], F#      Common/Library
Unit Test Project         mstest           [C#], F#      Test/MSTest
xUnit Test Project        xunit            [C#], F#      Test/xUnit
ASP.NET Core Empty        web              [C#]          Web/Empty
ASP.NET Core Web App      mvc              [C#], F#      Web/MVC
ASP.NET Core Web API      webapi           [C#]          Web/WebAPI
Nuget Config              nugetconfig                    Config
Web Config                webconfig                      Config
Solution File             sln                            Solution
・・・

今回は F# のコンソールアプリケーションを作成します。

適当なディレクトリを作成・移動し、dotnet new console を実行します。 F# の場合は -lang オプションで指定します。 (デフォルトは C#

F# コンソールプロジェクトの作成
root@・・・# mkdir sample
root@・・・# cd sample
root@・・・# dotnet new console -lang F#

以下のファイルが生成されました。

Program.fs
open System

[<EntryPoint>]
let main argv =
    printfn "Hello World from F#!"
    0 // return an integer exit code
sample.fsproj
<Project Sdk="FSharp.NET.Sdk;Microsoft.NET.Sdk">
  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>netcoreapp1.1</TargetFramework>
  </PropertyGroup>
  <ItemGroup>
    <Compile Include="Program.fs" />
  </ItemGroup>
  <ItemGroup>
    <PackageReference Include="FSharp.Core" Version="4.1.*" />
    <PackageReference Include="FSharp.NET.Sdk" Version="1.0.*" PrivateAssets="All" />
  </ItemGroup>
</Project>

(3) ビルドと実行

とりあえず、このままビルドして実行してみます。

まずは dotnet restore を実行してプロジェクトの依存関係を復元します。

これにより依存関係などが定義された obj/project.assets.json ファイルが作られます。

リストア
root@・・・# dotnet restore

・・・
  Installing FSharp.Compiler.Tools 4.1.17.
  Installing FSharp.NET.Sdk 1.0.5.
  Installing FSharp.Core 4.1.17.
  ・・・
  Installed:
      3 package(s) to /sample/sample.fsproj

ビルドは dotnet build で行います。 なお、dotnet build しなくても dotnet run すればビルドも実施してくれるようです。

ビルド
root@・・・# dotnet build

・・・
Build succeeded.
    0 Warning(s)
    0 Error(s)

dotnet run で実行します。

実行
root@・・・# dotnet run

Hello World from F#!

(4) Chiron パッケージの追加

次に、Chiron という F# で JSON を処理するためのパッケージを使ってみます。

依存パッケージは dotnet add package <パッケージ名> で追加できますが、(現時点で)デフォルトの Chiron 6.2.1 を追加しようとすると以下のようにエラーとなりました。

Chiron パッケージ(6.2.1)追加 - 失敗
root@・・・# dotnet add package chiron

・・・
log  : Installing Chiron 6.2.1.
error: Package Chiron 6.2.1 is not compatible with netcoreapp1.1 (.NETCoreApp,Version=v1.1). Package Chiron 6.2.1 supports:
error:   - net40 (.NETFramework,Version=v4.0)
error:   - portable-net45+win8+wp8+wpa81 (.NETPortable,Version=v0.0,Profile=Profile259)
・・・
error: One or more packages are incompatible with .NETCoreApp,Version=v1.1.
error: Package 'chiron' is incompatible with 'all' frameworks in project '/sample/sample.fsproj'.

netcoreapp1.1 (.fsproj の TargetFramework で設定) を Chiron 6.2.1 がサポートしてない事が原因のようです。

nuget の Chiron のページ を見てみると .NETStandard 1.6 に対応したバージョン 7.0.0-alpha-170410 がありました。

netcoreapp1.1 は .NETStandard 1.6 をサポートしているので、このバージョンなら使えそうです。

add package では -v オプションでバージョンを指定できるので、以下のようにバージョンを指定して Chiron を追加します。

Chiron パッケージ(7.0.0-alpha-170410)追加 - 成功
root@・・・# dotnet add package chiron -v 7.0.0-alpha-170410

・・・
log  : Installing FParsecCS 1.0.3-alpha-170404.
log  : Installing FParsec 1.0.3-alpha-170404.
log  : Installing Chiron 7.0.0-alpha-170410.
info : Package 'chiron' is compatible with all the specified frameworks in project '/sample/sample.fsproj'.
info : PackageReference for package 'chiron' version '7.0.0-alpha-170410' added to file '/sample/sample.fsproj'.

今度は成功し、.fsproj は以下のようになりました。

sample.fsproj (Chiron 追加後)
<Project Sdk="FSharp.NET.Sdk;Microsoft.NET.Sdk">
  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>netcoreapp1.1</TargetFramework>
  </PropertyGroup>
  <ItemGroup>
    <Compile Include="Program.fs" />
  </ItemGroup>
  <ItemGroup>
    <PackageReference Include="chiron" Version="7.0.0-alpha-170410" />
    <PackageReference Include="FSharp.Core" Version="4.1.*" />
    <PackageReference Include="FSharp.NET.Sdk" Version="1.0.*" PrivateAssets="All" />
  </ItemGroup>
</Project>

(5) Chiron サンプルアプリケーションの実装

動作確認のため Chiron を使った単純な処理を実装してみました。 (Chiron 7 ではモジュール構成等が変わっているようでした)

Program.fs
open System
open Chiron.Inference

[<EntryPoint>]
let main argv =
    let d = Map ["a", 123]
    let res = Json.serialize d

    printfn "json = %s" res
    0

(6) Chiron サンプルアプリケーションのビルドと実行

(3) と同様にリストアしてから実行します。

リストア
root@・・・# dotnet restore

  Restoring packages for /sample/sample.fsproj...
  ・・・
ビルド
root@・・・# dotnet build

・・・
Build succeeded.
    0 Warning(s)
    0 Error(s)
実行
root@・・・# dotnet run

json = {"a":123}