There is no way to have constructors visible in patterns but not expressions. For an abstract data type, the constructors can only be referenced inside the module (or abstype) that implements it. So this is just a question of finding a suitable interface to the module. For your example, it could provide
val getInt : Number -> Integer
val isPrime : Number -> bool
In the case when the most suitable interface is precisely the internal datatype representation, then you could expose the internal datatype declaration via another datatype name that is not abstract and provide a function to convert to that. Here's a modified version of your example but using odd/even rather than prime/composite:
signature TEST_1 =
sig
type t
val mkT : int -> t
val succ : t -> t
datatype dest =
Even of int
| Odd of int
val destT : t -> dest
end
structure Test1 :> TEST_1 =
struct
datatype t =
Even of int
| Odd of int
fun mkT n = if n mod 2 = 0 then Even n else Odd n
val succ = fn Even n => Odd (n + 1) | Odd n => Even (n + 1)
datatype dest = datatype t
fun destT x = x
end
datatype dest is the external view of the abstract datatype t. Note that there is no actual duplication internally: see last two lines of the structure. So you can then do
open Test1;
case destT (mkT 2) of
Even _ => "even"
| Odd _ => "odd";
Although it is possible to write
Even 3;
this has type dest, not t, so you cannot do
succ (Even 3);
Personally, I would not encourage a module's interface to be strongly oriented towards the internal representation of its abstract type(s) but more oriented towards the required operations. This makes it easier to change the internal representation/implementation in future.
Phil