Syntax F#:为什么我必须明确指定';单位';对于不带参数的函数?

Syntax F#:为什么我必须明确指定';单位';对于不带参数的函数?,syntax,f#,function,Syntax,F#,Function,因此,我刚刚完成了我的第一个F#程序,我唯一的功能背景是对Haskell有一点了解(阅读:在其中没有真正生成任何程序) 在经历了一些令人难以置信的行为之后,我开始意识到F#区分了: prepareDeck = allSuits |> List.collect generateCards |> shuffle 及 我注意到它“缓存”了前者,如果再次调用它,就不会重新计算,而它将后者视为一个普通函数。很明显,如果所讨论的函数没有副作用,您就无法区分它们之间的区别,但我的shuffle确

因此,我刚刚完成了我的第一个F#程序,我唯一的功能背景是对Haskell有一点了解(阅读:在其中没有真正生成任何程序)

在经历了一些令人难以置信的行为之后,我开始意识到F#区分了:

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

我注意到它“缓存”了前者,如果再次调用它,就不会重新计算,而它将后者视为一个普通函数。很明显,如果所讨论的函数没有副作用,您就无法区分它们之间的区别,但我的
shuffle
确实有副作用

这应该是常识吗?我还没有在任何教程材料中看到它。原因是否只是解析器中的一个弱点,有点像您在使用函数之前声明函数的方式

大多数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()
> 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
一个是静态属性,另一个是函数。这是一个理想的属性,因为想象一下如果我们有这样的东西:

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

//...

public static string juliet2()
{
    return "awesome";
}
let x = someLongRunningDatabaseCall()
> 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
我们只希望
x
绑定一次,不希望每次访问
x
时它都调用数据库函数

此外,我们还可以编写如下有趣的代码:

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

//...

public static string juliet2()
{
    return "awesome";
}
let x = someLongRunningDatabaseCall()
> 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次,我们也只需填充一次
城市集

让我们将该代码与此进行比较:

> 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
哎呀,我们每次调用这个函数都会创建一个新的城市集


因此,在值和函数之间确实存在着合法和真实的区别。

这就是几乎每种语言都会产生副作用的情况

let name = expr
“立即”运行代码,如果
expr
有影响,可能会导致副作用。对
name
的后续引用无效。鉴于

let name() = expr

定义一个函数,现在没有效果,每次调用
name()
时都会计算(并产生效果)。

这是一个很好的例子。对于任何感兴趣的人来说,《专家F#》一书(第8章)在为有效的局部应用程序设计函数的背景下进一步发展了这一讨论。我认为这里有一个缺陷:静态属性和静态方法之间没有太大区别,因为两者都是幕后方法,对属性的访问实际上是一个方法调用(这就是为什么可以将属性标记为虚拟的)。我看不到您的“理想属性”。我认为区别在于,当您使用
let julietNoFunction=…
时,静态属性将只返回存储在静态字段中的值,并且该值仅在模块生成类型的静态构造函数中计算一次。我想我在《反射器》中看到过,但我不确定了。不管怎样,你展示它的方式在使用
let juliet=…
let juliet()。我想我很困惑,因为F#是我使用的第一种功能性但有副作用的语言。