How do types and modules interact?

To grasp the best styles (starting almost from scratch), I went on to model two-dimensional figures with area calculations, for example:

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 

And so on for other kinds of shapes.

This is great, but it occurred to me to try the following:

 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 

etc .. which is also great. In order to hide the information, I modify the module declaration to look like this:

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

and, to my surprise, this 1) works and 2) in ghci aCircle is rated as TwoDShapeParams 1.0 1.0 3.1415927 , which is true, but I do not understand how the view of TwoDShapeParams is visible outside the module. I'm not sure what I expected, but not that.

I would really like the typeclass class, its method and smart constructors to be visible outside the module and nothing more. It can be done?

+4
source share
2 answers

Although the TwoDShapes hidden, you got a Show instance for it that allows you to convert an arbitrary value of type TwoDShapes to String , so this is a source of information leakage. A truly abstract type should not define an instance of Show or, indeed, a Data , which likewise provides presentation information. It's good that you have a way to convert your type to String if the String is view-independent (see the Examples of Show Data.Map.Map and Data.Array.Array for good examples of this).

Note that the module system does its job: you still cannot reference the TwoDShapes constructor outside the module that defines it.

+6
source

If you see *TwoDShapes at the ghci prompt, it can access everything in the module:

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

The new prompt is * Main, which indicates that we are printing expressions in the context of the top level of the main module. Everything that is at the top level in the Main module that we just loaded is also in scope at the prompt (possibly including Prelude, while Main doesn't hide it explicitly).

The syntax module * indicates that this is the full volume of the top-level module that contributes to the area of ​​expressions entered on the command line. Without * only export of the module is visible.

Try downloading TwoDShapes without * and check what aCircle .

+1
source

Source: https://habr.com/ru/post/1315392/


All Articles