[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).
Enjoy.
:- 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