[erlang-questions] Emulating phantom types maybe

Daniel Abrahamsson <>
Fri Aug 26 07:57:36 CEST 2016


You can achieve something close to what you are after by using opaque
types. Partly re-using your original example:

-module(string_with_case).

-opaque lowercase() :: string().
-opaque uppercase() :: string().
-type anycase() :: lowercase() | uppercase() | string().

-export_type([ lowercase/0
             , uppercase/0
             ]).

-export([ uppercase/1
        , lowercase/1
        , need_lower/1
        , concat/2
        ]).

-spec lowercase(anycase()) -> lowercase().
lowercase(Str) -> string:to_lower(Str).

-spec uppercase(anycase()) -> uppercase().
uppercase(Str) -> string:to_upper(Str).

-spec need_lower(lowercase()) -> ok.
need_lower(_Lower) -> ok.

-spec concat(anycase(), anycase()) -> string().
concat(A, B) -> A ++ B.


Note that these two (maybe in your eyes valid) examples will NOT pass
dialyzer:

string_with_case:need_lower("example"). % FAILS: Breaks opaqueness of
string_with_case:lowercase().

string_with_case:lowercase("test") ++ "mixing". % FAILS: Breaks opaqueness
of string_with_case:lowercase().

In other words, all operations involving types uppercase() or lowercase()
must take place inside the string_with_case module.
If you need to operate on a lowercase() as if it were a string outside the
string_with_case module, you must provide a to_string function:

-spec to_string(anycase()) -> string().
to_string(X) -> X.

This of course means you would have to call the lowercase/1 or uppercase/1
functions again to convert it back, should you need to
use the "variant/sort specific" functions of string_with_case on the
result. This is however necessary to maintain the guarantees you are trying
to achieve.

//Daniel
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://erlang.org/pipermail/erlang-questions/attachments/20160826/fbcef761/attachment.html>


More information about the erlang-questions mailing list