【问题标题】:Implementing Standard ML signatures in F# / .NET在 F# / .NET 中实现标准 ML 签名
【发布时间】:2019-12-18 16:13:21
【问题描述】:

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

我正在检查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 中编译为一个类,但显然没有构造函数或任何实现它的东西。之前sn-p的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。无论如何,是否可以在不使用接口和抽象类的情况下在 F# 中实现标准 ML 数据结构,或者由于 .NET 的结构方式,它们是强制性的?

【问题讨论】:

  • 现在没有时间给出完整的答案,但有两件事:1) 几乎可以将 ML 签名移植到 F# 并不是巧合,因为 F# 的语法被采用来自 ML(我认为来自 OCaml)。 2) 您可能有兴趣查看github.com/fsprojects/FSharpx.Collections 的源代码以了解已经实现的内容。

标签: .net types f# sml translate


【解决方案1】:

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

你已经实现的编译,但它肯定不是你打算实现的。它只是在语法上相似 - 您使用的是称为 explicit class syntax 的东西,而您的 STACK 类型是一个表面上没有公共构造函数的类,具有一堆仅限 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 数据结构,或者由于 .NET 的结构方式,它们是强制性的?

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

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

不确定您的经验是什么 - 我知道来自 OOP 背景的语言新手通常渴望完全放弃类、接口以及与 OOP 相关的任何东西,但这是一种相当误导的方法。这些概念之所以成为 F# 的一部分是有原因的,如果不使用它们,您就放弃了表达事物的能力的重要部分。

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

【讨论】:

    猜你喜欢
    • 2015-05-01
    • 2019-01-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-04-24
    • 2013-04-04
    • 2021-05-28
    • 1970-01-01
    相关资源
    最近更新 更多