Я пытаюсь сравнить классы типов Haskell и интерфейсы C #. Предположим, что есть Functor
.
Haskell:
class Functor f where
fmap :: (a -> b) -> f a -> f b
Как реализовать этот тип класса в качестве интерфейса в C #?
Что я пробовал:
interface Functor<A, B>
{
F<B> fmap(Func<A, B> f, F<A> x);
}
Это неверная реализация, и я на самом деле застрял с универсальным F
типом, который должен быть возвращен fmap
. Как это должно быть определено и где?
Это невозможно реализовать Functor
в C # и почему? Или, может быть, есть другой подход?
Ответы:
В системе типов C # отсутствует пара функций, необходимых для правильной реализации классов типов в качестве интерфейса.
Давайте начнем с вашего примера, но ключ показывает более полное описание того, что такое класс типов и что делает, а затем пытается сопоставить их с битами C #.
Это определение класса типа или аналог интерфейса. Теперь давайте посмотрим на определение типа и его реализацию класса этого типа.
Теперь мы можем ясно видеть один особый факт классов типов, которые вы не можете иметь с интерфейсами. Реализация класса типа не является частью определения типа. В C # для реализации интерфейса вы должны реализовать его как часть определения типа, который его реализует. Это означает, что вы не можете реализовать интерфейс для типа, который вы не реализуете самостоятельно, однако в Haskell вы можете реализовать класс типа для любого типа, к которому у вас есть доступ.
Это, вероятно, самое большое сразу, но есть еще одно довольно существенное отличие, которое делает эквивалент C # действительно не слишком эффективным, и вы затрагиваете его в своем вопросе. Это о полиморфизме. Также есть некоторые относительно общие вещи, которые Haskell позволяет вам делать с классами типов, которые прямо не переводятся, особенно когда вы начинаете смотреть на количество обобщений в экзистенциальных типах или других расширениях GHC, таких как Generic ADT.
Видите ли, с Haskell вы можете определить функторы
Тогда в потреблении вы можете иметь функцию:
В этом и заключается проблема. В C # как вы пишете эту функцию?
Таким образом , есть пара вещей , которые не так с C # версии, с одной стороны , я даже не уверен , что это позволит использовать
<b>
спецификатор , как я сделал, но без него я буду уверен , что это не будет рассылатьShow<>
надлежащим образом ( не стесняйтесь попробовать и собрать, чтобы узнать; я не сделал).Однако, здесь большая проблема заключается в том, что в отличие от вышеописанного в Haskell, где мы
Terminal
определили наши s как часть типа и затем использовали их вместо типа, из-за отсутствия в C # соответствующего параметрического полиморфизма (который становится супер очевидным, как только вы пытаетесь взаимодействовать F # с C #) вы не можете четко или четко различать, являются ли Right или LeftTerminal
s. Лучшее, что вы можете сделать, это использоватьnull
, но что, если вы пытаетесь создать тип значения aFunctor
или в случае,Either
когда вы различаете два типа, оба из которых имеют значение? Теперь вы должны использовать один тип и иметь два разных значения для проверки и переключения между ними для моделирования вашей дискриминации?Отсутствие правильных типов сумм, типов объединений, ADT, как бы вы их ни называли, на самом деле делает многое из того, что дают вам классы типов, потому что в конце дня они позволяют вам рассматривать несколько типов (конструкторов) как один тип, и базовая система типов .NET просто не имеет такой концепции.
источник
Вам нужны два класса: один для моделирования обобщенного функтора более высокого порядка (функтор), а второй - для свободного комбинированного значения A
Так что, если мы используем монаду Option (потому что все монады являются функторами)
Затем вы можете использовать методы статического расширения для преобразования из IF <Option, B> в Some <A>, когда вам нужно
источник
pure
интерфейсом универсального функтора: компилятор жалуется наIF<Functor, A> pure<A>(A a);
«ТипFunctor
не может использоваться в качестве параметраFunctor
типа в методе универсального типаIF<Functor, A>
. Нет преобразования в бокс или преобразования параметра типа изFunctor
вF<Functor>
». Что это значит? И почему мы должны определитьpure
в двух местах? Кроме того, не должноpure
быть статичным?