mirror of
https://github.com/idris-lang/Idris2.git
synced 2024-12-20 18:21:47 +03:00
165 lines
4.2 KiB
ReStructuredText
165 lines
4.2 KiB
ReStructuredText
.. _builtins:
|
|
|
|
********
|
|
Builtins
|
|
********
|
|
|
|
.. role:: idris(code)
|
|
:language: idris
|
|
|
|
Natural numbers
|
|
===============
|
|
|
|
Idris2 supports an optimized runtime representation of natural numbers (non-negative integers).
|
|
This optimization is automatic,
|
|
however it only works when natural numbers are represented in a specific way
|
|
|
|
Here is an example of a natural number that would be optimized:
|
|
|
|
.. code-block:: idris
|
|
|
|
data Natural
|
|
= Zero
|
|
| Succ Natural
|
|
|
|
Natural numbers are generally represented as either zero or the successor (1 more than)
|
|
of another natural number. These are called Peano numbers.
|
|
|
|
At runtime, Idris2 will automatically represent this the same as the ``Integer`` type.
|
|
This will massively reduce the memory usage.
|
|
|
|
There are a few rules governing when this optimization occurs:
|
|
|
|
- The data type must have 2 constructors
|
|
|
|
- After erasure of runtime irrelevant arguments
|
|
+ One must have no arguments
|
|
+ One must have exactly 1 argument (called ``Succ``)
|
|
|
|
- The type of the argument to ``Succ`` must have the same type constructor as the parent type.
|
|
This means indexed data types, like ``Fin``, can be optimised.
|
|
- The argument to ``Succ`` must be strict, ie not ``Lazy Natural``
|
|
|
|
To ensure that a type is optimized to an ``Integer``, use ``%builtin Natural`` ie
|
|
|
|
.. code-block:: idris
|
|
|
|
data MyNat
|
|
= Succ MyNat
|
|
| Zero
|
|
|
|
%builtin Natural MyNat
|
|
|
|
Casting between natural numbers and integer
|
|
===========================================
|
|
|
|
Idris optimizes functions which convert between natural numbers and integers,
|
|
so that it takes constant time rather than linear time.
|
|
|
|
Such functions must be written in a specific way,
|
|
so that idris can detect that it can be optimised.
|
|
|
|
Here is an example of a natural to ``Integer`` function.
|
|
|
|
.. code-block:: idris
|
|
|
|
cast : Natural -> Integer
|
|
cast Z = 0
|
|
cast (S k) = cast k + 1
|
|
|
|
This optimization is applied late in the compilation process,
|
|
so it may be sensitive to seemingly insignificant changes.
|
|
|
|
However here are roughly the rules governing this optimisation:
|
|
|
|
- Exactly one argument must be pattern matched on
|
|
(any other forced or dotted patterns are allowed)
|
|
- The right hand side of the 'Zero' case must be ``0``
|
|
- The right hand side of the 'Succ' case must be ``1 + cast k``
|
|
where ``k`` is the predecessor of the pattern matched argument
|
|
|
|
Casting from an ``Integer`` to a natural is a little more complex.
|
|
|
|
.. code-block:: idris
|
|
|
|
castNonNegative : Integer -> Natural
|
|
castNonNegative x = case x of
|
|
0 => Zero
|
|
_ => Succ $ castNonNegative (x - 1)
|
|
|
|
cast : Integer -> Natural
|
|
cast x = if x < 0 then Zero else castNonNegative x
|
|
|
|
For now you must manually check the given integer is non-negative.
|
|
|
|
If you are using an indexed data type it may be very hard to write
|
|
your ``Integer`` to natural cast in such a way,
|
|
so you can use ``%builtin IntegerToNatural`` to assert to the compiler
|
|
that a function is correct. It is your responsibility to make sure this is correct.
|
|
|
|
.. code-block:: idris
|
|
|
|
module ComplexNat
|
|
|
|
import Data.Maybe
|
|
|
|
data ComplexNat
|
|
= Zero
|
|
| Succ ComplexNat
|
|
|
|
integerToMaybeNat : Integer -> Maybe ComplexNat
|
|
integerToMaybeNat _ = ...
|
|
|
|
integerToNat :
|
|
(x : Integer) ->
|
|
{auto 0 prf : IsJust (ComplexNat.integerToMaybeNat x)} ->
|
|
ComplexNat
|
|
integerToNat x {prf} = fromJust (integerToMaybeNat x) @{prf}
|
|
|
|
%builtin IntegerToNatural ComplexNat.integerToNat
|
|
|
|
Other operations
|
|
================
|
|
|
|
This can be used with ``%transform`` to allow many other operations to be O(1) too.
|
|
|
|
.. code-block:: idris
|
|
|
|
eqNat : Nat -> Nat -> Bool
|
|
eqNat Z Z = True
|
|
eqNat (S j) (S k) = eqNat j k
|
|
eqNat _ _ = False
|
|
|
|
%transform "eqNat" eqNat j k = natToInteger j == natToInteger k
|
|
|
|
plus : Nat -> Nat -> Nat
|
|
plus Z y = y
|
|
plus (S x) y = S $ plus x y
|
|
|
|
%transform "plus" plus j k = integerToNat (natToInteger j + natToInteger j)
|
|
|
|
Compilation
|
|
===========
|
|
|
|
Here are the details of how natural numbers are compiled to ``Integer`` s.
|
|
Note: a numeric literal here is an ``Integer``.
|
|
|
|
``Zero`` => ``0``
|
|
|
|
``Succ k`` => ``1 + k``
|
|
|
|
.. code-block:: idris
|
|
|
|
case k of
|
|
Z => zexp
|
|
S k' => sexp
|
|
|
|
=>
|
|
|
|
.. code-block:: idris
|
|
|
|
case k of
|
|
0 => zexp
|
|
_ => let k' = k - 1 in sexp
|
|
|