В системе типов C # отсутствует пара функций, необходимых для правильной реализации классов типов в качестве интерфейса.
Давайте начнем с вашего примера, но ключ показывает более полное описание того, что такое класс типов и что делает, а затем пытается сопоставить их с битами C #.
class Functor f where
fmap :: (a -> b) -> f a -> f b
Это определение класса типа или аналог интерфейса. Теперь давайте посмотрим на определение типа и его реализацию класса этого типа.
data Awesome a = Awesome a a
instance Functor Awesome where
fmap f (Awesome a1 a2) = Awesome (f a1) (f a2)
Теперь мы можем ясно видеть один особый факт классов типов, которые вы не можете иметь с интерфейсами. Реализация класса типа не является частью определения типа. В C # для реализации интерфейса вы должны реализовать его как часть определения типа, который его реализует. Это означает, что вы не можете реализовать интерфейс для типа, который вы не реализуете самостоятельно, однако в Haskell вы можете реализовать класс типа для любого типа, к которому у вас есть доступ.
Это, вероятно, самое большое сразу, но есть еще одно довольно существенное отличие, которое делает эквивалент C # действительно не слишком эффективным, и вы затрагиваете его в своем вопросе. Это о полиморфизме. Также есть некоторые относительно общие вещи, которые Haskell позволяет вам делать с классами типов, которые прямо не переводятся, особенно когда вы начинаете смотреть на количество обобщений в экзистенциальных типах или других расширениях GHC, таких как Generic ADT.
Видите ли, с Haskell вы можете определить функторы
data List a = List a (List a) | Terminal
data Tree a = Tree val (Tree a) (Tree a) | Terminal
instance Functor List where
fmap :: (a -> b) -> List a -> List b
fmap f (List a Terminal) = List (f a) Terminal
fmap f (List a rest) = List (f a) (fmap f rest)
instance Functor Tree where
fmap :: (a -> b) -> Tree a -> Tree b
fmap f (Tree val Terminal Terminal) = Tree (f val) Terminal Terminal
fmap f (Tree val Terminal right) = Tree (f val) Terminal (fmap f right)
fmap f (Tree val left Terminal) = Tree (f val) (fmap f left) Terminal
fmap f (Tree val left right) = Tree (f val) (fmap f left) (fmap f right)
Тогда в потреблении вы можете иметь функцию:
mapsSomething :: Functor f, Show a => f a -> f String
mapsSomething rar = fmap show rar
В этом и заключается проблема. В C # как вы пишете эту функцию?
public Tree<a> : Functor<a>
{
public a Val { get; set; }
public Tree<a> Left { get; set; }
public Tree<a> Right { get; set; }
public Functor<b> fmap<b>(Func<a,b> f)
{
return new Tree<b>
{
Val = f(val),
Left = Left.fmap(f);
Right = Right.fmap(f);
};
}
}
public string Show<a>(Showwable<a> ror)
{
return ror.Show();
}
public Functor<String> mapsSomething<a,b>(Functor<a> rar) where a : Showwable<b>
{
return rar.fmap(Show<b>);
}
Таким образом , есть пара вещей , которые не так с C # версии, с одной стороны , я даже не уверен , что это позволит использовать <b>
спецификатор , как я сделал, но без него я буду уверен , что это не будет рассылать Show<>
надлежащим образом ( не стесняйтесь попробовать и собрать, чтобы узнать; я не сделал).
Однако, здесь большая проблема заключается в том, что в отличие от вышеописанного в Haskell, где мы Terminal
определили наши s как часть типа и затем использовали их вместо типа, из-за отсутствия в C # соответствующего параметрического полиморфизма (который становится супер очевидным, как только вы пытаетесь взаимодействовать F # с C #) вы не можете четко или четко различать, являются ли Right или Left Terminal
s. Лучшее, что вы можете сделать, это использовать null
, но что, если вы пытаетесь создать тип значения a Functor
или в случае, Either
когда вы различаете два типа, оба из которых имеют значение? Теперь вы должны использовать один тип и иметь два разных значения для проверки и переключения между ними для моделирования вашей дискриминации?
Отсутствие правильных типов сумм, типов объединений, ADT, как бы вы их ни называли, на самом деле делает многое из того, что дают вам классы типов, потому что в конце дня они позволяют вам рассматривать несколько типов (конструкторов) как один тип, и базовая система типов .NET просто не имеет такой концепции.