2015-12-10 5 views
4

Мне нужна была Призма для преобразования Integral a => a в (Integral b, Bounded b) => b, гарантируя, что a действительно вписывается в тип b. Мое текущее определение (ниже) требует использования ScopedTypeVariables и является довольно подробным.Призма для преобразования в ограниченный интеграл

Я хотел бы знать, если есть лучше (в идеале уже определены, и я пропустил его) способ проверить, если номер вписывается в тип Bounded или функции для безопасного преобразования, что я могу использовать построить призму.

Текущее определение:

boundedInt :: forall a b. (Integral a, Integral b, Bounded b) => Prism' a b 
boundedInt = prism fromIntegral f where 
    f n = 
    if n >= fromIntegral (minBound :: b) && n <= fromIntegral (maxBound :: b) 
    then Right (fromIntegral n) 
    else Left n 

ответ

2

Немного неудовлетворительный ответ: Я не думаю, что есть библиотека, которая поможет вам здесь.

Действительно, вам нужно что-то вроде

safeIntegerToBoundedIntegral :: (Integral b, Bounded b) => Integer -> Maybe b 
safeIntegerToBoundedIntegral = boundedFromInteger (minBound, maxBound) 

-- helper function's signature lets you avoid `ScopedTypeVariables` 
boundedFromInteger :: Integral b => (b,b) -> Integer -> Maybe b 
boundedFromInteger (lo,hi) n | toInteger lo <= n && n <= toInteger hi = Just (fromInteger n) 
boundedFromInteger _ _ = Nothing 

, который я в настоящее время не видим на hoogle или hayoo, то вы были бы в состоянии выразить это немного более легко:

integerAsIntegral :: Integral a => Iso' Integer a 
integerAsIntegral = iso fromInteger toInteger 

integerAsBoundedIntegral :: (Integral a, Bounded a) => Prism' Integer a 
integerAsBoundedIntegral = prism toInteger $ \n -> 
    maybe (Left n) Right $ safeIntegerToBoundedIntegral n 

integralAsBoundedIntegral :: (Integral a, Integral b, Bounded b) => Prism' a b 
integralAsBoundedIntegral = from integerAsIntegral . integerAsBoundedIntegral 

Я разделил это так, как я это сделал, потому что я не знаю никаких гарантий, что fromIntegral хранится в порядке; То, что a < bfromIntegral a < fromIntegral b, и поэтому я решил использовать Integer в качестве канонического порядка при сравнении Integral s, чтобы он был по крайней мере последователен.

2

Я думаю, что самым прямым вопросом является не «Является ли число в границах?» но «Может ли номер отправиться туда и обратно?» Так что я бы уронить Bounded вопрос в целом:

narrowIntegral :: (Integral a, Integral b) => Prism' a b 
narrowIntegral = prism fromIntegral f where 
    f n | fromIntegral candidate == n = Right candidate 
     | otherwise = Left n 
     where candidate = fromIntegral n 

Единственная проблема в том, что вы могли бы потенциально поразить Integral тип которого fromInteger функция частично. Вы могли бы попробовать проверить границы в этом случае, но было бы более интересно немного разобраться с чем-то вроде этого:

narrowIntegral :: (Integral a, NFData a, Integral b, NFData b) => Prism' a b 
narrowIntegral = prism fromIntegral f where 
    f n = n `deepseq` unsafePerformIO (narrow n) 

narrow :: (Integral a, Integral b, NFData b) => a -> IO (Either a b) 
narrow n = (evaluate . force) (narrow' n) `catches` 
      [Handler (\(_ :: ArithException) -> return (Left n)) 
      ,Handler (\(_ :: PatternMatchFail) -> return (Left n)) 
      ,Handler (\(_ :: ErrorCall) -> return (Left n))] 

narrow' n 
    | fromIntegral candidate == n = Right candidate 
    | otherwise = Left n 
    where candidate = fromIntegral n