Переменные типа привязки ошибок в экземпляре класса типов - PullRequest
1 голос
/ 29 февраля 2012

У меня есть класс "Shape", который должен иметь "area", определенную во всех экземплярах.area возвращает «Area b» (тип данных), который содержит число (b принадлежит классу типов Num), обозначающее область этой Shape.

Haskell имеет проблему с привязкой b к (x * y), где x иу имеют тип «а» и «а» также имеет тип класса Num.Как мне решить это ??[Если я заменю (x * y) на 0, это работает, но не работает даже с (0 :: Int)]

Код:

data Unit = Unit | Meter | CentiMeter               deriving Show

data Area a = Area a Unit                           deriving Show

class Shape a where
      area :: (Num b) => a -> Area b

data Rectangle side = Rectangle side side Unit  deriving Show

instance (Num a) => Shape (Rectangle a) where
     area (Rectangle x y unit) = Area (x*y) unit

Ошибка:

[1 of 1] Compiling Main             ( y.hs, interpreted )

y.hs:11:46:
    Could not deduce (a ~ b)
    from the context (Num a)
      bound by the instance declaration at y.hs:10:10-39
    or from (Num b)
      bound by the type signature for
                 area :: Num b => Rectangle a -> Area b
      at y.hs:11:10-52
      `a' is a rigid type variable bound by
          the instance declaration at y.hs:10:15
      `b' is a rigid type variable bound by
          the type signature for area :: Num b => Rectangle a -> Area b
          at y.hs:11:10
    In the second argument of `(*)', namely `y'
    In the first argument of `Area', namely `(x * y)'
    In the expression: Area (x * y) unit
Failed, modules loaded: none.

Ответы [ 2 ]

5 голосов
/ 29 февраля 2012

Проблема в том, что подпись типа area:

area :: (Num b) => a -> Area b

То, что он говорит, это «дай мне a, и я дам тебе Area b для любого b, который ты хочешь; ты можешь выбрать». Так, например, я мог бы дать area Integer и ожидать обратно Area Double. Очевидно, это не то, что вы хотите!

В этом случае ошибка возникает из-за того, что вы используете x*y, который имеет тип a , когда ожидается b - вы должны указать значение, которое работает для любой числовой тип b , но вы даете значение, которое работает только для одного ( a ).

Если вы изменили тип area на a -> Area Integer или что-то подобное, то это сработало бы. Однако у меня есть ощущение, что вы хотите, чтобы instance мог указывать тип области. Для этого вам нужно использовать языковое расширение под названием семейства типов :

{-# LANGUAGE TypeFamilies #-}

class (Num (AreaComponent a)) => Shape a where
    type AreaComponent a
    area :: a -> Area (AreaComponent a)

instance (Num a) => Shape (Rectangle a) where
    type AreaComponent (Rectangle a) = a
    area (Rectangle x y unit) = Area (x*y) unit

Это говорит о том, что для каждого типа a , который является экземпляром Shape, существует связанный тип AreaComponent a, представляющий тип каждого компонента его области. Этот тип должен быть экземпляром Num по определению Shape.

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

class Shape sh where
    area :: (Num a) => sh a -> Area a

instance Shape Rectangle where
    area (Rectangle x y unit) = Area (x*y) unit
3 голосов
/ 29 февраля 2012

Проблема в том, что

class Shape a where
      area :: (Num b) => a -> Area b

обещает быть в состоянии доставить любой тип Num, который может потребовать вызывающая сторона, но ваша реализация просто предоставляет некоторый тип Num, который предоставляет вызывающий.

Единственный способ получить числа любого желаемого типа - это использовать fromInteger (или литералы, если fromInteger подразумевается).

Чтобы иметь возможность предоставлять некоторый тип Num, определяемый типом объекта, область которого должна быть вычислена, вы можете использовать классы многопараметрических типов и функциональные зависимости или семейства типов, например

{-# LANGUAGE TypeFamilies #-}

class Shape a where
    type NumType a :: *
    area :: a -> Area (NumType a)

instance (Num side) => Shape (Rectangle side) where
    type NumType (Rectangle side) = side
    area (Rectangle w h u) = Area (w*h) u
...