在 F#/.NET 中实现标准 ML 签名

Implementing Standard ML signatures in F# / .NET

(问题在底部以粗体显示。)

我正在研究 Chris Okasaki's Purely Functional Data Structures,我试图将第一个数据结构及其实现从标准 ML 转换为 F#。 ML如下(从书中翻译而来):

signature STACK =
sig
    type 'a Stack
    val empty : 'a Stack
    val isEmpty : 'a Stack -> bool
    val cons : 'a * 'a Stack -> 'a Stack
    val head : 'a Stack -> 'a (* raises EMPTY if stack is empty *)
    val tail : 'a Stack -> 'a Stack (* raises EMPTY if stack is empty *)
end

及其第一个实现:

structure List:STACK =
struct
    type 'a Stack = 'a list

    val empty = []
    fun isEmpty s = null s

    fun cons (x,s) = x :: s
    fun head s = hd s
    fun tail s = tl s
end

并且,为了清楚起见,它的第二个实现:

structure CustomStack:STACK =
struct 
    datatype 'a Stack = NIL | CONS of 'a * 'a Stack

    val empty = NIL
    fun isEmpty NIL = true | isEmpty _ = false

    fun cons (x, s) = CONS(x, s)
    fun head NIL = raise EMPTY
      | head (CONS(x, s)) = x
    fun tail NIL = raise EMPTY
      | tail (CONS(x, s)) = s
end

我能够将 ML signature 移植到 F# 几乎一个学期一个学期:

type 'a Stack = Stack of 'a

type 'a STACK =
    val Stack : 'a Stack
    val empty : 'a Stack

    val isEmpty : 'a Stack -> bool
    val cons : 'a * 'a Stack -> 'a Stack
    val head : 'a Stack -> 'a
    val tail : 'a Stack -> 'a Stack

尽管显然无法将 F# 类型实现为任何实质性内容,但此代码仍可编译,因为没有构造函数并且 将其识别为接口(并且不能写成一个,因为 val Stackval empty 不是函数)。它在 fsi 中编译为 class,但显然没有构造函数或任何东西来实现它。上一个片段的 fsi 签名是:

type 'a Stack = | Stack of 'a
type 'a STACK =
  class
    val Stack: 'a Stack
    val empty: 'a Stack
    val isEmpty: 'a Stack -> bool
    val cons: 'a * 'a Stack -> 'a Stack
    val head: 'a Stack -> 'a
    val tail: 'a Stack -> 'a Stack
  end

1.有什么方法可以在 .NET 生态系统中利用它,只拥有像这样的纯类型,实用与否?另外,这是否被认为是某种能够创建不可实现的数据类型的错误?

2。是否可以在不使用接口和抽象 classes 的情况下在 F# 中实现标准 ML 数据结构,或者由于 .NET 的结构方式,它们是强制性的吗?

1。有什么方法可以在 .NET 生态系统中利用它,只拥有像这样的纯类型,实用与否?另外,这是否被认为是某种能够创建不可实现的数据类型的错误?

你已经实现了编译,但它肯定不是你打算实现的。它只是在语法上相似——你使用的是 explicit class syntax,你的 STACK 类型是 class 表面上没有 public 构造函数和一堆 getter-仅属性。

这是你可以通过反思自己检查的东西API。如果您在 FSI 中使用此代码,您可能会发现无论如何都会为该类型生成一个构造函数,并且您可以使用 Activator.Create 来实例化它 - 您会看到所有属性都具有默认的空值。

open System
open System.Reflection

let typ = typeof<STACK<int>>

typ.GetProperties()

let stack = Activator.CreateInstance<STACK<int>>()

这是 FSI 使用 afaik 的产物,在编译代码中你无法构造这种类型的值。

2。无论如何在 F# 中实现标准 ML 数据结构而不使用接口和抽象 classes,或者它们是强制性的,因为 .NET 的结构方式?

就转换概念而言,抽象 classes 及其子类型是 SML 签名和结构最直接的 F# 等价物。

这与 F# 避开 OCaml 模块系统(带有签名、结构和函子)以支持 OOP 系统已经存在的 .NET 中间语言有关。相比之下,F# 模块受到严重限制,它们更像是一个类似于 Haskell 的 grouping/namespacing 工具。

不确定您的体验是什么 - 我知道来自 OOP 背景的语言新手通常急于完全放弃 classes、接口以及他们与 OOP 相关的任何东西,但这是相当误入歧途的方法。这些概念是 F# 的一部分是有原因的,如果不使用它们,您将放弃表达能力的重要部分。

检查 以获取与 F# "translation" 相似的 ML 示例。请注意,这是比 "solid library code" 级别更高的 "toy example" 级别 - 因为建议使用根深蒂固的 .NET 集合接口,以便它与更广泛的生态系统很好地融合。例如,这就是 FSharp.Core 集合所做的。