Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How does Haskell hide data constructor?

I explored System.Random.StdGen and saw this code in the source.

data StdGen = StdGen Int32 Int32

It seems the module export StdGen too.

module System.Random (

    RandomGen(next, split, genRange)

    , StdGen

    ...

However, why can't I do this in my code, say like,

Prelude System.Random> StdGen 1 2

Not in scope: data constructor `System.Random.StdGen'**

In the other hand, I can do this,

module F (Foo) where

    import GHC.Int

    data Foo = Foo GHC.Int.Int32 GHC.Int.Int32 deriving (Show)

and

Prelude> Foo 1 2

Foo 1 2

Would someone please kindly tell me how actually this data constructor is hidden?

like image 327
Znatz Avatar asked Mar 07 '13 14:03

Znatz


People also ask

What is data constructor in Haskell?

Data constructors are first class values in Haskell and actually have a type. For instance, the type of the Left constructor of the Either data type is: Left :: a -> Either a b. As first class values, they may be passed to functions, held in a list, be data elements of other algebraic data types and so forth.

Is maybe a data constructor in Haskell?

Maybe is a type constructor because it is used to construct new types (the result type depends on the type of a in Maybe a ), where such a type might be Maybe Int (notice, there's no type param a anymore, i.e. all type parameters are bound).

Is Io a data constructor in Haskell?

IO is a type constructor, not a value constructor. IO True would be a type, not a value (if True was a type).


2 Answers

There are two things to understand here. Export syntax and a difference in GHCi behaviour between compiled and interpreted values.

Export syntax

Exporting from a module using this syntax

module System.Random (
    -- ...
    , StdGen
    -- ...

tells GHC only to export the datatype, not the constructor (even if both have the same name). The constructor can be listed explicitly within parentheses after the datatype name if you want to export it, like this:

    StdGen(StdGen)

Or you can export a datatype with all its constructors like this:

    StdGen(..)

GHCi behaviour

In addition, GHCi, when loading an interpreted module, always allows you to see all the entities visisble at the top-level of the module, even if they're hidden by the export list. This is to facilitate development and debugging, and is the reason why your Foo is visible.

This mode where "everything" is visible is reflected by putting a * in front of the module name at the GHCi prompt. If there's a *, everything is visisble, and if not, then the exported entities are visible.

When using the :m command to add or remove modules from scope, you can select whether you want to add modules in *-form or not.

But for compiled modules (and a library module like System.Random usually is compiled), the *-form is not available, so for these you'll always be in the situation that the export list is respected.

See the documentation for a full description of the scoping behaviour of GHCi.

like image 77
kosmikus Avatar answered Sep 28 '22 04:09

kosmikus


If you look at the sources, you'll see something along the lines of:

module System.Random
    (
    -- stuff...
    , StdGen
    -- even more stuff...
    )

This syntax means that only the type is exported, not its constructor(s). If you want to export the constructor too, you'd do:

module System.Random
    ( StdGen(..)
    -- ...
    )
like image 34
Niklas B. Avatar answered Sep 28 '22 02:09

Niklas B.