==============
Abstract types
==============

.. contents::

Abstract types in Nimrod provide a means to model different `units`:idx: of
a `base type`:idx:.


Use case 1: SQL strings
-----------------------
An SQL statement that is passed from Nimrod to an SQL database might be
modelled as a string. However, using string templates and filling in the
values is vulnerable to the famous `SQL injection attack`:idx:\:

.. code-block:: nimrod
  proc query(db: TDbHandle, statement: TSQL) = ...

  var
    username: string

  db.query("SELECT FROM users WHERE name = '$1'" % username)
  # Horrible security hole, but the compiler does not mind!

This can be avoided by distinguishing strings that contain SQL from strings
that don't. Abstract types provide a means to introduce a new string type
``TSQL`` that is incompatible with ``string``:

.. code-block:: nimrod
  type
    TSQL = abstract string

  proc query(db: TDbHandle, statement: TSQL) = ...

  var
    username: string

  db.query("SELECT FROM users WHERE name = '$1'" % username)
  # Error at compile time: `query` expects an SQL string!


It is an essential property of abstract types that they **do not** imply a
subtype relation between the abtract type and its base type. Explict type
conversions from ``string`` to ``TSQL`` are allowed:

.. code-block:: nimrod
  proc properQuote(s: string): TSQL =
    # quotes a string properly for an SQL statement
    ...

  proc `%` (frmt: TSQL, values: openarray[string]): TSQL =
    # quote each argument:
    var v = values.each(properQuote)
    # we need a temporary type for the type conversion :-(
    type TStrSeq = seq[string]
    # call strutils.`%`:
    result = TSQL(string(frmt) % TStrSeq(v))

  db.query("SELECT FROM users WHERE name = $1".TSQL % username)

Now we have compile-time checking against SQL injection attacks.
Since ``"".TSQL`` is transformed to ``TSQL("")`` no new syntax is needed
for nice looking ``TSQL`` string literals.



Use case 2: Money
-----------------
Different currencies should not be mixed in monetary calculations. Abstract
types are a perfect tool to model different currencies:

.. code-block:: nimrod
  type
    TDollar = abstract int
    TEuro = abstract int

  var
    d: TDollar
    e: TEuro

  echo d + 12
  # Error: cannot add a number with no unit with a ``TDollar``

Unfortunetaly, ``d + 12.TDollar`` is not allowed either,
because ``+`` is defined for ``int`` (among others), not for ``TDollar``. So
we define our own ``+`` for dollars:

.. code-block::
  proc `+` (x, y: TDollar): TDollar =
    result = TDollar(int(x) + int(y))

It does not make sense to multiply a dollar with a dollar, but with a
number without unit; and the same holds for division:

.. code-block::
  proc `*` (x: TDollar, y: int): TDollar =
    result = TDollar(int(x) * y)

  proc `*` (x: int, y: TDollar): TDollar =
    result = TDollar(x * int(y))

  proc `div` ...

This quickly gets tedious. The implementations are trivial and the compiler
should not generate all this code only to optimize it away later - after all
``+`` for dollars should produce the same binary code as ``+`` for ints.
The pragma ``borrow`` has been designed to solve this problem; in principle
it generates the trivial implementation for us:

.. code-block:: nimrod
  proc `*` (x: TDollar, y: int): TDollar {.borrow.}
  proc `*` (x: int, y: TDollar): TDollar {.borrow.}
  proc `div` (x: TDollar, y: int): TDollar {.borrow.}

The ``borrow`` pragma makes the compiler to use the same implementation as
the proc that deals with the abstract type's base type, so no code is
generated.

But it seems we still have to repeat all this boilerplate code for
the ``TEuro`` currency. Fortunately, Nimrod has a template mechanism:

.. code-block:: nimrod
  template Additive(typ: typeDesc): stmt =
    proc `+` *(x, y: typ): typ {.borrow.}
    proc `-` *(x, y: typ): typ {.borrow.}

    # unary operators:
    proc `+` *(x: typ): typ {.borrow.}
    proc `-` *(x: typ): typ {.borrow.}

  template Multiplicative(typ, base: typeDesc): stmt =
    proc `*` *(x: typ, y: base): typ {.borrow.}
    proc `*` *(x: base, y: typ): typ {.borrow.}
    proc `div` *(x: typ, y: base): typ {.borrow.}
    proc `mod` *(x: typ, y: base): typ {.borrow.}

  template Comparable(typ: typeDesc): stmt =
    proc `<` * (x, y: typ): bool {.borrow.}
    proc `<=` * (x, y: typ): bool {.borrow.}
    proc `==` * (x, y: typ): bool {.borrow.}

  template DefineCurrency(typ, base: expr): stmt =
    type
      typ* = abstract base
    Additive(typ)
    Multiplicative(typ, base)
    Comparable(typ)

  DefineCurrency(TDollar, int)
  DefineCurrency(TEuro, int)

