Переопределение экземпляра класса новыми членами - PullRequest
3 голосов
/ 15 декабря 2011

Скажем, у меня есть простой класс AClass с открытым членом f1, который можно переопределить. Как определить новый экземпляр AClass с другим членом f2, если не считать дубликата исходного кода AClass? Код игрушки ниже:

class AClass a where
    f1 :: a -> Int

data Val = I Int

instance AClass Val where
  f1 x = 0

  -- the method below can't be added as it is not public member of AClass
  -- f2:: a -> Float
  -- f2 x = 0.0 

Я огляделся, но не нашел четких примеров того, как это сделать (то есть, примеры, которые я мог бы понять - ясность относительна). Каковы возможные пути? Закрытие, объявление нового типа или что-то еще? Будет полезно продемонстрировать эту технику с помощью вышеприведенного игрушечного кода - вы можете изменить объявление data и т. Д. (Например, заменить его на newtype оболочку вокруг Int), но единственное, что остается неизменным в приведенном выше коде - это объявление класса AClass. Это потому, что предположение о том, что класс уже был написан разработчиком библиотеки, и поэтому я не могу его коснуться. Конечным результатом должен быть другой игрушечный код, который наследует вкусности AClass и добавляет f2 member.

Будут, конечно, предостережения в переопределяющих классах, подобных этому. Но это помогает узнать, что возможно и как.

- Обновление -

Рабочий код ниже - Благодарим Бена и mergeconflict за то, что он придумал решение - было несколько пропущенных фрагментов - заполнено ниже:

class AClass a where
    f1 :: a -> Int

class (AClass a) => BClass a where
    f2 :: a -> Float

data Val = I Int

instance AClass Val where
   f1 _ = 0

instance BClass Val where
   f2 _ = 0.0                       

Ответы [ 3 ]

9 голосов
/ 15 декабря 2011

Чего вы пытаетесь достичь?

У вас есть тип Val, который вы создаете экземпляр AClass.Вы можете определить любое количество функций, которые используют Val, которые не имеют ничего общего с классом.Просто прекратите пытаться определить их в объявлении instance.

Если вы ожидаете, что сможете иметь один конкретный экземпляр AClass, который имеет дополнительную функцию f2, которую вы затемиспользовать в функциях, которые используют AClass экземпляры, и иметь возможность вызывать f2 ... это абсурд.По определению, единственные вещи, которые, как известно, являются общими для всех AClass экземпляров, - это вещи, объявленные в AClass.Если все, что вы знаете о некотором значении, это то, что он является членом типа, который является экземпляром AClass, вы не можете ничего с ним сделать, что вы не можете сделать со всеми экземплярами AClass.Вы не можете вызывать ничего особенного для определенных экземпляров.

Если вы хотите создать новый класс, который поддерживает все операции, которые AClass выполняет, а также f2и пусть Val будет экземпляром этого нового класса ... тогда просто сделайте это.

class AClass a => AnotherClass a where
    f2 :: a -> Float

instance AnotherClass Val where
    f2 x = 0.0
5 голосов
/ 16 декабря 2011

Ваш вопрос не имеет смысла в Haskell:

Скажем, у меня есть простой класс AClass с открытым членом f1, который можно переопределить.

Если вы думаете о «классах» с «открытыми членами», которые можно «переопределить», вы думаете об объектно-ориентированных терминах.Код, который вы показали, не представляет эти понятия вообще.См. Сообщение Тони Морриса «Классы типов не имеют ничего общего с интерфейсами».

класс типов определяет концепцию С ++ смысл , если это вообще помогает).Эта концепция состоит из некоторого набора функций, например:

class Eq a where 
  (==) :: a -> a -> Bool

... но без фактического поведения или реализации этих функций.Здесь нечего «переопределить».Типы данных, которые моделируют эта концепция будет предоставлять объявления экземпляров, например:

data Integer = {- ... -}
instance Eq Integer where 
  x == y =  x `integerEq` y

data Float = {- ... -}
instance Eq Float where
  x == y =  x `floatEq` y

Таким образом, вы можете реализовать полиморфные алгоритмы, такие как:

allEqual :: Eq a => a -> a -> a -> Bool
allEqual a b c = (a == b) && (b == c)

Теперь вернемся кВаш вопрос, вы также можете определить классы типов, которые моделируют более конкретную концепцию , чем некоторые ранее определенные классы типов.Например:

class (Eq a) => Num a where
  (+), (-), (*) :: a -> a -> a

Итак, есть экземпляры Eq, которые не являются экземплярами Num, но все экземпляры Num должны быть экземплярами Eq.В вашем примере вы можете захотеть что-то вроде этого:

class AClass a where
  f1 :: a -> Int

class (AClass b) => BClass b where
  f2 :: a -> Float

data Val = {- whatever -}
instance BClass Val where
  f1 _ = 0
  f2 _ = 0.0

Опять же, Val не "наследует вкусности" как таковой, он просто говорит, что это экземпляр BClass и, следовательно, также экземплярAClass.Но это, очевидно, игрушечный код ...

4 голосов
/ 15 декабря 2011

Следует помнить, что Haskell не является объектно-ориентированным, и классы типов Haskell не очень похожи на классы в объектно-ориентированном смысле.

Вы можете просто определить функцию f2 _ = 0.0. Он не будет членом класса типов AClass, если вы не добавите его в определение - но зачем вам это нужно?

...