2010-01-30 81 views
8

所以我只是完成了我的第一個F#程序,與我的唯一的功能背景色是哈斯克爾的知識一點點(讀:還沒有真正產生任何方案它)。F#:爲什麼我必須爲不帶參數的函數明確指定'unit'?

遇到了一些令人難以置信的行爲後,我才明白,F#使得之間的區別:

prepareDeck = allSuits |> List.collect generateCards |> shuffle 

prepareDeck() = allSuits |> List.collect generateCards |> shuffle 

我注意到,它「緩存」前,從來沒有重新計算,如果它被再次調用,而後者像一個正常的函數一樣對待後者。你不能區分這個功能是否有副作用,但是我的shuffle沒有!

當時這應該是常識?我還沒有看到它在任何教程材料中提及。原因只是解析器中的一個弱點,有點像你在使用它之前如何聲明一個函數

回答

15

大多數F#素材確實解釋了模塊中的所有頂級語句都是在聲明中從上到下執行的。換句話說,你聲明的不是一個函數,而是一個在程序運行時綁定一次的值。

它確實有助於看到反射的代碼。我有一個簡單的文件:

let juliet = "awesome" 
let juliet2() = "awesome" 

的編譯後的代碼看起來是這樣的:

public static string juliet 
{ 
    [CompilerGenerated, DebuggerNonUserCode] 
    get 
    { 
     return "awesome"; 
    } 
} 

//... 

public static string juliet2() 
{ 
    return "awesome"; 
} 

所以一個是靜態屬性,另一個是功能。這是一個理想的物業,因爲想象一下,如果我們有這樣的事情:

let x = someLongRunningDatabaseCall() 

我們只想x被綁定一次,我們不希望它來調用數據庫功能每次我們訪問x

此外,我們可以寫有趣的代碼是這樣的:

> let isInNebraska = 
    printfn "Creating cities set" 
    let cities = set ["Omaha"; "Bellevue"; "Lincoln"; "Papillion"; "La Vista"; "Ralston"] 
    fun n -> cities.Contains(n);; 
Creating cities set 

val isInNebraska : (string -> bool) 

> isInNebraska "Omaha";; 
val it : bool = true 

> isInNebraska "Okaloosa";; 
val it : bool = false 

由於isInNebraska是一種價值,它的直接評價。它只是恰巧,其數據類型是(string -> bool),所以它看起來的功能。因此,即使我們調用1000次函數,我們也只填寫cities集合。

讓我們的代碼比較這樣的:

> let isInNebraska2 n = 
    printfn "Creating cities set" 
    let cities = set ["Omaha"; "Bellevue"; "Lincoln"; "Papillion"; "La Vista"; "Ralston"] 
    cities.Contains(n);; 

val isInNebraska2 : string -> bool 

> isInNebraska2 "Omaha";; 
Creating cities set 
val it : bool = true 

> isInNebraska2 "Okaloosa";; 
Creating cities set 
val it : bool = false 

哎呀,我們創建設定每次我們調用功能的新城市。

所以肯定是價值和功能之間的合法和真正的區別。

+1

不錯的例子。對於任何有興趣的人來說,「Expert F#」書(第8章)在設計用於高效部分應用的功能的背景下進一步發展了這個討論。 – itowlson 2010-01-30 01:29:59

+1

我認爲這裏存在一個缺陷:靜態屬性和靜態方法之間沒有多大區別,**都是幕後的方法**,對屬性的訪問實際上是方法調用(這就是爲什麼您可以標記屬性爲*虛擬*)。我看不到你的「理想財產」。 – 2010-02-02 00:35:45

+1

我相信不同的是,當你有'let julietNoFunction = ...'時,靜態屬性將簡單地返回一個存儲在靜態字段中的值,並且這個值在生成類型的靜態構造函數中只計算一次模塊。我想我已經在Reflector中看到過這個,但我不確定了。無論如何,你提出的方式在使用'let juliet = ...'到'let juliet()= ...'上並沒有顯示任何優勢。 – 2010-02-02 00:46:13

6

這東西幾乎副作用每一種語言是如何工作的。

let name = expr 

運行的代碼「現在」,並且可能導致副作用如果expr有特效。隨後對name的引用不起作用。而

let name() = expr 

定義一個函數,現在已經沒有效果,並且將評估(並有特效)每次name()被調用。

+0

Gotcha。猜猜我很困惑,因爲F#是我用過的第一種功能強大但副作用的語言。 – 2010-01-30 02:26:09

相關問題