F#(Fシャープと発音)は、汎用的で高水準の、厳密な型付けを持つマルチパラダイムプログラミング言語であり、関数型、命令型、オブジェクト指向プログラミングの手法を包含しています。最もよく使用されるのは、 .NET上のクロスプラットフォーム共通言語インフラストラクチャ(CLI)言語ですが、 JavaScript [ 8 ]やグラフィックス処理ユニット(GPU)コード[ 9 ]も生成できます。
F# はF# Software Foundation [ 10 ] 、Microsoft、およびオープンな貢献者によって開発されています。F # 用のオープンソースのクロスプラットフォームコンパイラは F# Software Foundation から入手できます。[ 11 ] F# はVisual Studio [ 12 ]およびJetBrains Rider [ 13 ] で完全にサポートされている言語です。Visual Studio Code、Vim、Emacsなど、広く使用されている多くのエディタ用の F# をサポートするプラグインが存在します。
F# はML言語ファミリーの一員であり、プログラミング言語OCamlの中核部分の.NET Framework実装として誕生しました。[ 5 ] [ 6 ]また、 C#、 Python、Haskell、[ 4 ] Scala、Erlangからも影響を受けています。
F#はオープンな開発およびエンジニアリングプロセスを採用しています。言語の進化プロセスは、 F#ソフトウェア財団とともに、言語設計における終身の慈悲深い独裁者(BDFL)であるマイクロソフトリサーチのドン・サイムによって管理されています。F#言語の以前のバージョンは、マイクロソフトとマイクロソフトリサーチがクローズドな開発プロセスを用いて設計しました。
F#は、Visual Studioの2010年版で初めて搭載され、Visual Basic(.NET)やC#と同等のレベル(ただしオプションとして)で提供され、それ以降のすべての版にも搭載されているため、広く利用可能でサポートも充実している言語となっている。
F# は、英国ケンブリッジの Microsoft Research で開発されました。この言語は元々Don Symeによって設計および実装されました。 [ 5 ]彼によると、fsharp チームでは F は「Fun」の F だと言われています。[ 41 ] Andrew Kennedy は測定単位の設計に貢献しました。[ 5 ] Visual Studio 用の Visual F# ツールは Microsoft によって開発されています。[ 5 ] F# ソフトウェア財団は、Microsoft Visual F# ツール チームが提供するオープンソース コンパイラの実装を組み込んだ F# オープンソース コンパイラとツールを開発しました。[ 10 ]
F#は、型付けが厳密に定められた関数型プログラミング言語であり、通常は関数型プログラミング言語にしか見られない多くの機能を備えている一方で、C#で利用可能なオブジェクト指向機能もサポートしています。これらの機能により、F#プログラムは完全に関数型スタイルで記述できるだけでなく、関数型スタイルとオブジェクト指向スタイルを混在させることも可能です。
機能的特徴の例としては、以下のようなものがあります。
F# は、即時評価と場合によっては遅延評価を使用する式ベースの言語です。F# のすべてのステートメントif(式、try式、ループを含む)は、静的型を持つ合成可能な式です。[ 55 ]値を返さない関数と式の戻り値の型は です。F# は、値を名前にバインドするためにキーワードをunit使用します。 [ 55 ]例:let
x = 3 + 4とする値を7名前にバインドしますx。
新しい型はキーワードを使用して定義されますtype。関数型プログラミングの場合、F# はタプル、レコード、判別共用体、リスト、オプション、および結果型を提供します。[ 55 ]タプルはn個の値のセットを表し、n ≥ 0 です。値nはタプルのアリティと呼ばれます。3 タプルは と表され(A, B, C)、A、B、C は異なる型の値である可能性があります。タプルは、設計時に値の数がわかっていて、実行中も一定である場合にのみ、値を格納するために使用できます。
レコードとは、データメンバーに名前が付けられている型です。レコード定義の例を以下に示します。
型R = {名前:文字列年齢:整数}レコードは として作成できます。キーワード は、 のようにレコードのコピーを作成するために使用されます。これは、 をコピーしてフィールドの値を変更することによって新しいレコードを作成します(前の例で作成されたレコードの名前が であると仮定します)。letr={Name="AB";Age=42}with{rwithName="CD"}rNamer
判別共用体型は、C言語の共用体の型安全なバージョンです。たとえば、
型A = | UnionCaseX of string | UnionCaseY of int共用型の値は、どちらの共用ケースにも対応できます。各共用ケースが持つ値の型は、それぞれのケースの定義に含まれています。
リスト型は、表記法(cons演算子)または省略形として表現される不変のリンクリストです。空のリストはと表記されます。オプション型は、またはの選択肢を持つ判別共用体型です。F#型は、汎用.NET型として実装される汎用型にすることができます。head::tail::[item1;item2;item3][]Some(x)None
F# はラムダ関数とクロージャをサポートしています。[ 55 ] F# のすべての関数は第一級の値であり、不変です。[ 55 ]関数はカリー化できます。第一級の値であるため、関数は他の関数の引数として渡すことができます。他の関数型プログラミング言語と同様に、F# ではand演算子を使用して関数の合成が可能です。>><<
F# はシーケンス式[ 56 ]は、値を生成するコードによってシーケンスseq { ... }、リスト[ ... ]、または配列[| ... |]。たとえば、
seq { for b in 0 .. 25 do if b < 15 then yield b * b }0から25までの範囲の数値をフィルタリングすることで、0から14までの数値の平方数の数列を形成します。シーケンスはジェネレーターであり、値はオンデマンドで生成されます(つまり、遅延評価されます)。一方、リストと配列は即座に評価されます。
F# はパターンマッチングを使用して値を名前にバインドします。パターンマッチングは、判別共用体にアクセスする場合にも使用されます。共用体はパターンルールに対して値マッチングされ、マッチングが成功した場合にルールが選択されます。F# は、拡張可能なパターンマッチングの形式としてアクティブパターンもサポートしています。 [ 57 ]これは、たとえば、型に対して複数のマッチング方法が存在する場合に使用されます。[ 55 ]
F# は、合成計算を定義するための一般的な構文をサポートしています。計算式。シーケンス式、非同期計算、クエリは、計算式の特定の種類です。計算式は、モナドの実装です。 [ 56 ]
F# の命令型プログラミングのサポートには以下が含まれます
forループwhileループ[| ... |]構文で作成された配列dict [ ... ]構文またはSystem.Collections.Generic.Dictionary<_,_>型を使用して作成されます。値とレコードフィールドには、ラベルを付けることもできますmutable。例:
// 初期値 '1' で 'x' を定義しますlet mutable x = 1 // 'x' の値を '3' に変更しますx <- 3また、F# は、命令型データ構造を定義する名前空間で定義されているものなど、すべての CLI 型およびオブジェクトへのアクセスをサポートしていますSystem.Collections.Generic。
他の共通言語基盤(CLI)言語と同様に、F# はオブジェクト指向プログラミングを通じて CLI 型を使用できます。[ 55 ] F# の式におけるオブジェクト指向プログラミングのサポートには以下が含まれます。
x.Name{newobj()withmemberx.ToString()="hello"}newForm()x:?stringx:?>stringx.Method(someArgument=1)newForm(Text="Hello")x.Method(OptionalArgument=1)オブジェクト指向プログラミングのパターンにおけるサポートには以下が含まれます
:?stringassF# のオブジェクト型定義は、 C#の定義形式に対応するクラス、構造体、インターフェース、列挙型、またはデリゲート型定義のいずれかになります。たとえば、名前と年齢を引数にとり、2 つのプロパティを宣言するコンストラクタを持つクラスを以下に示します。
/// シンプルなオブジェクト型定義type Person ( name : string , age : int ) = member x . Name = name member x . Age = ageF# は非同期ワークフローによる非同期プログラミングをサポートしています。[ 58 ]非同期ワークフローは、内のコマンドのシーケンスとして定義されます。async{ ... }
let asynctask = async { let req = WebRequest.Create ( url ) let ! response = req.GetResponseAsync ( ) use stream = response.GetResponseStream ( ) use streamreader = new System.IO.StreamReader ( stream ) return streamreader.ReadToEnd ( ) }これはlet!、右側の式(応答の取得)を非同期で実行する必要があるが、結果が利用可能になった時点で処理フローを続行する必要があることを示しています。言い換えれば、コードブロックの観点からは、応答の取得はブロッキング呼び出しのように見えますが、システムの観点からは、スレッドはブロックされず、このフローに必要な結果が利用可能になるまで他のフローの処理に使用できます。
非同期ブロックは、Async.RunSynchronously関数を使用して呼び出すことができます。複数の非同期ブロックは、Async.Parallelオブジェクトのリストasync(この例では、asynctaskは非同期オブジェクト)を受け取り、リスト内のタスクを並列に実行するための別の非同期オブジェクトを作成する関数を使用して並列に実行できます。結果として得られるオブジェクトは、を使用して呼び出されますAsync.RunSynchronously。[ 58 ]
F# における制御の反転はこのパターンに従います。 [ 58 ]
バージョン6.0以降、F#は.NETタスクの作成、消費、および返却を直接サポートしています。[ 59 ]
System.Net.Httpを開き、fetchUrlAsync ( url : string ) = // string -> Task<string> task { use client = new HttpClient () let! response = client . GetAsync ( url ) let! content = response . Content . ReadAsStringAsync () do ! Task . Delay 500 return content }// 使用例let fetchPrint () = let task = task { let! data = fetchUrlAsync "https://example.com" printfn $ "{data}" } task . Wait ()並列プログラミングはAsync.Parallel、Async.Start非同期ブロックを並列に実行する操作などを通じて部分的にサポートされています。
並列プログラミングは、Array.ParallelF# 標準ライブラリの関数型プログラミング演算子、タスクプログラミングモデルの直接使用、.NET スレッドプールと .NET スレッドの直接使用、および F# コードをGPU [ 9 ]System.Threading.Tasksコードなどの代替並列実行エンジンに動的に変換することによってもサポートされています。
F# 型システムは、数値の単位チェックをサポートしています。 [ 60 ]メートルやキログラムなどの単位を、浮動小数点、符号なし整数[ 61 ]および符号付き整数値に割り当てることができます。これにより、コンパイラはこれらの値を含む算術演算が次元的に一貫しているかどうかをチェックでき、たとえば長さが誤って時間に加算されないようにすることで、一般的なプログラミングミスを防ぐのに役立ちます。
測定単位機能はF#の型推論と統合されているため、ユーザーコードでの型注釈は最小限で済みます。[ 62 ]
[<計測単位>]型m // メートル[<計測単位>]型s // 秒let distance = 100 . 0 < m > // float<m> let time = 5 . 0 < s > // float<s> let speed = distance / time // float<m/s>[<測定単位>] type kg // キログラム[<測定単位>] type N = ( kg * m )/( s ^ 2 ) // ニュートン[<測定単位>] type Pa = N /( m ^ 2 ) // パスカル[< Measure >] type days let better_age = 3u < days > // uint<days>F#の静的型チェッカーはコンパイル時にこの機能を提供しますが、コンパイルされたコードから単位が消去されます。そのため、実行時に値の単位を特定することはできません。
F# では、メタプログラミングによる構文のカスタマイズがいくつか可能で、特に計算式を通して、F# 言語内にカスタムドメイン固有言語を埋め込むことができます。 [ 55 ]
F# には、クォーテーションと呼ばれる実行時メタプログラミング機能が含まれています。[ 63 ]クォーテーション式は、F# 式の抽象構文木表現に評価されます。同様に、属性でラベル付けされた定義も、クォーテーション形式でアクセスできます。F# クォーテーションは、F# コードをJavaScript [ 8 ]やGPU [ 9 ]コードにコンパイルするなど、さまざまな目的で使用されます。クォーテーションは、プログラムの他の部分で使用するためのデータとして F# コード式を表しますが、構文的に正しい F# コードである必要があります。[<ReflectedDefinition>]
F# 3.0 では、F# 型プロバイダと呼ばれる静的に拡張可能な型生成によるコンパイル時メタプログラミングが導入されました。[ 64 ] F# 型プロバイダを使用すると、コンパイル時にオンデマンドでコンパイラに型情報を提供するコンポーネントで F# コンパイラとツールを拡張できます。F# 型プロバイダは、 Freebase知識グラフを含む、接続された情報ソースへの強力な型付きアクセスをスケーラブルな方法で提供するために使用されています。[ 65 ]
F# 3.0 では、F# の引用符と計算式の機能が組み合わされてLINQクエリが実装されています。[ 66 ]例えば、次のようになります。
// OData 型プロバイダーを使用して、Northwind データベースにアクセスするために使用できる型を作成します。open Microsoft.FSharp.Data.TypeProviderstype Northwind = ODataService < "http://services.odata.org/Northwind/Northwind.svc" > let db = Northwind . GetDataContext ()//クエリ式。let query1 = query { for customer in db.Customers do select customer }型プロバイダ、クエリ、および厳密に型付けされた関数型プログラミングの組み合わせは、情報豊富なプログラミングとして知られています。[ 67 ]
F# は、軽量な非同期エージェントのインメモリ実装を通じて、アクタープログラミングモデルのバリエーションをサポートしています。たとえば、次のコードはエージェントを定義し、2 つのメッセージを送信します。
type Message = |文字列のエンキュー| AsyncReplyChannel < Option < string >>のデキュー// 文字列のリストへの同時アクセスを提供しますlet listManager = MailboxProcessor . Start ( fun inbox -> let rec messageLoop list = async { let! msg = inbox . Receive () match msg with | Enqueue item -> return ! messageLoop ( item :: list )| ReplyChannelをデキュー->マッチリスト| [] -> replyChannel . Reply None return ! messageLoop list | head :: tail - > replyChannel . Reply ( Some head ) return ! messageLoop tail }// 空のリストmessageLoop []でループを開始します// 使用方法async { //いくつかの文字列をキューに追加listManager.Post ( Enqueue " Hello " ) listManager.Post ( Enqueue " World" )//文字列をデキューして処理しますlet ! str = listManager.PostAndAsyncReply ( Dequeue ) str | > Option.iter ( printfn "Dequeued: %s " )} | > Async.StartF#は汎用プログラミング言語です。
SAFE Stackは、Webアプリケーションを開発するためのエンドツーエンドのF#スタックです。サーバー側ではASP.NET Coreを、クライアント側ではFableを使用します。 [ 71 ]
代替のエンドツーエンドのF#オプションとしては、WebSharperフレームワークとOxpeckerフレームワークがある。[ 72 ]
F#は、Visual Studio Tools for Xamarinと組み合わせて、 iOSおよびAndroid向けアプリを開発するために使用できます。Fabulousライブラリは、より使いやすい関数型インターフェースを提供します。
F#は、とりわけ定量金融プログラミング[ 73 ] 、エネルギー取引とポートフォリオ最適化[ 74 ] 、機械学習[ 75]、ビジネスインテリジェンス[76 ]、Facebook上のソーシャルゲーム[ 77 ]などに使用されています。
2010年代には、F#はC#の最適化された代替言語として位置づけられてきました。F#のスクリプト機能とすべてのMicrosoft製品との言語間互換性により、開発者の間で人気が高まっています。[ 78 ]
F# はスクリプト言語として使用でき、主にデスクトップの読み込み・評価・出力ループ(REPL) スクリプトに使用されます。[ 79 ]
F# のオープンソースコミュニティには、F# Software Foundation [ 10 ]とGitHubの F# Open Source Group [ 11 ]が含まれます。人気のオープンソース F# プロジェクトには、次のものがあります。
F#には、ファンクタ、オブジェクト、ポリモーフィックなバリアント、その他の追加機能を含まない、OCamlの大部分のサブセットで書かれたプログラムを直接コンパイルできる、従来の「ML互換モード」が備わっています。
以下にいくつかの小さな例を示します。
// これはサンプル「Hello World」プログラムのコメントです。printfn "Hello World!"レコード型の定義。レコードはデフォルトでは変更不可能であり、構造的な等価性に基づいて比較されます。
type Person = { FirstName : string LastName : string Age : int }// レコードのインスタンスを作成するlet person = { FirstName = "John" ; LastName = "Doe" ; Age = 30 }名前と年齢、および2つの不変プロパティを受け取るコンストラクタを持つPersonクラス。
/// これは型定義のドキュメントコメントです。type Person ( name : string , age : int ) = member x . Name = name member x . Age = age /// クラスのインスタンス化let mrSmith = Person ( "Smith" , 42 )関数型言語の構文を説明するためによく用いられる簡単な例として、非負の32ビット整数に対する階乗関数があります。以下にF#での記述を示します。
/// パターンマッチング式を使用して、rec factorial n = match n with | 0 -> 1 | _ -> n * factorial ( n - 1 )/// 引数が 1 つの関数には、構文糖衣 (パターン マッチング関数) があります: let rec factorial = function | 0 -> 1 | n -> n * factorial ( n - 1 ) /// fold 演算子と range 演算子を使用しますlet factorial n = [ 1 .. n ] |> Seq . fold (*) 1反復処理の例:
/// 'for' ループを使用した反復処理let printList lst = for x in lst do printfn $ "{x}"/// 高階関数を使用した反復処理let printList2 lst = List.iter ( printfn " % d" ) lst/// 再帰関数とパターンマッチングを使用した反復処理let rec printList3 lst = match lst with | [] -> () | h :: t -> printfn "%d" h printList3 tフィボナッチ数列の例:
/// フィボナッチ数列の公式[< TailCall >] let fib n = let rec g n f0 f1 = match n with | 0 -> f0 | 1 -> f1 | _ -> g ( n - 1 ) f1 ( f0 + f1 ) g n 0 1/// 別のアプローチ - フィボナッチ数の遅延無限シーケンスlet fibSeq = Seq . unfold ( fun ( a , b ) -> Some ( a + b , ( b , a + b ))) ( 0 , 1 )// 偶数フィボナッチ数列[ 1 .. 10 ]を出力|> List . map fib |> List . filter ( fun n -> ( n % 2 ) = 0 ) |> printList// リスト式を使用した同じ処理[ for i in 1 .. 10 do let r = fib i if r % 2 = 0 then yield r ] |> printListWindowsフォームプログラムのサンプル:
// Windows Formsライブラリを開くopen System.Windows.Forms// ウィンドウを作成し、いくつかのプロパティを設定します。let form = new Form ( Visible = true , TopMost = true , Text = "F# へようこそ" )// フォームにテキストを表示するラベルを作成しますlet label = let x = 3 + ( 4 * 5 ) new Label ( Text = $ "{x}" )//フォームにラベルを追加しますform.Controls.Add ( label )// 最後にフォームを実行します[< System . STAThread >] Application . Run ( form )非同期並列プログラミングのサンプル(並列CPUタスクとI/Oタスク):
/// シンプルな素数検出器let isPrime ( n : int ) = let bound = int ( sqrt ( float n )) seq { 2 .. bound } |> Seq . forall ( fun x -> n % x <> 0 )// 非同期ワークフローを使用していますlet primeAsync n = async { return ( n , isPrime n ) }/// 複数のスレッドを使用して、m と n の間の素数を返します。let primes m n = seq { m .. n } |> Seq . map primeAsync |> Async . Parallel |> Async . RunSynchronously |> Array . filter snd |> Array . map fst// テスト素数1000000 1002000を実行|> Array.iter ( printfn "%d" )[F#] は Core ML 設計に根ざしており、特に OCaml とほぼ互換性のあるコア言語を備えています。