18

ML 仿函数可以用 .NET 接口和泛型实际表达吗?是否有一个高级 ML 仿函数使用示例来对抗这种编码?

答案摘要

在一般情况下,答案是否定的。ML 模块提供不直接映射到 .NET 概念的功能(例如通过签名 [ 1 ] 共享规范)。

但是,对于某些用例,可以翻译 ML 习语。这些案例不仅包括基本的Set函子 [ 2 ],还包括 monads [ 3 ] 的函子编码,以及 Haskell 的更高级用途,例如 finally 无标签解释器 [ 4 , 5 ]。

实际编码需要妥协,例如半安全向下转换。你的里程会很谨慎。

博客和代码:

  1. 博客.matthewdoig.com
  2. highlogics.blogspot.com
  3. F#中的monad函子
4

5 回答 5

12

HigherLogics 是我的博客,我花了很多时间研究这个问题。限制确实是对类型构造函数的抽象,也就是“泛型优于泛型”。模仿 ML 模块和仿函数的最佳方法似乎需要至少一个(半安全)演员表。

它基本上归结为定义一个抽象类型,以及一个对应于在该类型上运行的模块签名的接口。抽象类型和接口共享一个类型参数 B,我称之为“品牌”;品牌一般只是实现模块接口的子类型。品牌确保传入的类型是模块期望的正确子类型。

// signature
abstract class Exp<T, B> where B : ISymantics<B> { }
interface ISymantics<B> where B : ISymantics<B>
{
  Exp<int, B> Int(int i);
  Exp<int, B> Add(Exp<int, B> left, Exp<int, B> right);
}
// implementation
sealed class InterpreterExp<T> : Exp<T, Interpreter>
{
  internal T value;
}
sealed class Interpreter : ISymantics<Interpreter>
{
  Exp<int, Interpreter> Int(int i) { return new InterpreterExp<int> { value = i }; }
  Exp<int, Interpreter> Add(Exp<int, Interpreter> left, Exp<int, Interpreter> right)
  {
    var l = left as InterpreterExp<int>; //semi-safe cast
    var r = right as InterpreterExp<int>;//semi-safe cast
    return new InterpreterExp<int> { value = l.value + r.value; }; }
  }
}

如您所见,强制转换大多是安全的,因为类型系统确保表达式类型的品牌与解释器的品牌相匹配。解决这个问题的唯一方法是,如果客户创建自己的 Exp 类并指定 Interpreter 品牌。有一种更安全的编码也可以避免这个问题,但是对于普通编程来说太笨拙了。

后来我使用了这种编码,并翻译了Oleg 用 MetaOCaml 编写的一篇论文中的示例,以使用 C# 和 Linq。解释器可以透明地运行使用这种嵌入式语言在 ASP.NET 中的服务器端或作为 JavaScript 的客户端编写的程序。

这种对解释器的抽象是 Oleg 的最终无标记编码的一个特征。博客文章中提供了他论文的链接。

接口在 .NET 中是一等的,由于我们使用接口对模块签名进行编码,因此模块和模块签名在这种编码中也是一等的。因此,函子直接使用接口代替模块签名,即。他们将接受 ISymantics<B> 的实例并将任何调用委托给它。

于 2009-09-23T14:08:23.720 回答
8

我不太了解 ML 函子,无法真正回答您的问题。但我会说 .Net 的一个限制因素,我总是发现单子编程是无法抽象 'M' 在“forall M. some type expression with M<T> ”的意义上(例如,其中 M 是一个类型构造函数(接受一个或多个泛型参数的类型))。因此,如果这是您有时需要/使用仿函数的东西,那么我非常有信心在 .Net 上没有很好的方式来表达它。

于 2009-09-15T17:11:50.687 回答
7

ML 模块的关键特性之一是共享规范。.NET 中没有能够模拟它们的机制——所需的机制太不同了。

您可以尝试通过将共享类型转换为参数来做到这一点,但这不能忠实地模拟定义签名的能力,然后再对其应用共享,可能以多种不同的方式。

在我看来,.NET 将受益于拥有这种机制的东西——然后它将更接近于真正支持现代语言的多样性。希望包括像 MixML 这样的模块系统的最新进展,在我看来,这是模块系统的未来。 http://www.mpi-sws.org/~rossberg/mixml/

于 2009-09-19T11:16:04.283 回答
3

我现在发布了我将 ML 模块、签名和仿函数转换为等效 C# 编码的详细描述。我希望有人觉得它有用。

于 2009-10-02T17:51:55.577 回答
2

布赖恩的评论很到位。下面是 OCaml 代码,它使用函子来给出 Haskell 的(严格)实现,该实现sequence :: (Monad m) => [m a] -> m [a]在所讨论的 monad 上参数化:

module type Monad = 
sig
  type 'a t (*'*)
  val map : ('a -> 'b) -> ('a t -> 'b t)
  val return : 'a -> 'a t
  val bind : 'a t -> ('a -> 'b t) -> 'b t
end

module type MonadUtils =
sig
  type 'a t (*'*)
  val sequence : ('a t) list -> ('a list) t
end

module MakeMonad (M : Monad) : MonadUtils =
struct
  type 'a t = 'a M.t
  let rec sequence = function
    | [] -> 
        M.return []
    | x :: xs ->
        let f x = 
          M.map (fun xs -> x :: xs) (sequence xs)
        in 
          M.bind x f
end

这看起来很难用 .NET 表达。

更新

使用我的一种技术,naasking我能够以sequence一种主要类型安全的方式(使用向下转换)在 F# 中对可重用函数进行编码。

http://gist.github.com/192353

于 2009-09-15T21:47:26.357 回答