18

MLファンクターは、.NETインターフェースとジェネリックスで実際に表現できますか?そのようなエンコーディングに逆らう高度なMLファンクターの使用例はありますか?

回答の概要

一般的な場合、答えはNOです。MLモジュールは、.NETの概念に直接マッピングされない機能(署名による仕様共有[ 1 ]など)を提供します。

ただし、特定のユースケースでは、MLイディオムを翻訳できます。これらのケースには、基本的なSetファンクター[ 2 ]だけでなく、モナドのファンクトリアルエンコーディング[ 3 ]や、最終的にタグレスインタープリター[ 4、5 ]などのHaskellのさらに高度な使用法も含まれます

実用的なエンコーディングには、セミセーフダウンキャストなどの妥協が必要です。あなたのマイレージは警戒します。

ブログとコード:

  1. blog.matthewdoig.com
  2. highlogics.blogspot.com
  3. F#のモナドファンクター
4

5 に答える 5

12

HigherLogicsは私のブログであり、私はこの質問の調査に多くの時間を費やしてきました。制限は確かに型構築子の抽象化、別名「ジェネリックスのジェネリックス」です。MLモジュールを模倣するためにできる最善のことのようであり、ファンクターには少なくとも1つの(セミセーフ)キャストが必要です。

基本的には、抽象型と、その型で動作するモジュール署名に対応するインターフェースを定義することになります。抽象型とインターフェースは、私が「ブランド」と呼ぶ型パラメーター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ブランドを指定する場合です。この問題を回避するより安全なエンコーディングもありますが、通常のプログラミングには扱いにくいです。

後でこのエンコーディングを使用し、MetaOCamlで記述されたOlegの論文の​​1つから例を翻訳して、C#とLinqを使用しました。インタプリタは、ASP.NETのサーバー側またはJavaScriptとしてクライアント側でこの埋め込み言語を使用して記述されたプログラムを透過的に実行できます。

インタプリタに対するこの抽象化は、Olegの最終的なタグレスエンコーディングの機能です。彼の論文へのリンクはブログ投稿にあります。

インターフェイスは.NETではファーストクラスであり、インターフェイスを使用してモジュール署名をエンコードするため、モジュールとモジュール署名もこのエンコードではファーストクラスです。したがって、ファンクターは、モジュール署名の代わりにインターフェースを直接使用します。彼らはISymantics<B>のインスタンスを受け入れ、それへの呼び出しを委任します。

于 2009-09-23T14:08:23.720 に答える
8

私はあなたの質問に本当に答えるのに十分なMLファンクターをよく知りません。しかし、私がモナディックプログラミングで常に見つける.Netの1つの制限要因は、「すべてのM. M <T>を使用した型式」の意味で「M」を抽象化できないことです(たとえば、Mは型です。コンストラクター(1つ以上の一般的な引数を取る型))。したがって、それがファンクターで時々必要/使用されるものである場合、.Netでそれを表現する良い方法はないと私はかなり確信しています。

于 2009-09-15T17:11:50.687 に答える
7

MLモジュールの重要な機能の1つは、仕様の共有です。.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

ブライアンのコメントは的確です。これは、ファンクターを使用してsequence :: (Monad m) => [m a] -> m [a]、問題のモナドに対してパラメーター化されたHaskellの(厳密な)実装を提供するOCamlコードです。

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すると、再利用可能な関数をF#でほぼタイプセーフな方法でエンコードできましたsequence(ダウンキャストを使用)。

http://gist.github.com/192353

于 2009-09-15T21:47:26.357 に答える