Copyright © 2010-2016 Manolis Papadakis, Eirini Arvaniti and Kostis Sagonas
Version: Jun 20 2016 16:27:13
Behaviours: gen_server.
Authors: Manolis Papadakis.
Erlang type system - PropEr type system integration module.
PropEr can parse types expressed in Erlang's type language and convert them
to its own type format. Such expressions can be used instead of regular type
constructors in the second argument of ?FORALL
s. No extra notation is
required; PropEr will detect which calls correspond to native types by
applying a parse transform during compilation. This parse transform is
automatically applied to any module that includes the proper.hrl
header
file. You can disable this feature by compiling your modules with
-DPROPER_NO_TRANS
. Note that this will currently also disable the
automatic exporting of properties.
?FORALL
s.?FORALL
s, native types can be combined with other native
types, and even with PropEr types, inside tuples and lists (the constructs
[...]
, {...}
and ++
are all allowed).|
for
union, _
as an alias of any()
, <<_:_>>
binary type syntax and
fun((...) -> ...)
function type syntax) are not allowed in ?FORALL
s,
because they are rejected by the Erlang parser.++
application, local or remote call will automatically be considered a
PropEr type constructor and not be processed further by the parse
transform.?FORALL
. The arguments of parametric native
types are always interpreted as native types.my_proper_type() -> ?LET(...). -type my_native_type() :: ... .Then the following expressions are all legal:
vector(2, my_native_type()) function(0, my_native_type()) union([my_proper_type(), my_native_type()])
?FORALL
s):
?SUCHTHAT
, ?SUCHTHATMAYBE
, non_empty
, noshrink
: these work
with native types too?LAZY
, ?SHRINK
, resize
, ?SIZED
: these don't work with native
types?LET
, ?LETSHRINK
: only the top-level base type can be a native
type?FORALL
s of a module can reference any
custom type declared in a -type
or -opaque
attribute of the same
module, as long as no module identifier is used.?FORALL
s using the
#rec_name{}
syntax. To use a typed record in a ?FORALL
, enclose the
record in a custom type like so:
-type rec_name() :: #rec_name{}.and use the custom type instead.
?FORALL
s may contain references to self-recursive or mutually
recursive native types, so long as each type in the hierarchy has a clear
base case.
Currently, PropEr requires that the toplevel of any recursive type
declaration is either a (maybe empty) list or a union containing at least
one choice that doesn't reference the type directly (it may, however,
reference any of the types that are mutually recursive with it). This
means, for example, that some valid recursive type declarations, such as
this one:
?FORALL(..., a(), ...)where:
-type a() :: {'a','none' | a()}.are not accepted by PropEr. However, such types can be rewritten in a way that allows PropEr to parse them:
?FORALL(..., a(), ...)where:
-type a() :: {'a','none'} | {'a',a()}.This also means that recursive record declarations are not allowed:
?FORALL(..., rec(), ...)where:
-type rec() :: #rec{}. -record(rec, {a = 0 :: integer(), b = 'nil' :: 'nil' | #rec{}}).A little rewritting can usually remedy this problem as well:
?FORALL(..., rec(), ...)where:
-type rec() :: #rec{b :: 'nil'} | #rec{b :: rec()}. -record(rec, {a = 0 :: integer(), b = 'nil' :: 'nil' | #rec{}}).
?FORALL
, so long as they are
exported from the remote module. Currently, PropEr requires that any
remote modules whose types are directly referenced from within properties
are present in the code path at compile time, either compiled with
debug_info
enabled or in source form. If PropEr cannot find a remote
module at all, finds only a compiled object file with no debug
information or fails to compile the source file, all calls to that module
will automatically be considered calls to PropEr type constructors.?FORALL
declaration as well as any module that contains
the declaration of a type referenced (directly or indirectly) from inside
a ?FORALL
must be present in the code path at runtime, either compiled
with debug_info
enabled or in source form.no_auto_import
option.foo()
will shadow a type foo()
if they are both present in the module.
The same rule applies to remote functions and types as well.[integer()]
can be interpreted both ways, so the
PropEr way applies. Therefore, instances of this type will always be
lists of length 1, not arbitrary integer lists, as would be expected
when interpreting the expression as a native type.foo([integer()])
can only be interpreted as a native type declaration,
which means that the generic type of integer lists will be passed to
foo/1
.-type
declaration_
as a type variable in the LHS of a -type
declaration-type
declarationYou can use these functions to try out the type translation subsystem.
CAUTION: These functions should never be used inside properties. They are meant for demonstration purposes only.fin_type() = proper_types:type()
mod_name() = atom()
rich_result(T) = {ok, T} | {error, term()}
demo_is_instance/3 | Checks if Term is a valid instance of native type TypeExpr (which
should be provided inside a string). |
demo_translate_type/2 | Translates the native type expression TypeExpr (which should be
provided inside a string) into a PropEr type, which can then be passed to any
of the demo functions defined in the proper_gen module. |
demo_is_instance(Term :: term(), Mod :: mod_name(), TypeExpr :: string()) -> boolean() | {error, term()}
Checks if Term
is a valid instance of native type TypeExpr
(which
should be provided inside a string). PropEr acts as if it found this type
expression inside the code of module Mod
.
demo_translate_type(Mod :: mod_name(), TypeExpr :: string()) -> rich_result(fin_type())
Translates the native type expression TypeExpr
(which should be
provided inside a string) into a PropEr type, which can then be passed to any
of the demo functions defined in the proper_gen
module. PropEr acts
as if it found this type expression inside the code of module Mod
.
Generated by EDoc, Jun 20 2016, 16:27:13.