Ответ 1
Подумайте о том, какой должна быть подпись для методов IMonad<T>
. В Haskell класс Monad определяется как
class Monad m where
(>>=) :: m a -> (a -> m b) -> m b
return :: a -> m a
Трудно перевести это прямо на интерфейс С#, потому что вам нужно иметь возможность ссылаться на конкретный подтип реализации ( "m a" или ISpecificMonad<a>
) в определении общего интерфейса IMonad. ОК, вместо того, чтобы пытаться (например) IEnumerable<T>
реализовать IMonad<T>
напрямую, мы попробуем разложить реализацию IMonad на отдельный объект, который может быть передан вместе с конкретным экземпляром типа монады на все, что необходимо рассматривайте его как монаду (это "стиль передачи словаря" ). Это будет IMonad<TMonad>
, а TMonad здесь будет не T в IEnumerable<T>
, а IEnumerable<T>
. Но подождите - это тоже не сработает, потому что подпись Return<T>
, например, должна получить от любого типа T до a TMonad<T>
для любого TMonad<>
. IMonad следует определить как нечто вроде
interface IMonad<TMonad<>> {
TMonad<T> Unit<T>(T x);
TMonad<U> SelectMany<T, U>(TMonad<T> x, Func<T, TMonad<U>> f);
}
используя гипотетическую функцию С#, которая позволила бы нам использовать конструкторы типов (например, TMonad < > ) в качестве параметров типового типа. Но, конечно же, С# не имеет этой функции (более высокий тип полиморфизма). Вы можете reify конструкторы типов во время выполнения (typeof(IEnumerable<>)
), но не можете ссылаться на них в типе сигнатур, не задавая им параметры. Таким образом, помимо -100 пунктов вещь, реализация этого "правильно" потребует не просто добавления другого обычного определения интерфейса, но и глубоких дополнений к системе типов.
Вот почему способность иметь понимание запросов по вашим собственным типам взломана (они просто "магически" работают, если существуют правильные имена меток с правильными сигнатурами) вместо использования механизма интерфейса и т.д.