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

(^)上的类型推断问题

  •  2
  • Crazycolorz5  · 技术社区  · 8 年前

    因此,我正在尝试为Prelude编写自己的替换,我已经(^)实现了:

    {-# LANGUAGE RebindableSyntax #-}
    
    class Semigroup s where
        infixl 7 *
        (*) :: s -> s -> s
    
    class (Semigroup m) => Monoid m where
        one :: m
    
    class (Ring a) => Numeric a where
        fromIntegral :: (Integral i) => i -> a
        fromFloating :: (Floating f) => f -> a
    
    class (EuclideanDomain i, Numeric i, Enum i, Ord i) => Integral i where
        toInteger :: i -> Integer
        quot :: i -> i -> i
        quot a b = let (q,r) = (quotRem a b) in q
        rem :: i -> i -> i
        rem a b = let (q,r) = (quotRem a b) in r
        quotRem :: i -> i -> (i, i)
        quotRem a b = let q = quot a b; r = rem a b in (q, r)
    
    -- . . .
    
    infixr 8 ^
    (^) :: (Monoid m, Integral i) => m -> i -> m
    (^) x i
        | i == 0 = one
        | True   = let (d, m) = (divMod i 2)
                       rec = (x*x) ^ d in
                   if m == one then x*rec else rec
    

    (注意,这里使用的积分是我定义的积分,而不是前奏曲中的积分,尽管它很相似。此外, one

    数字类型是幺半群,所以我可以尝试这样做,比如说2^3,但是类型检查器会给我:

    *AlgebraicPrelude> 2^3
    
    <interactive>:16:1: error:
        * Could not deduce (Integral i0) arising from a use of `^'
          from the context: Numeric m
            bound by the inferred type of it :: Numeric m => m
            at <interactive>:16:1-3
          The type variable `i0' is ambiguous
          These potential instances exist:
            instance Integral Integer -- Defined at Numbers.hs:190:10
            instance Integral Int -- Defined at Numbers.hs:207:10
        * In the expression: 2 ^ 3
          In an equation for `it': it = 2 ^ 3
    
    <interactive>:16:3: error:
        * Could not deduce (Numeric i0) arising from the literal `3'
          from the context: Numeric m
            bound by the inferred type of it :: Numeric m => m
            at <interactive>:16:1-3
          The type variable `i0' is ambiguous
          These potential instances exist:
            instance Numeric Integer -- Defined at Numbers.hs:294:10
            instance Numeric Complex -- Defined at Numbers.hs:110:10
            instance Numeric Rational -- Defined at Numbers.hs:306:10
            ...plus four others
            (use -fprint-potential-instances to see them all)
        * In the second argument of `(^)', namely `3'
          In the expression: 2 ^ 3
          In an equation for `it': it = 2 ^ 3
    

    我知道这是因为Int和Integer都是整数类型,但为什么在正常的前奏中我可以很好地做到这一点呢

    Prelude> :t (2^)
    (2^) :: (Num a, Integral b) => b -> a
    Prelude> :t 3
    3 :: Num p => p
    Prelude> 2^3
    8
    

    即使部分应用于我的签名看起来完全相同?

    *AlgebraicPrelude> :t (2^)
    (2^) :: (Numeric m, Integral i) => i -> m
    *AlgebraicPrelude> :t 3
    3 :: Numeric a => a
    

    我怎样才能使2^3实际上起作用,从而得到8呢?

    1 回复  |  直到 8 年前
        1
  •  1
  •   leftaroundabout    8 年前

    Hindley-Milner类型的系统实际上不喜欢 违约 任何东西在这样的系统中,您希望类型为 正确固定 (刚性,skolem)或 适当多态 ,但这个概念是一个整数。。。但如果你愿意的话,我也可以把它转换成其他语言,因为许多其他语言并没有真正起作用。

    因此,哈斯克尔不善于违约。它没有一流的支持,只有一个相当粗糙的特别的硬编码机制,主要处理内置的数字类型,但在任何涉及更多的方面都失败了。

    因此,你应该尽量不要依赖违约。我的意见是 ^ 不合理;更好的签名是

    (^) :: Num a => a -> Int -> a
    

    这个 Int 当然可能有争议 Integer 在某种意义上更安全;但是 拥护者 通常意味着结果无论如何都将完全超出范围,并且无法通过迭代乘法进行计算;所以这种表达意愿的方式很好。它在极为常见的情况下提供了最佳性能,您只需编写 x^2 或者类似的,你肯定不想在指数中加入额外的签名。

    在较少的情况下,你有一个具体的,例如。 如果你想在指数中使用它,你总是可以插入一个显式的 fromIntegral . 这不太好,但不太麻烦。

    一般来说,我尽量 避免 任何比结果更多态的函数参数 . Haskell的多态性最好向后运行,即与动态语言相反:调用者请求结果应该是什么类型,编译器从中找出参数应该是什么。这几乎总是可行的,因为只要结果以某种方式用于主程序,整个计算中的类型就必须链接到树结构。

    当然,推断结果的类型通常是有问题的:参数可能是可选的,其本身可能仅与结果链接,或者作为多态常量(如Haskell数文字)提供。那么,如果 i ^ ,也不要让争论发生。


    †避免并不意味着我从不写它们,我只是不这样做,除非有充分的理由。