{-# LANGUAGE Rank2Types #-}
{- |
Module      :  Numeric.VariablePrecision.Precision.Reify
Copyright   :  (c) Claude Heiland-Allen 2012
License     :  BSD3

Maintainer  :  claudiusmaximus@goto10.org
Stability   :  provisional
Portability :  Rank2Types

Reify from value-level to type-level using Rank2Types.

-}

module Numeric.VariablePrecision.Precision.Reify
  ( reifyPrecision
  , withReifiedPrecision
  , (.@$)
  ) where

import Numeric.VariablePrecision.Precision (NaturalNumber, n0, successorTo, VariablePrecision, withPrecision)

-- | Reify a precision from value-level to type-level.
reifyPrecision :: Int -> (forall p . NaturalNumber p => p -> a) -> a
-- Implemented as described in an email from Gregory Grosswhite
-- <http://markmail.org/message/55iuty6axeljj2do>
reifyPrecision = go n0
  where
    go :: NaturalNumber q => q -> Int -> (forall p . NaturalNumber p => p -> a) -> a
    go n i f
      | i <  0 = error $ "Numeric.VariablePrecision.Precision.Reify.reifyPrecision: negative argument: " ++ show i
      | i == 0 = f n
      | i >  0 = go (successorTo n) (i - 1) f

-- | Much like 'reifyPrecision' combined with 'withPrecision'.
withReifiedPrecision
  :: (VariablePrecision t, NaturalNumber p)
  => t p {-^ original value -}
  -> Int {- ^ new precision -}
  -> (forall q. NaturalNumber q => t q -> a) {-^ operation -}
  -> a
withReifiedPrecision x i f = reifyPrecision i (f . withPrecision x)
infixl 1 `withReifiedPrecision` -- same fixity as Prelude.$

-- | An alias for 'withReifiedPrecision'.
(.@$)
  :: (VariablePrecision t, NaturalNumber p)
  => t p {-^ original value -}
  -> Int {- ^ new precision -}
  -> (forall q. NaturalNumber q => t q -> a) {-^ operation -}
  -> a
(.@$) = withReifiedPrecision
infix 1 .@$ -- same fixity as Prelude.$