代码之家  ›  专栏  ›  技术社区  ›  schuelermine

为什么floatRange、floatRadix和floatDigits是函数?

  •  7
  • schuelermine  · 技术社区  · 6 年前

    Hackage ,这些函数 RealFloat

    ... 常量函数。。。

    如果它们始终保持相同的值,无论参数是什么,如本说明所示,为什么不简单地使用:

    class (RealFrac a, Floating a) => RealFloat a where
        floatRadix :: Integer
        floatDigits :: Int
        floatRange :: (Int, Int)
        ...
    
    3 回复  |  直到 6 年前
        1
  •  3
  •   dfeuer    6 年前

    这个 RealFloat undefined 在那种类型。正如leftaroundabout所解释的,GHC现在有很多扩展,它们在大多数情况下都能很好地实现这一点。但之前 TypeApplications ,发明了另外两种技术来更干净地完成这项工作。

    要在没有GHC扩展的情况下消除歧义,可以使用代理传递或基于新类型的标记。我相信这两种技术都是由Edward Kmett和Shachaf Ben Kiki的最后一个多态性自旋给出的(参见 Who invented proxy passing and when? ). 代理传递倾向于提供一个易于使用的API,而newtype方法在某些情况下更有效。下面是代理传递方法。这要求您传递某种类型的参数。传统上,调用者会使用 Data.Proxy.Proxy

    data Proxy a = Proxy
    

    下面是使用代理传递时类的外观:

    class (RealFrac a, Floating a) => RealFloat a where
        floatRadix :: proxy a -> Integer
        floatDigits :: proxy a -> Int
        ...
    

    foo :: Int
    foo = floatDigits (Proxy :: Proxy Double)
    

    tagged Control.Applicative.Const ,但这并不能很好地传达意图。

    newtype Tagged t a = Tagged
      { unTagged :: a }
    

    下面是课堂的样子:

    class (RealFrac a, Floating a) => RealFloat a where
        floatRadix :: Tagged a Integer
        floatDigits :: Tagged a Int
        ...
    

    下面是您如何使用它:

    foo :: Int
    foo = unTagged (floatDigits :: Tagged Double Int)
    
        2
  •  11
  •   leftaroundabout    6 年前

    您建议的非函数方法将具有

    floatRadix' :: RealFloat a => Integer
    floatDigits' :: RealFloat a => Int
    ...
    

    不明确的类型 a 类型变量,但它实际上并不显示在 => 因此无法从上下文中推断。在标准的Haskell中 (floatDigits' :: Int) (floatDigits' :: RealFloat Double => Int) ,它实际上不起作用编译器不能推断你的意思是 instance RealFloat Double 方法的版本。

    class RealFloat' a where
      floatDigits' :: Int
    instance RealFloat' Double where
      floatDigits' = floatDigits (0 :: Double)
    
    *Main> floatDigits' :: Int
    
    <interactive>:3:1: error:
        • No instance for (RealFloat' a0)
            arising from a use of ‘floatDigits'’
        • In the expression: floatDigits' :: Int
          In an equation for ‘it’: it = floatDigits' :: Int
    *Main> floatDigits' :: RealFloat Double => Int
    
    <interactive>:4:1: error:
        • Could not deduce (RealFloat' a0)
            arising from a use of ‘floatDigits'’
          from the context: RealFloat Double
            bound by an expression type signature:
                       RealFloat Double => Int
            at :4:17-39
          The type variable ‘a0’ is ambiguous
        • In the expression: floatDigits' :: RealFloat Double => Int
          In an equation for ‘it’:
              it = floatDigits' :: RealFloat Double => Int
    

    因此,Haskell首先不允许您编写类型不明确的方法。实际上,当我在上面编写类时尝试编译该类时,会出现以下错误消息:

        • Could not deduce (RealFloat' a0)
          from the context: RealFloat' a
            bound by the type signature for:
                       floatDigits' :: forall a. RealFloat' a => Int
            at /tmp/wtmpf-file3738.hs:2:3-21
          The type variable ‘a0’ is ambiguous
        • In the ambiguity check for ‘floatDigits'’
          To defer the ambiguity check to use sites, enable AllowAmbiguousTypes
          When checking the class method:
            floatDigits' :: forall a. RealFloat' a => Int
          In the class declaration for ‘RealFloat'’
    

    {-# LANGUAGE AllowAmbiguousTypes #-} 到文件的顶部 class RealFloat'

    但是,当实例无法在使用站点解析时,有什么意义呢?好吧,是的 可以 实际上可以解决,但只能使用 another pretty new GHC extension :

    *Main> :set -XTypeApplications 
    *Main> floatDigits' @Double
    53
    
        3
  •  5
  •   willeM_ Van Onsem    6 年前

    问题在于,您需要为多个实例构造函数,例如:

    instance RealFloat Float where
        -- ...
        floatRadix = 2
        floatDigits = 24
        floatRange = (-125, 128)
    
    instance RealFloat Double where
        -- ...
        floatRadix = 2
        floatDigits = 53
        floatRange = (-1021, 1024)
    

    floatDigits :我们应该举什么例子?是给你的吗 Float ,或是 Double (或其他类型)?所有这些都是有效的候选人。

    a

    Prelude> floatDigits (0 :: Float)
    24
    Prelude> floatDigits (0 :: Double)
    53
    

    但它认为参数的值并不重要,例如:

    Prelude> floatDigits (undefined :: Float)
    24
    Prelude> floatDigits (undefined :: Double)
    53
    
    推荐文章