Question

OK, so here's an obscure corner of the language:

Haskell allows you to export an identifier who's type signature mentions a type that is not exported. What exactly is the semantics of this?

For example, suppose I have

module Foobar (bar) where

data Foo = ...

bar :: String -> Foo

Foo is not exported, while bar is. The type of bar mentions Foo, though. Many programming languages wouldn't let you do this, but Haskell does.

So what now? It seems that I can call bar, but then can't do much with its result. I particular, I (presumably) cannot utter the name of the result type, which is kinda weird. Presumably if the module exported some functions that take Foo as input, I ought to be able to call those with my result as input... yet all the whole I can't say so in a type signature.

For sure, doing something like the above is not a good idea; I'm not proposing to do this in actual code. I'm just curious as to what it actually does.

Was it helpful?

Solution

It might be more interesting if Haskell were to forbid you from exporting types of your own which infer Foos---this would make the situation here behave a bit more like existential typing which is A Good Idea for module systems.

Instead, the type information leaks. So does the instance information. For "instance" the following is unsafe

module Foo ( foo ) where

  data Foo ...
    deriving (Data, Typeable) -- for internal use

  foo :: Foo -> IO ()

since using foo will allow an "evil" user to unify Data.Data.fromConstr ... with Foo even if users are not supposed to be able to generate Foo values.

-- mkFoo :: Constr -> Foo       (except I have to let this be inferred)
mkFoo c = out where
  out     = fromConstr c
  ignored = foo out

Ultimately, I'd consider this to be a poor API. If you want to emphasize the use of a type without allowing the user to construct it, export the type.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top