[mercury-users] Maths and contexts.

Paul Bone pbone at csse.unimelb.edu.au
Fri Apr 20 17:09:28 AEST 2012

On Fri, Apr 20, 2012 at 01:25:42PM +0800, Michael Richter wrote:
> I'm trying to implement the General Decimal
> Arithmetic<http://speleotrove.com/decimal> (henceforth
> GDA) specification in Mercury as a learning project.  So far it's been a
> lot of fun and I've been learning quite a bit (as well as getting
> frequently humbled by Boney and ski in #mercury as they gently suggest
> obvious ways to improve my code).  There is one problem I've been facing
> that I can't resolve, however.  Boney suggested the use of type classes to
> solve it, but I just can't see how they'd help.

Hi Michael,

This is what I ment when I said you could use a typeclass.  (In IRC I use the
name Boney, for those following along).

I've given this a quick test and the compiler is happy with what's there.  (it
only complains about the bits that I havn't written).


:- module decimal.

:- interface.

:- import_module bool.
:- import_module integer.
:- import_module string.

:- type rounding_strategy
    ---> round_down       % mandatory strategies
       ; round_half_up
       ; round_half_even
       ; round_ceiling
       ; round_floor
       ; round_half_down  % optional strategies
       ; round_up
       ; round_05up.

:- type signal
    ---> signal( flag         :: bool
               , trap_enabler :: bool ).

    % This is a little tricky, because contexts don't really exist.
    % there will be types that contain contexts, but they'll hold no
    % data and the programmer will never create values of that type.
    % However, the typesystem will see them and make sure that a context
    % is choosen when the programmer calls one of the functions below.
:- typeclass context(C) where [
        func precision(dec(C))         = integer,
        func rounding(dec(C))          = rounding_strategy,
        func clamped(dec(C))           = signal,
        func division_by_zero(dec(C))  = signal,
        func inexact(dec(C))           = signal,
        func invalid_operation(dec(C)) = signal,
        func overflow(dec(C))          = signal,
        func rounded(dec(C))           = signal,
        func subnormal(dec(C))         = signal,
        func underflow(dec(C))         = signal

:- type sign
    ---> positive
       ; negative.

    % Decimals are parametized with the context.
    % Note also that this type is abstract. it's implementation is in
    % the implementation section below.
:- type decimal(C).

:- type dec(C) == decimal(C).

    % A context must be defined to call these functions.
    % You might find that you don't need a context for simply printing
    % out the number, therefore the typeclass constraint can be dropped
    % from these declrations.
:- func to_scientific_string(dec(C))   = string
    <= context(C).
:- func to_engineering_string(dec(C))  = string
    <= context(C).

:- func to_number(string)              = (dec(C))
    <= context(C).
:- func abs(dec(C))                   = (dec(C))
    <= context(C).
:- func add(dec(C), dec(C))          = (dec(C))
    <= context(C).

% So how do we create contexts.  This module might create some
% predefined contexts: These are exported but they are abstract, only
% this module knows how they're implemented.

:- type basic_default_context.
:- instance context(basic_default_context).
:- type extended_default_context.
:- instance context(extended_default_context).

% How about an optimization?  Lets say basic_default_context is very
% common, we can ask the compiler to generate optimized versions of our
% functions for this context.

:- pragma type_spec(add/2, C = basic_default_context).

:- implementation.

    % Note that there is no need to store a value of C in this type.
    % Infact, because the functions declared by the typeclass take a dec(C)
    % rather than a C as a parameter, you never need to construct a value in
    % the type C.  If your code for add wants to know the precision on a
    % number, it just calls precision(Number).
:- type decimal(C)
    ---> number( sign        :: sign
               , coefficient :: integer
               , exponent    :: integer )
       ; infinity(sign)
       ; quiet_nan(sign)
       ; signalling_nan(sign).

% Because our pre-defined contexts are actually real types we still have
% to define them here.  We give them a sigle constructor and no
% arguments.  The compiler will know that they're a dummy type, since
% they cannot ever encode any information.

:- type basic_default_context
    ---> basic_default_context.

% Remember to define the typeclass instance.

:- type extended_default_context
    ---> extended_default_context.

-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 490 bytes
Desc: Digital signature
URL: <http://lists.mercurylang.org/archives/users/attachments/20120420/c9baf68f/attachment.sig>

More information about the users mailing list