F#についてLTしたよ

3/1から入社した職場でF#愛でLTしてきたよ。


この世界の片隅で使われている言語


みなさん、F# というプログラミング言語をご存知でしょうか


お前誰よ

  • 寺嶋 哲(Terajima Satoshi)
  • @meganehouser
  • 3月1日からjoin
  • 前職: Excelおじさん
  • よさこいとHouse踊れます


F#とは

  • マイクロソフトが開発した .NET Framework向けのプログラミング言語
  • OCamlベースの関数型+OOP言語
  • 静的片付け+型推論
  • クロスプラットフォーム(Mac,Linux,Win,Android,iOS,JS/HTML5,GPU,FreeBSD)
  • オープンソース
  • fsharp/fsharp

F#の特徴的な機能

  • exe,dllにコンパイルまたはスクリプト実行できる
  • REPL標準装備
  • オフサイドルールを元にしたコンパクトな軽量構文
  • パイプライン演算子
  • 判別共用体とパターンマッチ
  • コンピュテーション式
  • タイププロバイダ
  • 測定単位

オフサイドルールを元にしたコンパクトな軽量構文

例 C#の場合

using System;
namespace HelloWorld
{
    class Hello 
    {
        static string AddTitle(string name)
        {
            return name + "さん";
        }

        static void Main() 
        {
            var name = Console.ReadLine();
            var nameWithTitle = AddTitle(name);
            Console.WriteLine($"Hello {NameWithTitle}!");
        }
    }
}

F#の場合

open System

let addTitle name = name + "さん"

let nameWithTitle = Console.ReadLine() |> addTitle
printf "Hello, %s!" nameWithTitle

パイプライン演算子

関数の戻り値を次の関数の引数として渡す機能 - F#から広まった機能 - Elixir、ES2019は標準で入っている

let plus n1 n2 = n1 + n2

let multiply n1 n2 = n1 * n2


10 |> plus 40 |> multiply 2 // 100

パイプライン演算子 例2

type Person = {name:string; age: int}

let persons = [
    {name="Bob"; age=32}
    {name="Alice"; age=24}
]

persons
|> List.filter(fun p -> p.age > 30)
|> List.filter(fun p -> p.name.StartsWith("B"))
|> List.map(fun p -> String.Format("{0},{1}歳", (p.name), p.age))
|> fun ps -> printf "%s" (ps.Head)

コンピュテーション式

  • 内部DSL。モナド用の構文として使われることも多い
  • 式変形により言語の用意する構文の意味をカスタマイズ
open System
open System.Net

let fetchHtmlAsync url = 
    async {
        let uri = Uri(url)
        use webClient = new WebClient()
        let! html = webClient.AsyncDownloadString(uri)
        return html
    }

let html = "https://dotnetfoundation.org" 
           |> fetchHtmlAsync 
           |> Async.RunSynchronously
printfn "%s" html

コード出典 非同期プログラミング | Microsoft Docs


タイププロバイダー

任意のメタデータを与えると、コンパイラがコンパイル時に"型のないもの"に"型を与えて"提供してくれる機能

  • FSharp.Data
  • JSON、XML、CSV、HTMLドキュメント
  • SQLProvider
  • DBのテーブル・カラムへの型付
  • Azure のストレージ型プロバイダー
  • Azure Blob、テーブル、およびキュー等
  • FSharp.Data.GraphQL
  • GraphQLProviderURLで指定されたGraphQL serverに基づいて型

型プロバイダー | Microsoft Docs


F#の好きな機能

判別共用体(直和型)とパターンマッチ

  • いわゆるリッチなEnum

改善前のコード

let doSomething (x:int) :int = 
    // 何か失敗するかもしれない処理
    n

判別共用体(直和型)とパターンマッチ

  • 判別共用体を使って書き換えたコード
type Result = 
    | Success of int 
    | Failure of int * string

let doSomething' x = 
    let result = // 何iか失敗するかもしれない処理
    if result > 9 then
        Success(result)
    else 
        Failure(result, "faild to do something")

let result = doSomething' 1 

match result with
| Success(n) -> printfn "%d" n
| Failure(errCode, errMessage) -> printfn "[%d] %s" errCode errMessage

判別共用体(直和型)とパターンマッチ

  • 失敗した以降は関数を適用しない中値演算子を定義してみる
let (|>>) (x:Result) (f:int -> Result) = 
    match x with
    | Success data -> f data
    | Failure e -> Failure e

let result = 10 |> doSomething1 |>> doSomething2 |>> doSmething3

測定単位

  • 数値型に単位をつけられる機能
[<Measure>] type km
[<Measure>] type hours
> 1<km> < 2<km>;;
val it : bool = true

> 1<km> < 2<hours>;;
error
let speed: int<km/hours> = 100<km> / 1<hours>;;

let doHoge (s: int<km/hours>) = // do somehting

F#何に使ってたの

  • REPLで .NET Frameworkのクラスの挙動を確認
  • F#スクリプトで作成中のC#アプリケーションまたはライブラリをアドホックに呼び出す
  • F#スクリプトでテストデータの生成やログの集計とか

「F#、割とマジでWindowsでまともにストレスなく使える唯一のスクリプト言語なので。@igeta談」


海外でのF#コミュニティ

日本でのF#コミュニティ

  • 現在ではあまり活発な動きはない
  • 2014年頃まではF# MVP受賞するような方が2〜3人いた
  • F#談話室 → 2017年以降開催なし
  • Advent Calendar → 2017年で終了
  • fsugjpのgitterが残っているのみ

この~~世界~~"日本"の片隅で使われている言語


なぜ日本では片隅でしか使われていないの

  • 日本では.NETはエンプラ系で使われることが多く、言語を変更するモチベーションがない
  • みんなC#で満足している
  • アンダース・ヘルスバーグがリードアーキテクトですし
  • C#がF#の便利な機能を取り込んでいっている

Javaに対するベターJavaであるScala/Kotlin的な立場になれなかった


まとめ

  • 言語が流行るかどうかはその国でのコンテクキストにも依存する
  • コミュニティが強い言語は強い
    • Pythonは日本でも海外でもみんなで盛り上げていく感じがあって素晴らしい
  • F#のことも記憶の片隅に残しておいてほしい
Show Comments