[erlang-questions] Emulating phantom types maybe

Michael Truog <>
Fri Aug 26 09:51:19 CEST 2016


On 08/25/2016 10:57 PM, Daniel Abrahamsson wrote:
> 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
>
>
> _______________________________________________
> erlang-questions mailing list
> 
> http://erlang.org/mailman/listinfo/erlang-questions
This approach works in a closed-system that only relies on the opaque types.  If the opaque types are not always used as opaque types, i.e., in a way that depends on it being a list of integers, then that will just generate dialyzer errors.  Also, the fact remains that it does not actually check the contents of the list, so this approach depends on the developer always using the opaque types in the proper way (i.e., it isn't as if development errors with the opaque types will be easily caught based on the contents of the list of integers).  Since software is normally not developed in complete isolation, requiring complete isolation for a solution is not helpful for any system that might want to grow in the future.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://erlang.org/pipermail/erlang-questions/attachments/20160826/9a927aee/attachment.html>


More information about the erlang-questions mailing list