【问题标题】:Haskell type error matching Num a with IntHaskell 类型错误将 Num a 与 Int 匹配
【发布时间】:2016-03-13 13:46:24
【问题描述】:

为什么我不能这样做?

genList :: Num a => Int -> [a]
genList m_size = [1..m_size]

上面写着:

 Couldn't match expected type `a' with actual type `Int'
      `a' is a rigid type variable bound by
          the type signature for genList :: Num a => Int -> [a]
          at uloha1.hs:105:12
    Relevant bindings include
      genList :: Int -> [a] (bound at uloha1.hs:106:1)
    In the expression: m_size
    In the expression: [1 .. m_size]

为什么不能隐式地将 Int 重新输入为 Num? Int 是 Num 的实例,不是吗? 我在这方面找不到任何东西。 我正在使用最新的 Haskell 平台 Ghci 我对 haskell 也完全陌生。

【问题讨论】:

    标签: haskell


    【解决方案1】:

    Haskell 永远不应该“重新输入”任何东西。您可能将此与多态数字文字混淆了。但是,m_size 不是文字,而是明确的 Int。你可以写

    [1 .. fromIntegral m_size]
    

    但是你仍然有这个构造还需要一个Enum 约束的问题。

    另一种选择是

    genList :: Enum a => Int -> [a]
    genList m_size = [toEnum 1 .. toEnum m_size]
    

    由于这适用于任何枚举类型,它也适用于所有数字枚举类型。

    【讨论】:

    • 谢谢!所以基本上我不能使用 yhis 的结构,必须想点别的,对吧? :)
    • 您可以简单地添加枚举约束 @Mylan719。
    • 这就是问题所在,我不能。我有这样的强制性类型定义: identity :: Num a => Int -> [[a]] 我将问题简单化为一维数组以便更清楚。我无法更改类型定义一点。 :) 我虽然这个符号很方便,但它似乎做了一些我不明白的事情。
    • @Mylan719 这是什么意思“我有强制性的类型定义”?你可以告诉要求这个的好人,这是不可能的。对于所有数字类型,您不能从 1 变为某个数字,至少不进行比较。由于 Ord 不是 Haskell 中 Num 的超类,因此您的类型根本没有受到足够的约束。就好像有人要求你只用氧气和碳来制造酒精。这是不可能的,你还需要氢气。
    【解决方案2】:

    如果您确实需要这个签名,您仍然可以让它工作 - 您不能使用 [x..y] 枚举 - 一个简单的实现是这样的:

    genList :: Num a => Int -> [a]
    genList = reverse . genList'
    
    genList' :: Num a => Int -> [a]
    genList' 0 = []
    genList' n = fromIntegral n : genList' (n-1)
    

    它应该按预期工作:

    λ> genList 5 :: [Int]
    [1,2,3,4,5]
    λ> genList 5 :: [Double]
    [1.0,2.0,3.0,4.0,5.0]
    

    【讨论】:

      【解决方案3】:

      我四处打听这个问题。我意识到,我不明白的是,haskell 不能“施放”任何东西。如果之前过于笼统,它只能使类型更具体。

      因此,1 的类型为 Num,但通过执行 1 + 1::Int,结果类型变为 Int。但它不能从Int 回到Num。 所以[1..m_size][Int] 类型,它比[Num] 更具体,并且haskell 不能转换它,因为我认为它会因为我习惯于使用OO 语言。

      所以解决这个问题的方法类似于@Carsten 所说的。我将首先生成带有Int 类型元素的列表,然后再“重新输入”它。所以更简洁的解决方案看起来像这样:

      genList :: Num a => Int -> [a]
      genList n = map fromIntegral [1..n]
      

      有人告诉我,由于 haskell 惰性实现,列表元素在被map 处理时只会被迭代一次。

      还要感谢@Ingo @Kiraa @chepner 的贡献,我基本上试图在这里总结一下。

      【讨论】:

        【解决方案4】:

        问题不在于将 Int 重新键入为 Num,而在于使用 [1..m_size] 表示法而没有向编译器保证 a 正在实现 Enum。函数的类型应该是

        genList :: (Num t, Enum t) => t -> [t]                                                                                                              
        

        【讨论】:

        • 这并不能解决我的问题,不幸的是,我无法更改函数的类型。我要做的就是生成从 1 到 m_size 的包含 Nums 的列表,我不明白为什么列表的元素需要是可枚举的。
        • Enum 类告诉编译器,他将能够假定一个值的前任和后继。要生成给定最小和最大元素的列表,您需要知道第一个元素的后继者,然后是第二个元素的后继者,依此类推。
        • 不,我不要求它为我生成数字列表 [1,2,3,4,...,m_size],它与 a 是什么无关。 1 是 Int 是 Enum 类型,m_size 也是 Int 和 enum 类型。
        • 这不是问题,因为[1..m_size] 非常好,因为m_size :: IntEnum 的一个实例(以及Ord 等)
        【解决方案5】:

        仅仅因为IntNum 的实例并不意味着它等同于任何 可以声明为Num 实例的类型。考虑

        genList 1000 :: [Int8]
        

        Int8Num 的一个实例,但通常Int(在本例中为1000)不是Int8 类型的值。

        假设我们不打算更改您的函数的定义(请参阅其他答案以了解如何做到这一点),我们可以做以下两件事之一:

        1. 将参数类型设置为Int,并意识到我们必须返回Ints 的列表,因为我们没有指定创建另一个列表的方法输入。

          genList :: Int -> [Int]
          
        2. 允许不太通用的输出类型,它规定了允许的输入类型。与其他答案类似,ghci 可以从您的定义中推断出有效的函数类型:genList :: (Enum t, Num t) => t -> [t]。也就是说,您可以为任何类型 t 返回 [t],只要输入类型也是 t 并且该类型是 EnumNum 的实例。

          李>

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2012-09-14
          • 2018-03-16
          相关资源
          最近更新 更多