Version 0.10.0
Version 0.10.0 has been released! Ⲗ
Additions & Improvements
Support for function-types (for typechecks as well as property-testing generators):
(-> result_type)
(...-> result_type)
(param_type, param2_type -> result_type)
Type-checking function-types
Type-checking a function value against a function-type works a bit differently from most other types.
The reason for this is that we can only ascertain whether the function-value works correctly when the function-value is called.
Specifically:
- When a call to
TypeCheck.conforms/3
(and variants) or a function wrapped with a@spec!
is called, we can immediately check whether a particular parameter:- is a function
- accepts the expected arity
- Then, the parameter-which-is-a-function is wrapped in a 'wrapper function' which, when called:
- typechecks whether the passed parameters are of the expected types (This checks whether your function uses the parameter-function correctly.)
- calls the original function with the parameters.
- typechecks whether the result is of the expected type. (This checks whether the parameter-function works correctly.)
- returns the result.
In other words, the 'wrapper function' which is added for a type (param_type, param_type2 -> result_type)
works similarly
to a named function with the spec @spec! myfunction(param_type, param_type2) :: result_type
.
As an example:
iex> # The following passes the first check...
iex> fun = TypeCheck.conforms!(&div/2, (integer(), integer() -> boolean()))
iex> # ... but once the function returns, the wrapper will raise
iex> fun.(20, 5)
** (TypeCheck.TypeError) The call to `#Function<...>/2` failed,
because the returned result does not adhere to the spec `boolean()`.
Rather, its value is: `4`.
Details:
The result of calling `#Function<...>.(20, 5)`
does not adhere to spec `(integer(), integer() -> boolean())`. Reason:
Returned result:
`4` is not a boolean.
This was quite the adventure to implement. I am happy that it turned out to be possible, and it is working great!
Data generation for function types
For property-testing generators, the data passed to a generated function is converted into a seed (using [param1, param2, param3] |> :erlang.term_to_binary |> Murmur.hash_x86_32
) and this seed is then used as seed for the data returned from the function.
This means that for any particular test run, any generated function will be pure (i.e. when given the same input multiple times, the same output will be returned).