mirror of
https://github.com/idris-lang/Idris2.git
synced 2024-12-19 17:21:59 +03:00
121 lines
3.1 KiB
ReStructuredText
121 lines
3.1 KiB
ReStructuredText
|
Failing blocks
|
||
|
==============
|
||
|
|
||
|
.. role:: idris(code)
|
||
|
:language: idris
|
||
|
|
||
|
Failing blocks let users check that some code parses but is rejected during
|
||
|
elaboration. Their simplest form is using the keyword ``failing`` followed by
|
||
|
some indented Idris code:
|
||
|
|
||
|
.. code-block:: idris
|
||
|
|
||
|
failing
|
||
|
trueNotFalse : True === False
|
||
|
trueNotFalse = Refl
|
||
|
|
||
|
Putting the code above in a file and asking Idris to check it will not produce
|
||
|
an error message since the code in the ``failing`` block is rejected, i.e.
|
||
|
fails.
|
||
|
|
||
|
The ``failing`` keyword optionally takes a string argument. If present, the
|
||
|
string attached to the failing block is checked against the error message raised
|
||
|
to check that it appears in the error. This lets the user document the kind of
|
||
|
error expected in the block, while at the same time checking that the error
|
||
|
message is indeed what we expected. For example, in the following example, we
|
||
|
make sure that Idris rejects a proof that the character ``'a'`` is equal to
|
||
|
``'b'`` by throwing an error when unifying them:
|
||
|
|
||
|
.. code-block:: idris
|
||
|
|
||
|
failing "When unifying"
|
||
|
noteq : 'a' === 'b'
|
||
|
noteq = Refl
|
||
|
|
||
|
Failing blocks can be helpful when demonstrating false paths or starts in a
|
||
|
program or example. Valid code is accepted in failing blocks, so intermediary
|
||
|
helper functions can be used as long as at least one statement in the block
|
||
|
fails (it does not have to be the first or last statement!):
|
||
|
|
||
|
.. code-block:: idris
|
||
|
|
||
|
failing "Mismatch between: Integer and Double"
|
||
|
record Point where
|
||
|
constructor MkPoint
|
||
|
x : Integer
|
||
|
y : Integer
|
||
|
|
||
|
Origin : Point
|
||
|
Origin = MkPoint 0 0
|
||
|
|
||
|
dist : Point -> Point -> Integer
|
||
|
dist p1 p2 = sqrt $ (pow (p2.x - p1.x) 2) + (pow (p2.y - p1.y) 2)
|
||
|
|
||
|
Invalid failing blocks
|
||
|
----------------------
|
||
|
|
||
|
Should the failing block not fail, i.e. the code inside is accepted during
|
||
|
elaboration, the compiler will report an error:
|
||
|
|
||
|
.. code-block:: idris
|
||
|
|
||
|
failing
|
||
|
validRefl : 1 === 1
|
||
|
validRefl = Refl
|
||
|
|
||
|
Checking the above gives:
|
||
|
|
||
|
::
|
||
|
|
||
|
Error: Failing block did not fail.
|
||
|
|
||
|
Similarly, if an expected error (sub)string is given but the error output does
|
||
|
not match, we get:
|
||
|
|
||
|
::
|
||
|
|
||
|
Error: Failing block failed with the wrong error.
|
||
|
|
||
|
Followed by the given expected error string and the actual error output,
|
||
|
allowing us to compare and contrast.
|
||
|
|
||
|
One block per falsehood
|
||
|
-----------------------
|
||
|
|
||
|
**Take care to use separate ``failing`` blocks per thing to check.** Using a
|
||
|
single block can lead to unexpected results. The following example passes, since
|
||
|
the second statement fails:
|
||
|
|
||
|
.. code-block:: idris
|
||
|
|
||
|
failing
|
||
|
stmt1 : True === True
|
||
|
stmt1 = Refl
|
||
|
|
||
|
-- at least one failing statement, so the block is accepted
|
||
|
stmt2 : 'a' === 'b'
|
||
|
stmt2 = Refl
|
||
|
|
||
|
stmt3 : 1 === 1
|
||
|
stmt3 = Refl
|
||
|
|
||
|
Instead, separate the statements out into separate failing blocks:
|
||
|
|
||
|
.. code-block:: idris
|
||
|
|
||
|
failing
|
||
|
stmt1 : True === True
|
||
|
stmt1 = Refl
|
||
|
|
||
|
failing
|
||
|
stmt2 : 'a' === 'b'
|
||
|
stmt2 = Refl
|
||
|
|
||
|
failing
|
||
|
stmt3 : 1 === 1
|
||
|
stmt3 = Refl
|
||
|
|
||
|
This causes two ``Error: Failing block did not fail`` messages to be emitted, as
|
||
|
one would expect.
|
||
|
|