Можно ли полностью кодировать ML-функторы в .NET (C # / F #)? - PullRequest
17 голосов
/ 15 сентября 2009

Можно ли практически выразить функторы ML с помощью .NET-интерфейсов и обобщений? Есть ли пример продвинутого использования функтора ML, который игнорирует такие кодировки?

Сводка ответов :

В общем случае ответ НЕТ. Модули ML предоставляют функции (такие как совместное использование спецификаций через подписи [ 1 ]), которые напрямую не сопоставляются с концепциями .NET.

Однако, для определенных случаев использования идиомы ML могут быть переведены. Эти случаи включают в себя не только базовый функтор Set [ 2 ], но и функторное кодирование монад [ 3 ], а также более расширенное использование Haskell, например, наконец, tagless переводчики [ 4 , 5 ].

Практические кодировки требуют компромиссов, таких как полубезопасные откаты. Ваш пробег будет осторожным.

Блоги и код:

  1. blog.matthewdoig.com
  2. higherlogics.blogspot.com
  3. функтор монад в F #

Ответы [ 5 ]

11 голосов
/ 23 сентября 2009

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. Существует более безопасное кодирование, которое также позволяет избежать этой проблемы, но оно слишком громоздкое для обычного программирования.

Я позже использовал эту кодировку и перевел примеры из одной из статей Олега, написанных на MetaOCaml, для использования C # и Linq. Интерпретатор может прозрачно запускать программы, написанные с использованием этого встроенного языка на стороне сервера в ASP.NET или на стороне клиента, как JavaScript.

Эта абстракция над интерпретаторами является особенностью окончательного кодирования Олега без тегов. Ссылки на его статью приведены в сообщении в блоге.

Интерфейсы являются первоклассными в .NET, и поскольку мы используем интерфейсы для кодирования сигнатур модулей, модули и сигнатуры модулей также являются первоклассными в этой кодировке. Таким образом, функторы просто используют интерфейс непосредственно вместо сигнатур модулей, т.е. они будут принимать экземпляр ISymantics и делегировать ему любые вызовы.

8 голосов
/ 15 сентября 2009

Я не знаю функторов ML достаточно хорошо, чтобы действительно ответить на ваш вопрос. Но я скажу, что один ограничивающий фактор .Net, который я всегда нахожу с монадическим программированием, - это неспособность абстрагироваться над «M» в смысле «для всех. * "(например, где M - конструктор типа (тип, который принимает один или несколько общих аргументов)). Так что, если вам иногда это нужно / нужно использовать с функторами, то я уверен, что нет хорошего способа выразить это в .Net.

6 голосов
/ 19 сентября 2009

Одной из ключевых особенностей модулей ML является совместное использование спецификаций. В .NET нет механизма, который бы мог их подражать - необходимый механизм просто слишком отличается.

Вы можете попытаться сделать это, превратив общие типы в параметры, но это не может точно подражать способности определять подпись, а затем применять к ней общий доступ, возможно, несколькими различными способами.

По моему мнению, .NET выиграет от чего-то такого, что имеет такой механизм - тогда оно приблизится к реальной поддержке разнообразия современных языков. Надеюсь, в том числе более поздние достижения в системах модулей, таких как в MixML, что, на мой взгляд, является будущим модульных систем. http://www.mpi -sws.org / ~ Росберг / mixml /

3 голосов
/ 02 октября 2009

Я сейчас опубликовал подробное описание моего перевода модулей ML, подписей и функторов в эквивалентную кодировку C #. Я надеюсь, что кто-то найдет это полезным.

2 голосов
/ 16 сентября 2009

Комментарий Брайана - точный. Вот код OCaml, который использует функторы, чтобы дать (строгую) реализацию Haskell sequence :: (Monad m) => [m a] -> m [a], параметризованную по рассматриваемой монаде:

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.

UPDATE

Используя технику naasking Я смог закодировать многократно используемую функцию sequence в F # главным образом безопасным для типов способом (использует downcasts).

http://gist.github.com/192353

...