В C# я могу объявить следующее
class A {
int Field;
}
class B : A {
int Field2;
}
static int f(A a) { return a.Field; }
static int f(B b) { return a.Field + b.Field2; }
static void Main(string[] args) {
A a = new A() { Field = 1 };
A b = new B() { Field = 1, Field = 2};
Console.WriteLine(f(a) + f(b));
}
В Haskell я вывел бы вышеупомянутое как
data A = A { field :: Int } | B { field :: Int, field2 :: Int }
f :: A -> Int
f (A a) = a
f (B a b) = a + b
main :: IO()
main = do putStrLn $ show (f(a) + f(b))
where a = A 1
b = B 1 2
То, что мне не нравится приблизительно дубликат Haskell, - то, что я должен повториться field
дважды в определении данных A
(это становится более утомительным как количество полевых увеличений, которые присутствуют в A
это должно быть в B
). Есть ли более краткий способ в Haskell записать B
как подкласс A
(который несколько подобен пути C#)?
Если A имеет много полей, один вариант, который может иметь смысл, состоит в том, чтобы иметь два разных типа данных для A и B, где B содержит A, а затем использовать класс типов для определения f . Вот так:
data A = A {field1 :: Int, field2 :: Int, ..., field9999 :: Int}
data B = B {a :: A, field10000 :: Int}
class ABC a where
f :: a -> Int
instance ABC A where
f a = field1 a + field101 a
instance ABC B where
f b = f (a b) + field10000 b
Определение ваших данных является боковым ходом, конструкторы A и B являются экземплярами типа A, где вы ищете тип B, который является типом A, а не просто экземпляром типа A.
Попробуйте:
data Foo = Foo {field :: Type}
newtype Bar = Bar Foo
fromBar Bar x = x
toBar x = Bar x
Это позволит вам объявлять всевозможные специальные функции для типа Bar, которые не будут применяться к Foo, а также работать с Bar, используя функции, разработанные для типа Foo.
Если вы хотите расширить параметры данных Foo дополнительной информацией в Bar, вы можете использовать объявление данных и отношение has-a. Это не так желательно, как newtype, поскольку newtype вычисляется лениво там, где объявление данных строго. Тем не менее:
data Foo = Foo {field :: Type}
data Bar = Bar {fooField :: Foo, moreField :: Type}
Как указывает sepp2k, вы действительно хотите определить «природу» типа не по тому, как он выглядит, а по тому, как он работает . Мы делаем это в Haskell, определяя класс типа и имея экземпляр типа для этого класса.
Надеюсь, это поможет.