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

Maintainer  :  claude@mathr.co.uk
Stability   :  unstable
Portability :  Rank2Types

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

-}

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

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

-- | Reify a precision from value-level to type-level.
reifyPrecision :: Word -> (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 -> Word -> (forall p . NaturalNumber p => p -> a) -> a
    go n i f
      | i == 0 = f n
      | otherwise = go (successorTo n) (i - 1) f

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

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