Skip to content

Commit 50fb72d

Browse files
committed
Introduce the ?TYPE macro
It allows explicitly using PropEr's "native types" feature. This is more powerful than the current automatic parse transform-based inference: * it can be used outside of `?FORALL` to produce PropEr types & combine them with other PropEr or native types. * it allows using the "native types" feature with the proper parse transform disabled * it allows expressing more types directly, without creating additional type aliases, for example: `?TYPE(atom() | float())`. The semantics are identical to automatic inference, if the expression was wrapped in a simple `id` type (when the syntax allows expressing such a type). For example: ```erlang -record(foo, {}). -type id(X) :: X. % semantically equivalent definitions: ?FORALL(X, id(#foo{}), Prop). ?FORALL(X, ?TYPE(#foo{}), Prop). ```
1 parent cfc29e7 commit 50fb72d

4 files changed

Lines changed: 75 additions & 10 deletions

File tree

include/proper_common.hrl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@
6161
proper_types:add_constraint(RawType,fun(X) -> Condition end,true)).
6262
-define(SUCHTHATMAYBE(X,RawType,Condition),
6363
proper_types:add_constraint(RawType,fun(X) -> Condition end,false)).
64+
-define(TYPE(X), proper_types:native_type(?MODULE, ??X)).
6465

6566
%%------------------------------------------------------------------------------
6667
%% Targeted macros

src/proper_typeserver.erl

Lines changed: 29 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -27,16 +27,23 @@
2727
%%%
2828
%%% PropEr can parse types expressed in Erlang's type language and convert them
2929
%%% to its own type format. Such expressions can be used instead of regular type
30-
%%% constructors in the second argument of `?FORALL's. No extra notation is
31-
%%% required; PropEr will detect which calls correspond to native types by
32-
%%% applying a parse transform during compilation. This parse transform is
33-
%%% automatically applied to any module that includes the `proper.hrl' header
34-
%%% file. You can disable this feature by compiling your modules with
35-
%%% `-DPROPER_NO_TRANS'. Note that this will currently also disable the
36-
%%% automatic exporting of properties.
30+
%%% constructors in the second argument of `?FORALL's. Such types can be expressed
31+
%%% in two ways:
32+
%%% <ul>
33+
%%% <li> directly, with no extra notation required; PropEr will detect which calls
34+
%%% correspond to native types by applying a parse transform during compilation.
35+
%%% This parse transform is automatically applied to any module that includes
36+
%%% the `proper.hrl' header file.</li>
37+
%%% <li> by leveraging the `?TYPE` macro; This allows urestricted use of Erlang's
38+
%%% type language and does not require applying the parse transform.</li>
39+
%%% </ul>
40+
%%%
41+
%%% You can disable the parse transform (and automatic detection of native types)
42+
%%% by compiling your modules with `-DPROPER_NO_TRANS' flag. Note that this will
43+
%%% currently also disable the automatic exporting of properties.
3744
%%%
38-
%%% The use of native types in properties is subject to the following usage
39-
%%% rules:
45+
%%% The use of automatically-detected native types in properties is subject
46+
%%% to the following usage rules:
4047
%%% <ul>
4148
%%% <li>Native types cannot be used outside of `?FORALL's.</li>
4249
%%% <li>Inside `?FORALL's, native types can be combined with other native
@@ -154,6 +161,19 @@
154161
%%% </li>
155162
%%% </ul>
156163
%%%
164+
%%% The `?TYPE` macro for explicitly taking advantage of PropEr's native type
165+
%%% support is subject to the following usage rules:
166+
%%% <ul>
167+
%%% <li>It is allowed in any position to produce a PropEr type, including
168+
%%% outside of `?FORALL`.</li>
169+
%%% <li>The same restriction on allowed recursive native types as in `?FORALL` apply.</li>
170+
%%% <li>There's no risk of confusion between expressions and native types,
171+
%%% inside `?TYPE` everything is interpreted as a native type, and as such
172+
%%% `?TYPE([integer()])` will produce an arbitrary integer list.</li>
173+
%%% <li>It is not checked for correct syntax - using invalid syntax can produce
174+
%%% errors when the property is evaluated, which are hard to understand.</li>
175+
%%% </ul>
176+
%%%
157177
%%% You can use <a href="#index">these</a> functions to try out the type
158178
%%% translation subsystem.
159179
%%%

test/no_transforms.erl

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
%%% -*- coding: utf-8 -*-
2+
%%% -*- erlang-indent-level: 2 -*-
3+
%%% -------------------------------------------------------------------
4+
%%% Copyright 2010-2011 Manolis Papadakis <manopapad@gmail.com>,
5+
%%% Eirini Arvaniti <eirinibob@gmail.com>
6+
%%% and Kostis Sagonas <kostis@cs.ntua.gr>
7+
%%%
8+
%%% This file is part of PropEr.
9+
%%%
10+
%%% PropEr is free software: you can redistribute it and/or modify
11+
%%% it under the terms of the GNU General Public License as published by
12+
%%% the Free Software Foundation, either version 3 of the License, or
13+
%%% (at your option) any later version.
14+
%%%
15+
%%% PropEr is distributed in the hope that it will be useful,
16+
%%% but WITHOUT ANY WARRANTY; without even the implied warranty of
17+
%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18+
%%% GNU General Public License for more details.
19+
%%%
20+
%%% You should have received a copy of the GNU General Public License
21+
%%% along with PropEr. If not, see <http://www.gnu.org/licenses/>.
22+
23+
%%% @copyright 2010-2011 Manolis Papadakis, Eirini Arvaniti and Kostis Sagonas
24+
%%% @version {@version}
25+
%%% @doc This module tests whether proper works with parse transforms disabled.
26+
27+
-module(no_transforms).
28+
-export([prop_1/0, prop_2/0]).
29+
30+
-define(PROPER_NO_TRANS, true).
31+
32+
-include_lib("proper/include/proper.hrl").
33+
34+
-type local_integer() :: integer().
35+
-type local_float() :: float().
36+
37+
prop_1() -> ?FORALL(X, ?TYPE(local_integer() | local_float()), is_number(X)).
38+
39+
prop_2() -> ?FORALL(X, native_type(), is_integer(X) orelse is_atom(X)).
40+
41+
native_type() ->
42+
?TYPE(local_integer() | atom()).

test/proper_tests.erl

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -837,7 +837,9 @@ parse_transform_test_() ->
837837
?_assertError(undef, auto_export_test2:prop_1()),
838838
?_assertError(undef, no_native_parse_test:prop_1()),
839839
?_passes(let_tests:prop_1()),
840-
?_failsWith([3*42], let_tests:prop_2())].
840+
?_failsWith([3*42], let_tests:prop_2()),
841+
?_passes(no_transforms:prop_1()),
842+
?_passes(no_transforms:prop_2())].
841843

842844
native_type_props_test_() ->
843845
[?_passes(?FORALL({X,Y}, {my_native_type(),my_proper_type()},

0 commit comments

Comments
 (0)