Наследование стиля ООП в Haskell

В 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#)?

5
задан GEL 15 July 2010 в 18:45
поделиться

2 ответа

Если 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
6
ответ дан 14 December 2019 в 01:00
поделиться

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

Надеюсь, это поможет.

3
ответ дан 14 December 2019 в 01:00
поделиться
Другие вопросы по тегам:

Похожие вопросы: