打開網(wǎng)易新聞 查看精彩圖片

在編程領(lǐng)域,數(shù)百種編程語言各有千秋,各自適應(yīng)不同的應(yīng)用場景。開發(fā)者 Bozhidar Batsov 近日發(fā)布了一篇主題為《Why F#》的文章,引發(fā)了廣泛關(guān)注,甚至登上了 Hacker News 的熱榜。這也讓人不禁好奇,F(xiàn)# 這門已經(jīng)發(fā)布了 20 年的編程語言,如今有哪些值得關(guān)注的特別之處?

對此,一位網(wǎng)友評論道:“F# 與 AI 非常契合,所有 AI 模型都能輕松生成慣用的 F# 代碼。更重要的是,F(xiàn)# 擁有一個非常強大的類型系統(tǒng),這使得在實現(xiàn)之前,通過 AI 模型準(zhǔn)確建模問題,能夠有效捕獲潛在的幻覺錯誤?!北疚闹?,Batsov 深入探討了 F# 的優(yōu)缺點、生態(tài)系統(tǒng)及開發(fā)工具,揭示了這門語言在現(xiàn)代編程和AI領(lǐng)域中的獨特優(yōu)勢。

原文鏈接:https://batsov.com/articles/2025/03/30/why-fsharp/

作者 | Bozhidar Batsov

責(zé)編 | 蘇宓

出品 | CSDN(ID:CSDNnews)

如果幾個月前有人告訴我,我會在時隔 15 年之后再次使用 .NET,我肯定會對此嗤之以鼻。在我早期職業(yè)生涯中,我曾接觸過 .NET 和 Java,盡管 .NET 在某些方面比 Java 做得更好(畢竟它有機會從 Java 早期的錯誤中吸取教訓(xùn)),但我最終選擇了 Java,因為它是真正的跨平臺環(huán)境。

其實在過去幾年里,我斷斷續(xù)續(xù)地也在玩 OCaml(一個函數(shù)式、指令式、模塊化、面向?qū)ο蟮耐ㄓ玫木幊陶Z言),可以說它已經(jīng)成為我最喜歡的編程語言之一,就像 Ruby 和 Clojure 一樣。我對 OCaml 的研究讓我最近開始關(guān)注F#——一種面向 .NET 的 ML 語言,由微軟開發(fā)。它是 C#(主要是面向?qū)ο蟮恼Z言)的函數(shù)式對應(yīng)版本,也是最新的 ML 語言之一。

什么是 F#?

不幸的是,沒有人能被告知“Matrix”是什么,你必須自己去看。 ——莫菲斯,《黑客帝國》

在討論 F# 之前,我們首先應(yīng)該回答“F# 是什么?”這個問題。我借用官方頁面的一些描述來回答:

F# 是一種通用編程語言,用于編寫簡潔、健壯且高性能的代碼。

F# 讓你能夠編寫簡潔、自解釋的代碼,讓你的注意力集中在問題領(lǐng)域,而不是編程的細節(jié)。

它在保證速度和兼容性的同時做到這一點——它是開源的、跨平臺的,并且具有良好的互操作性。

open System // Gets access to functionality in System namespace.

// Defines a list of names
let names = [ "Peter"; "Julia"; "Xi" ]

// Defines a function that takes a name and produces a greeting.
let getGreeting name = $"Hello, {name}"

// Prints a greeting for each name!
names
|> List.map getGreeting
|> List.iter (fun greeting -> printfn $"{greeting}! Enjoy your F#")

趣聞:F# 是讓管道操作符 |> 流行起來的編程語言。

F# 擁有眾多特性,包括:

  • 輕量級語法

  • 默認(rèn)不可變

  • 類型推導(dǎo)和自動泛化

  • 一等函數(shù)(First-class functions)

  • 強大的數(shù)據(jù)類型

  • 模式匹配

  • 異步編程

完整的特性集可以在 F# 語言指南中找到:https://learn.microsoft.com/en-us/dotnet/fsharp/language-reference/

看起來很有前途,對吧?

F# 1.0 由微軟研究院于 2005 年 5 月正式發(fā)布。最初,它由微軟劍橋研究院的 Don Syme 開發(fā),起源于一個名為Caml.NET的研究項目,旨在將 OCaml 引入 .NET 平臺。2010 年,隨著 F# 2.0 的發(fā)布,F(xiàn)# 從微軟研究院轉(zhuǎn)移到微軟的開發(fā)者工具部門,成為正式支持的開發(fā)語言。

自最初發(fā)布以來,F(xiàn)# 一直在不斷演進,最近的版本F# 9.0已于 2024 年 11 月發(fā)布。恰逢 F# 誕生 20 周年,這時候關(guān)注它似乎再合適不過了!

我想嘗試 F# 的原因有幾個:

  • .NET 在幾年前變成了開源且可移植的,我想看看它的進展如何。

  • 我很好奇 F# 是否相比 OCaml 有額外的優(yōu)勢。

  • 我聽說 F# 的開發(fā)工具(如 Rider 和 Ionide)體驗不錯。

  • 我喜歡嘗試新的編程語言。

接下來是我對幾個方面的初步印象。

語言特性

作為 ML 語言家族的一員,F(xiàn)# 的語法對熟悉 OCaml 的人來說不會有太大驚喜。不過,由于熟悉 OCaml 的人并不多,我要補充一點:Haskell 程序員會很容易上手,Lisp 開發(fā)者也會感到親切。

對于其他人來說,掌握基礎(chǔ)知識并不難。

// function application
printfn "Hello, World!"
// function definition
let greet name =
    printfn "Hello, %s!" name
greet "World"
// whitespace is significant, like in Python
let foo =
    let i, j, k = (1, 2, 3)
    // Body expression:
    i + 2 * j + 3 * k
// conditional expressions
let test x y =
  if x = y then "equals"
  elif x < y then "is less than"
  else "is greater than"
printfn "%d %s %d." 10 (test 10 20) 20
// Looping over a list.
let list1 = [ 1; 5; 100; 450; 788 ]
for i in list1 do
   printfn "%d" i
// Looping over a sequence of tuples
let seq1 = seq { for i in 1 .. 10 -> (i, i*i) }
for (a, asqr) in seq1 do
  printfn "%d squared is %d" a asqr
// A simple for...to loop.
let function1 () =
  for i = 1 to 10 do
    printf "%d " i
  printfn ""
// A for...to loop that counts in reverse.
let function2 () =
  for i = 10 downto 1 do
    printf "%d " i
  printfn ""
// Records
// Labels are separated by semicolons when defined on the same line.
type Point = { X: float; Y: float; Z: float }
// You can define labels on their own line with or without a semicolon.
type Customer =
    { First: string
      Last: string
      SSN: uint32
      AccountNumber: uint32 }
let mypoint = { X = 1.0; Y = 1.0; Z = -1.0 }
// Discriminated Union
type Shape =
    | Circle of radius: float
    | Rectangle of width: float * height: float
// Functing using pattern matching
let area shape =
    match shape with
    | Circle radius -> System.Math.PI * radius * radius
    | Rectangle (width, height) -> width * height
let circle = Circle 5.0
let rectangle = Rectangle(4.0, 3.0)
printfn "Circle area: %f" (area circle)
printfn "Rectangle area: %f" (area rectangle)

這里沒什么令人震驚的,對吧?

這是另一個稍微復(fù)雜一些的例子:

open System

// Sample data - simple sales records
type SalesRecord = { Date: DateTime; Product: string; Amount: decimal; Region: string }

// Sample dataset
let sales = [
    { Date = DateTime(2023, 1, 15); Product = "Laptop"; Amount = 1200m; Region = "North" }
    { Date = DateTime(2023, 2, 3);  Product = "Phone";  Amount = 800m;  Region = "South" }
    { Date = DateTime(2023, 1, 20); Product = "Tablet"; Amount = 400m;  Region = "North" }
    { Date = DateTime(2023, 2, 18); Product = "Laptop"; Amount = 1250m; Region = "East" }
    { Date = DateTime(2023, 1, 5);  Product = "Phone";  Amount = 750m;  Region = "West" }
    { Date = DateTime(2023, 2, 12); Product = "Tablet"; Amount = 450m;  Region = "North" }
    { Date = DateTime(2023, 1, 28); Product = "Laptop"; Amount = 1150m; Region = "South" }
]

// Quick analysis pipeline
let salesSummary =
    sales
    |> List.groupBy (fun s -> s.Product)                          // Group by product
    |> List.map (fun (product, items) ->                          // Transform each group
        let totalSales = items |> List.sumBy (fun s -> s.Amount)
        let avgSale = totalSales / decimal (List.length items)
        let topRegion =
            items
            |> List.groupBy (fun s -> s.Region)                   // Nested grouping
            |> List.maxBy (fun (_, regionItems) ->
                regionItems |> List.sumBy (fun s -> s.Amount))
            |> fst

        (product, totalSales, avgSale, topRegion))
    |> List.sortByDescending (fun (_, total, _, _) -> total)      // Sort by total sales

// Display results
salesSummary
|> List.iter (fun (product, total, avg, region) ->
    printfn "%s: $%M total, $%M avg, top region: %s"
        product total avg region)

你可以嘗試將上面的代碼片段保存到一個名為 Sales.fsx 的文件中,然后這樣運行它:

dotnet fsi Sales.fsx

現(xiàn)在你已經(jīng)知道,F(xiàn)# 是進行臨時腳本(ad-hoc scripts)的絕佳選擇!此外,直接運行 dotnet fsi 會啟動F# REPL(交互式環(huán)境),讓你可以隨意探索這門語言。

我不會在這里詳細介紹 F#,如果你想快速了解 F# 的語法,我建議閱讀 F# 快速入門教程:https://learn.microsoft.com/en-us/dotnet/fsharp/tour

讓我印象深刻的一點是,F(xiàn)# 語言設(shè)計者非常注重降低入門門檻,通過許多細節(jié)優(yōu)化提升新手體驗。以下是幾個示例,這些可能對你來說無關(guān)緊要,但如果你熟悉 OCaml,就會發(fā)現(xiàn)它們的意義所在:

// line comments
(* the classic ML comments are around as well *)

// mutable values
let mutable x = 5
x <- 6

// ranges and slices
let l = [1..2..10]
name[5..]

// C# method calls look pretty natural
let name = "FOO".ToLower()

// operators can be overloaded for different types
let string1 = "Hello, " + "world"
let num1 = 1 + 2
let num2 = 1.0 + 2.5

// universal printing
printfn "%A" [1..2..100]

我猜其中一些改動可能會引發(fā)爭議,具體取決于你是否是ML 語言的原教旨主義者。但在我看來,任何讓 ML 語言更受歡迎的改進,都是一件好事。

順便提一句,F(xiàn)# 處理Unicode 字符串正則表達式也非常方便!

經(jīng)常有人說,F# 主要是C# 未來功能的試驗田。也許這是真的,但由于我沒有長期關(guān)注這兩門語言,所以沒法給出自己的判斷。不過,我確實驚訝地發(fā)現(xiàn),廣受歡迎的 async/await 語法最早竟然是起源于 F# 2.0!

在 2012 年,隨著C# 5的發(fā)布,async/await 語法才正式進入 C#,并逐漸流行開來。這項功能讓開發(fā)者能夠?qū)懗?b>像同步代碼一樣易讀,但同時又能享受手寫異步代碼的所有優(yōu)勢(例如避免 UI 線程被阻塞)。如今,這種模式已經(jīng)被許多現(xiàn)代編程語言借鑒,比如Python、JavaScript、Swift、Rust,甚至 C++。

F# 的異步編程方式雖然和 async/await 有些不同,但目標(biāo)是一樣的。實際上,async/await 只是 F#更早推出的異步機制的簡化版本,該機制最早出現(xiàn)在 F# 2.0。

—— Isaac Abraham,《F# in Action》

未來 F# 會如何發(fā)展,還需要時間檢驗。但我認(rèn)為,C#很難完全取代 F#

我還發(fā)現(xiàn)了一條來自2022 年的好消息,表明微軟可能會加大對 F# 的投資

“終于有好消息了!過去 10 年,F(xiàn)# 在微軟內(nèi)部只有 2.5 個人在維護(再加上一些社區(qū)貢獻者)。但在 2022 年夏天,微軟終于決定 正式投資 F#,并在布拉格組建了一支完整的 F# 開發(fā)團隊 。我也是這個團隊的成員,作為一名多年的 F# 粉絲,我很高興終于有了真正的進展?!?/blockquote>

從 F# 8.0 到 F# 9.0 的變化來看,這支新團隊確實做出了不少卓越的工作!

F# 生態(tài)系統(tǒng)

短短幾天的體驗,很難全面評估 F# 的生態(tài),但整體來看,原生的 F# 庫和框架并不多。大多數(shù)開發(fā)者主要依賴.NET 核心 API針對 C# 的第三方庫。這在托管語言(如 Scala、Clojure、Groovy)中是很常見的情況,因此也不算意外。

如果你想了解 F# 生態(tài),可以查看Awesome F#(https://github.com/fsprojects/awesome-fsharp),它整理了 F# 相關(guān)的流行庫、工具和框架。以下是一些值得關(guān)注的項目:

Web 開發(fā)相關(guān)

  • Giraffe:基于 ASP.NET Core 的輕量級 Web 開發(fā)庫,提供函數(shù)式開發(fā)方式。
  • Suave:一個簡潔的 Web 服務(wù)器庫,提供組合式的路由和任務(wù)管理(Giraffe 受 Suave 啟發(fā))。
  • Saturn:基于 Giraffe 和 ASP.NET Core,提供類似 Ruby on Rails 和 Elixir Phoenix 的 MVC 框架。
  • Bolero:基于 WebAssembly 和 Blazor 的 F# 前端開發(fā)框架。
  • Fable:將 F# 代碼編譯為 JavaScript,可與 React、Node.js 等生態(tài)集成。
  • Elmish:MVU(Model-View-Update)架構(gòu),適用于 F# 前端開發(fā),常與 Fable 結(jié)合使用。
  • SAFE Stack:端到端的 F# Web 開發(fā)棧,結(jié)合 Saturn、Azure、Fable 和 Elmish,提供類型安全的開發(fā)體驗。
數(shù)據(jù)科學(xué)相關(guān)
  • Deedle:類似 Python pandas 的數(shù)據(jù)分析庫。
  • DiffSharp:支持自動微分和機器學(xué)習(xí)的庫。
  • FsLab:數(shù)據(jù)科學(xué)工具集,包括可視化和統(tǒng)計分析工具。

目前我還沒有深入體驗這些庫,所以具體的反饋和推薦留到以后再說。

打開網(wǎng)易新聞 查看精彩圖片

文檔

官方文檔質(zhì)量不錯,不過有點分散,一部分在微軟官網(wǎng)(https://learn.microsoft.com/en-us/dotnet/fsharp/what-is-fsharp),另一部分在F# 軟件基金會的 fsharp.org 站點(https://fsharp.org/)上,這點讓我有些疑惑。

我特別喜歡以下幾部分文檔:

  • F# 風(fēng)格指南:https://learn.microsoft.com/en-us/dotnet/fsharp/style-guide/

  • F# 設(shè)計 RFC 倉庫(所有語言都應(yīng)該有類似的 RFC 機制?。篽ttps://github.com/fsharp/fslang-design

  • F# 標(biāo)準(zhǔn)庫 API:https://fsharp.github.io/fsharp-core-docs/

此外,F(xiàn)# for Fun and Profit(https://fsharpforfunandprofit.com/)也是一個不錯的學(xué)習(xí)資源(盡管內(nèi)容有點舊)。

開發(fā)工具F# 的開發(fā)工具支持一直是個問題。過去,F(xiàn)#主要在 Visual Studio 上體驗良好,但在其他編輯器上的支持較差。不過,過去十年情況有了很大改善。 2014 年,Tomas Petricek、Ryan Riley 和 Dave Thomas開發(fā)了FSharp.Compiler.Service(FCS)包,將F# 編譯器、編輯器工具和腳本引擎整合成一個庫。這使得 F# 能夠適配更多編輯器,例如:

  • JetBrains Rider—— .NET 生態(tài)的強大 IDE,對 F# 友好。

  • Emacs(fsharp-mode)

  • Zed(第三方插件)

  • Helix(內(nèi)置 F# 支持)

  • VS Code(Ionide 插件)

不過,我還是要提一下,我發(fā)現(xiàn) F# 的工具鏈在某些方面有所欠缺:

  • fsharp-mode 目前還不支持TreeSitter(至少暫時如此),而且似乎開發(fā)并不活躍。從代碼來看,它是從caml-mode派生出來的。

  • Zed 對 F# 的支持非常簡陋。

  • VS Code中,選擇區(qū)域的擴展和收縮功能竟然是壞的,這對于 F# 這樣一個本應(yīng)在 VS Code 上有良好支持的語言來說,實在讓人費解。

  • 我確實很難適應(yīng)VS Code的快捷鍵綁定(對我來說,修飾鍵和功能鍵太多了),以及它的編輯模式。所以,我可能會繼續(xù)堅持使用Emacs,或者抽出時間好好研究neovim

好消息是,大家似乎都在使用同一個代碼格式化工具Fantomas,包括 F# 官方團隊,這一點很棒!不過,F(xiàn)# 的linter(代碼檢查工具)情況就不太理想,目前唯一流行的FSharpLint似乎已經(jīng)變成棄坑項目了。不過話說回來,F(xiàn)# 的編譯器本身已經(jīng)足夠強大,所以對 linter 的需求也沒有那么高。

看起來微軟并沒有特別投入到 F# 工具鏈的支持中,幾乎所有的核心工具項目都是由社區(qū)驅(qū)動的。

至于 AI 輔助編程工具(如 GitHub Copilot),對 F# 的支持也不錯,不過我暫時沒有深入體驗。

最終來說,任何支持 LSP(語言服務(wù)器協(xié)議)的編輯器都應(yīng)該可以勝任 F# 開發(fā)。

順便說一下,我在使用F#(以及 OCaml)編程時有一個有趣的發(fā)現(xiàn):當(dāng)你使用一門擁有優(yōu)秀類型系統(tǒng)的語言時,對編輯器的需求其實并不高。大多數(shù)時候,我只需要一些內(nèi)聯(lián)類型信息(比如類似CodeLens的功能)、自動補全,以及能夠方便地將代碼發(fā)送到 F# 交互式環(huán)境(fsi就夠用了。簡單才是終極的復(fù)雜……

其他值得關(guān)注的工具:

  • Paket - 一個用于.NET項目的依賴管理工具??梢园阉醋魇?b>bundler(Ruby)、npm(Node.js)或pip(Python)在.NET生態(tài)中的等價物。

  • FAKE - 一個基于 F# 的DSL,用于構(gòu)建任務(wù)等。它類似于 Ruby 的rake,一些人甚至認(rèn)為這是在現(xiàn)有 .NET 項目中“偷偷”引入 F#的最佳方式。

應(yīng)用場景

.NET 生態(tài)本身非常龐大,所以F# 理論上適用于任何 .NET 相關(guān)領(lǐng)域。

不過,我覺得F# 特別適合數(shù)據(jù)分析,因為它提供了類型提供程序(Type Providers)這一獨特功能。

下面是一個使用JSON 類型提供程序的示例:

#r "nuget: FSharp.Data"

open System
open FSharp.Data

// Define the type based on a sample JSON entry
type PeopleJson = JsonProvider<"""
[
  { "name": "Alice", "age": 30, "skills": ["F#", "C#", "Haskell"] }
]
""">

// Simulated JSON list (could be loaded from file or API)
let jsonListString = """
[
  { "name": "Alice",  "age": 30, "skills": ["F#", "C#", "Haskell"] },
  { "name": "Bob",    "age": 25, "skills": ["F#", "Rust"] },
  { "name": "Carol",  "age": 28, "skills": ["OCaml", "Elixir"] },
  { "name": "Dave",   "age": 35, "skills": ["Scala", "F#"] },
  { "name": "Eve",    "age": 32, "skills": ["Python", "F#", "ML"] },
  { "name": "Frank",  "age": 29, "skills": ["Clojure", "F#"] },
  { "name": "Grace",  "age": 27, "skills": ["TypeScript", "Elm"] },
  { "name": "Heidi",  "age": 33, "skills": ["Haskell", "PureScript"] },
  { "name": "Ivan",   "age": 31, "skills": ["Racket", "F#"] },
  { "name": "Judy",   "age": 26, "skills": ["ReasonML", "F#"] }
]
"""

// Parse the JSON
let people = PeopleJson.Parse(jsonListString)

// Print it
printfn "People in the list:\n"
for p in people do
    printfn "%s (age %d) knows:" p.Name p.Age
    p.Skills |> Array.iter (printfn "  - %s")
    printfn "" 

我第一次看到這個時,感覺就像魔法一樣。F# 能夠從一個小的數(shù)據(jù)樣本中推斷出數(shù)據(jù)的結(jié)構(gòu)和類型,并自動生成解析器。你可以將代碼保存為TypeProvidersDemo.fsx 文件,然后像這樣運行它:

dotnet fsi TypeProvidersDemo.fsx

但這還不止于此,你還可以輕松地從 HTML 表格中提取數(shù)據(jù)并對其進行可視化:

#r "nuget:FSharp.Data"
#r "nuget: Plotly.NET, 3.0.1"

open FSharp.Data
open Plotly.NET

type LondonBoroughs = HtmlProvider<"https://en.wikipedia.org/wiki/List_of_London_boroughs">
let boroughs = LondonBoroughs.GetSample().Tables.``List of boroughs and local authorities``

let population =
    boroughs.Rows
    |> Array.map (fun row ->
                  row.Borough,
                  row.``Population (2022 est)``)
    |> Array.sortBy snd
    |> Chart.Column
    |> Chart.show

如果你運行該腳本,你將在瀏覽器中看到一張展示倫敦各區(qū)人口的精美圖表。不錯吧!

打開網(wǎng)易新聞 查看精彩圖片

在這里,我們也體會到了 F# 腳本中使用外部庫(例如 Plotly.NET)的便捷性!

展望未來,我認(rèn)為 F# 適用于后端服務(wù),甚至可以用于構(gòu)建全棧應(yīng)用,盡管我尚未深入嘗試 F# 在這一領(lǐng)域的原生解決方案。

Fable 和 Elmish 使 F# 成為客戶端編程的可行選擇,并可能為你在日常工作中引入 F# 提供一種簡單的途徑。

注意:傳統(tǒng)上,F(xiàn)able 主要用于轉(zhuǎn)換為 JavaScript,但自 Fable 4 以來,它還支持轉(zhuǎn)換為 TypeScript、Rust、Python 等其他語言。

以下是將 F# 代碼轉(zhuǎn)換為其他語言的簡單示例:

# If you want to transpile to JavaScript
dotnet fable
# If you want to transpile to TypeScript
dotnet fable --lang typescript
# If you want to transpile to Python
dotnet fable --lang python

打開網(wǎng)易新聞 查看精彩圖片
社區(qū)情況

我對 F# 社區(qū)的初步印象是規(guī)模相對較小,可能甚至比 OCaml 社區(qū)還要小。F# 相關(guān)討論最活躍的地方似乎是 Reddit 和 Discord(Reddit 上列出的那個)。

目前,我還不太清楚微軟在 F# 社區(qū)中扮演什么角色,因為整體上很少看到他們的參與。

對我來說,社區(qū)規(guī)模小并不是問題,只要它足夠活躍、充滿生機就行。此外,我也發(fā)現(xiàn)自己往往更容易融入小型社區(qū)。當(dāng)年從 Java 轉(zhuǎn)向 Ruby 時,這種社區(qū)氛圍和歸屬感的變化簡直是天壤之別。

關(guān)于 F# 的書籍、社區(qū)網(wǎng)站或博客并不多,但這也是我預(yù)料之中的情況。

我發(fā)現(xiàn)的一些比較重要的社區(qū)項目包括:

  • Amplifying F# —— 一個致力于推廣 F# 并吸引更多企業(yè)參與的項目

  • F# for Fun and Profit —— 一個收集了 F# 教程和文章的網(wǎng)站

  • F# Lab —— 一個社區(qū)驅(qū)動的 F# 數(shù)據(jù)科學(xué)工具包

  • F# Weekly —— 每周更新的 F# 相關(guān)新聞和動態(tài)

總的來說,F(xiàn)# 還有很多推廣和吸引新用戶的空間,但對于一個已經(jīng)存在 20 年的項目來說,這并不容易。我仍然不太理解為什么微軟沒有更積極地推廣 F#,因為我覺得它本可以成為微軟的一個很好的營銷工具。

整體而言,我目前還不太有資格對 F# 社區(qū)做太多評價。

受歡迎程度

是否在乎一門編程語言的“流行度”,這取決于個人。有些人經(jīng)常問我,為什么花這么多時間在一些幾乎不會帶來職業(yè)機會的語言上,比如:

  • Emacs Lisp

  • Clojure

  • OCaml

  • F#

當(dāng)然,職業(yè)發(fā)展很重要,但對我來說,還有其他因素:

  • 編程的樂趣(F# 里的 “F” 代表 “Fun”)

  • 學(xué)習(xí)新的編程范式和思想

  • 挑戰(zhàn)自己,換一種思維方式去編程

從大多數(shù)主流指標(biāo)來看,F(xiàn)# 并不是一門流行的語言。它在 TIOBE、Stack Overflow 或大多數(shù)招聘網(wǎng)站上的排名都不高。但如果與其他主流的函數(shù)式編程語言相比,F(xiàn)# 也沒有更不受歡迎。遺憾的是,函數(shù)式編程至今仍未進入主流,或許永遠不會。


打開網(wǎng)易新聞 查看精彩圖片
F# 與 OCaml 的比較

F# 最初的目標(biāo)是將 OCaml 的優(yōu)勢帶入 .NET,同時讓 .NET 生態(tài)可以使用 OCaml 風(fēng)格的強類型函數(shù)式編程。最初的任務(wù)相對明確:重新實現(xiàn) OCaml 語言的核心部分,并移植部分標(biāo)準(zhǔn)庫,使其可以在 .NET 運行時(CLR)上運行。

如果你問大多數(shù)人 F# 優(yōu)于 OCaml 的優(yōu)缺點,你可能會得到以下的答案:

F# 的優(yōu)點

  • 運行在 .NET 平臺上:可以使用大量 .NET 生態(tài)中的庫

  • 由微軟支持

  • 對 OO 開發(fā)者來說更容易上手:語法比 OCaml 略微簡單、編譯器的錯誤和警告更易理解、調(diào)試更友好

  • 強大的異步編程支持

  • 具有 OCaml 所沒有的一些獨特特性,比如匿名記錄、活動模式、計算表達式、序列推導(dǎo)、類型提供器、單位類型

F# 的缺點

  • 運行在 .NET 上(這既是優(yōu)點也是缺點):與 .NET 的互操作性影響了一些語言設(shè)計(如允許 null 值)

  • 由微軟支持(有些人不喜歡微軟),微軟對 F# 的資源投入似乎較少,長期支持不明朗

  • 命名風(fēng)格:F# 偏向 PascalCase 和 camelCase,而 OCaml 使用 snake_case

  • 缺少一些 OCaml 的特性,如一流的模塊和函子(First-class Modules & Functors)、廣義代數(shù)數(shù)據(jù)類型(GADTs)

  • 沒有 OCaml 那只可愛的駱駝 Logo

  • F# 這個名字雖然很酷,但在搜索和文件命名時可能會帶來困擾(經(jīng)常會看到 FSharp)

F# 和 OCaml 都可以編譯為 JavaScript,F(xiàn)# 通過Fable,OCaml 通過Js_of_ocamlMelange。從表面上看,F(xiàn)able 似乎更成熟,但我沒有深入研究它們,所以無法給出詳細對比。

總體而言,這兩者都是強大但小眾的語言,未來很難進入主流。不過,由于 .NET 生態(tài)龐大,F(xiàn)# 可能更容易在職業(yè)環(huán)境中找到落腳點,尤其是與 C# 代碼庫共存時。

總結(jié)

問:C# 能做而 F# 不能做的事情是什么?

答:拋出 NullReferenceException!

—— 來自 F# 社區(qū)的笑話

總體來說,我比預(yù)期更喜歡 F#!它讓我想起當(dāng)年學(xué)習(xí) Clojure 的經(jīng)歷 —— 當(dāng)時 Clojure 是最實用的 Lisp 語言之一,主要得益于它與 Java 生態(tài)的良好互操作性。

如果 .NET 從一開始就是跨平臺和開源的,也許ClojureCLR會和 Clojure 一樣流行,而 F# 也可能會擁有更大的社區(qū)和更廣泛的應(yīng)用。事實上,如果沒有 .NET Core,我可能根本不會再碰 .NET,而我相信有很多人和我一樣。F# 直到 2010 年才開源,這無疑影響了它的早期推廣。

即便如此,F(xiàn)# 依然是一門值得學(xué)習(xí)的語言,尤其適合那些熟悉 .NET 的開發(fā)者。對于想要深入了解 ML(元語言)家族語言的人來說,F(xiàn)# 也是一個不錯的選擇。

更重要的是,F(xiàn)able 讓 F# 可以運行在 JavaScript、Dart、Rust 和 Python 等多個環(huán)境中,這進一步拓寬了它的適用場景。

所以,為什么要學(xué) F#?

F# 社區(qū)有句話——F# 里的 “F” 代表 “Fun”(有趣)。

在我短暫的體驗中,這一點確實得到了驗證!

此外,如果你的 F# 代碼能夠成功編譯,那么它很可能會按照你的預(yù)期運行 —— 這在編程界可是個很寶貴的特性!