prevented
The first thing a consumer sees is that this type is not displayed in the Haddock documentation. In the documentation for f and g type T will not be hyperlinks, like an exported type. This may prevent the casual reader from discovering instances of class T
More importantly, the consumer cannot do anything with T at the type level. Anything that requires a type spelling will be impossible. For example, a consumer cannot write new instances of a class involving T or include T in a type family. (I don't think there is a way around this ...)
However, at the value level, the main limitation is that the user cannot write type annotations, including T :
> :t (f . read) :: Read b => String -> IO (AT b) <interactive>:1:39: Not in scope: type constructor or class `AT'
Not tagged
The restriction on type signatures is not as significant a limitation as it seems. The compiler can still output this type:
> :tf . read f . read :: Read b => String -> IO (AT b)
Any expression of an expression inside an output Haskell subset can be expressed regardless of the availability of a constructor of type T If, like me, you are addicted to ScopedTypeVariables and extensive annotations, you might be a little surprised at the unT' definition below.
In addition, since typeclass instances have global scope, the consumer can use any available class functions without additional restrictions. Depending on the respective classes, this can lead to significant manipulation of values of an unexposed type. With classes such as Functor , the consumer is also free to manipulate type parameters, since there is an available function of type T a -> T b .
In the T example, the output of Show , of course, exposes the "internal" Int and gives enough information for the hacker to inject unT :
> :t unT' unT' :: (Show b, Read b) => AT b -> (Int, b) > x <- f "x" > unT' x (-29353, "x")
The mkT' implementation with the Read instance remains as an exercise.
Getting something like Generic will completely blow up any containment idea, but you probably expect it.
prevented?
In Haskell corners where signature types are needed or where asTypeOf -type tricks do not work, I think that not exporting the type constructor can actually prevent the consumer from doing something that can be done using the export list (f, g, T()) .
Recommendations
Export all type constructors that are used in the type of any value you export. Here go and include T() in your export list. Leaving this does nothing but obscure the documentation. If you want to expose a purely abstract immutable type, use newtype with a hidden constructor and class instances.