Как взаимодействуют классы типов и модули? - PullRequest
4 голосов
/ 12 июля 2010

Чтобы лучше понять классы типов (начиная практически с нуля), я попытался смоделировать 2-D фигуры с вычислением площади, например:

module TwoDShapes where

class TwoDShape s where
    area :: s -> Float

data Circle = Circle Float deriving Show
aCircle radius | radius < 0 = error "circle radius must be non-negative"
               | otherwise  = Circle radius
instance TwoDShape Circle where
    area (Circle radius) = pi * radius * radius

data Ellipse = Ellipse Float Float deriving Show
anEllipse axis_a axis_b | axis_a < 0 || axis_b < 0 = error "ellipse axis length must be non-negative"
                        | otherwise                = Ellipse axis_a axis_b
instance TwoDShape Ellipse where         
    area (Ellipse axis_a axis_b) = pi * axis_a * axis_b

И так далее для других видов фигуры.

Это нормально, но мне пришло в голову попробовать это:

module TwoDShapes  where

class TwoDShape s where
    area :: s -> Float

data TwoDShapeParams = TwoDShapeParams Float Float Float deriving Show

instance TwoDShape TwoDShapeParams where
    area (TwoDShapeParams length_a length_b constant) = foldl (*) 1 [length_a, length_b, constant]

aCircle radius | radius < 0 = error "circle radius must be non-negative"
               | otherwise  = TwoDShapeParams radius radius pi

anEllipse axis_a axis_b | axis_a < 0 || axis_b < 0 = error "ellipse axis length must be non-negative"
                        | otherwise                = TwoDShapeParams axis_a axis_b pi

и т.д.. что тоже хорошо. С целью сокрытия информации я изменяю объявление модуля, чтобы оно выглядело так:

module TwoDShapes (TwoDShape, area, aCircle, anEllipse, aRectangle, aTriangle)

и, к моему удивлению, это 1) работает и 2) в ghci aCircle оценивается как TwoDShapeParams 1.0 1.0 3.1415927, что верно, но я не понимаю, как тип TwoDShapeParams виден за пределами модуля. Я не уверен, что я ожидал, но не это.

Что мне действительно нравится, так это чтобы класс типов, его метод и «умные конструкторы» были видны за пределами модуля и ничего больше. Можно ли это сделать?

Ответы [ 2 ]

6 голосов
/ 12 июля 2010

Хотя представление TwoDShapes скрыто, вы получили для него экземпляр Show, который позволяет преобразовать произвольное значение типа TwoDShapes в String, так что это источник утечка информации. Действительно абстрактный тип не должен определять экземпляр Show или экземпляр Data, который аналогичным образом предоставляет информацию о представлении. Хорошо иметь способ преобразовать ваш тип в String, если String не зависит от представления (хорошие примеры этого см. В Show экземплярах Data.Map.Map и Data.Array.Array).

Обратите внимание, что система модулей выполняет свою работу: вы все еще не можете ссылаться на конструктор TwoDShapes вне модуля, который его определяет.

1 голос
/ 12 июля 2010

Если вы видите *TwoDShapes в приглашении ghci, он может получить доступ ко всему в модуле:

http://www.haskell.org/ghc/docs/6.12.1/html/users_guide/interactive-evaluation.html

Новое приглашение * Main, которое указывает, что мы печатаем выражения в контексте верхнего уровня модуля Main. Все, что находится в области видимости на верхнем уровне в модуле Main, который мы только что загрузили, также находится в области действия в приглашении (возможно, включая Prelude, если Main явно не скрывает это).

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

Попробуйте загрузить TwoDShapes без * и проверьте тип aCircle.

...