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

标签 .net types f# sml translate

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

我正在浏览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 签名 移植到 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# 类型实现为任何实质性内容,但该代码仍能编译,因为没有构造函数,而且它被识别为接口(interface)(并且不能写为 1,因为 val Stackvalempty 不是函数)。它在 fsi 中编译为一个类,但显然没有构造函数或任何东西来实现它。上一个代码段的 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。无论如何,是否可以在不使用接口(interface)和抽象类的情况下在 F# 中实现标准 ML 数据结构,或者由于 .NET 的结构方式,它们是强制性的?

最佳答案

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

您所实现的内容可以编译,但它肯定不是您想要实现的内容。它只是在语法上相似 - 您使用的是名为 explicit class syntax 的东西。 ,并且您的 STACK 类型表面上是一个没有公共(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 使用的产物,在编译代码中您无法构造这种类型的值。

<强>2。无论如何,是否可以在不使用接口(interface)和抽象类的情况下在 F# 中实现标准 ML 数据结构,或者由于 .NET 的结构方式,它们是强制性的?

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

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

不确定您的体验如何 - 我知道来自 OOP 背景的语言新手通常渴望完全放弃类、接口(interface)以及与 OOP 相关的任何内容,但这是一种相当误导的方法。这些概念成为 F# 的一部分是有原因的,如果不使用它们,您就会放弃表达事物的能力的很大一部分。

检查this thread有关 ML 到 F#“翻译”的类似示例。请注意,这更多的是“玩具示例”级别,而不是“可靠的库代码”级别 - 因为建议使用根深蒂固的 .NET 集合接口(interface),以便它与更广泛的生态系统很好地融合。例如,这就是 FSharp.Core 集合所做的事情。

关于.net - 在 F#/.NET 中实现标准 ML 签名,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57455136/

相关文章:

wpf - 在 F# 中处理事件

.net - 为什么 MEF 不是 DI/IoC 容器?

.net - 使用 .NET SDK v.4 从 CosmosDB 数据库获取动态对象列表

scala - 部分功能接受单元

scala - 依赖于路径的类型 - 以下代码有什么问题?

f# - 在序列表达式中使用 "use"时的资源管理

f# - F# 中的嵌套联合类型

javascript - Toggle 不使用新浏览器

c# - 在 c# winforms 中使用 paint 方法时如何防止闪烁?

java - 如何在 Java 泛型中获取类型变量的类