Существует ли стандартная функция Haskell с типом: (Floating a, RealFrac b) => a -> b? - PullRequest
4 голосов
/ 14 июня 2019

Мне нужно вызвать floor() для значения, которое ограничено только классом Floating, но floor() требует RealFrac.

Как я могу это сделать?

Я совершенно готов позвонить abs() перед вызовом floor(), но одного этого недостаточно для разрешения моего конфликта ограничений.И coerce жалуется, что эти два представления нельзя считать эквивалентными, что неудивительно.

Мне кажется, что мне нужна функция с сигнатурой типа:

(Floating a, RealFrac b) => a -> b

И мне кажется (вполне законно) давать какую-то дополненную версию abs() этой подписи.Увы, поиск в Google по приведенной выше подписи типа оставил меня с пустыми руками.

Есть мысли?

Спасибо.
:)

Ответы [ 2 ]

6 голосов
/ 14 июня 2019

Рассмотрим следующий экземпляр Floating:

import Control.Applicative

instance (Num a) => Num (e -> a) where
    (+) = liftA2 (+)
    (*) = liftA2 (*)
    (-) = liftA2 (-)
    abs = fmap abs
    signum = fmap signum
    negate = fmap negate
    fromInteger = pure . fromInteger

instance (Fractional a) => Fractional (e -> a) where
    fromRational = pure . fromRational
    recip = fmap recip
    (/) = liftA2 (/)

instance (Floating a) => Floating (e -> a) where
    pi = pure pi
    exp = fmap exp
    log = fmap log
    sin = fmap sin
    cos = fmap cos
    asin = fmap asin
    acos = fmap acos
    atan = fmap atan
    sinh = fmap sinh
    cosh = fmap cosh
    asinh = fmap asinh
    acosh = fmap acosh
    atanh = fmap atanh

Демо:

main :: IO ()
main = do
    print (sqrt sqrt 81)
    let f = sin^2 + cos^2
    print (f 42)

(выводит 3.0000000000000004 и 1.0.)

Это делает функции экземпляром Floating, но код обобщает для всех типов Monad s или Applicative s.

Ваша гипотетическая функция должна иметь тип

(Floating a, RealFrac b) => (e -> a) -> b

в этом случае.Мы могли бы установить a и b на Double:

(e -> Double) -> Double

Как вы выполняете эту операцию?

Помните, что я говорил, что это обобщает все аппликативы?Мы можем заменить e -> на IO в приведенных выше случаях.Тогда тип, с которым вы работаете, становится еще хуже:

IO Double -> Double

Проблема в том, что Floating может быть любым, например, поддерживающим операции exp или sin (которые могут быть чисто символическими, например, надсинтаксическое дерево), тогда как RealFrac должно быть числом (или чем-то, что можно преобразовать в число).

0 голосов
/ 14 июня 2019

Можете ли вы позволить себе ограничение Ord?

module FBound (ffloor, fceil) where
import Data.List (foldl')

-- |
-- >>> ffloor . fromInteger <$> [-10..10]
-- [-10,-9,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,8,9,10]
-- >>> ffloor . (+0.001) . fromInteger <$> [-10..10]
-- [-10,-9,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,8,9,10]
-- >>> ffloor . (+0.999) . fromInteger <$> [-10..10]
-- [-10,-9,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,8,9,10]
ffloor :: (Ord a, Floating a, Integral b) => a -> b
ffloor a | a >= 0     = ffloor' a
         | otherwise  = negate $ fceil' (-a)

-- |
-- >>> fceil. fromInteger <$> [-10..10]
-- [-10,-9,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,8,9,10]
-- >>> fceil . (-0.001) . fromInteger <$> [-10..10]
-- [-10,-9,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,8,9,10]
-- >>> fceil . (-0.999) . fromInteger <$> [-10..10]
-- [-10,-9,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,8,9,10]
fceil :: (Ord a, Floating a, Integral b) => a -> b
fceil a | a >= 0     = fceil' a
        | otherwise  = negate $ ffloor' (-a)

-- given a >= 0, ffloor' a <= a < ffloor' a + 1
ffloor' :: (Ord a, Floating a, Integral b) => a -> b
ffloor' = foldl' roundDown 0 . reverse . takeWhile (>=1) . iterate (/2)

-- given a >= 0, fceil' a - 1 < a <= fceil' a
fceil' :: (Ord a, Floating a, Integral b) => a -> b
fceil' a = ffloor' (a/2) `roundUp` a

-- given 2*i <= a < 2*i + 2, roundDown i a <= a < roundDown i a + 1
roundDown :: (Ord a, Num a, Integral b) => b -> a -> b
roundDown i a | a < fromIntegral (2*i + 1) = 2*i
              | otherwise                  = 2*i + 1

-- given 2*i <= a < 2*i + 2, roundUp i a - 1 < a <= roundUp i a
roundUp :: (Ord a, Num a, Integral b) => b -> a -> b
roundUp i a | a == fromIntegral (2*i)     = 2*i
            | a <= fromIntegral (2*i + 1) = 2*i + 1
            | otherwise                   = 2*i + 2
...