Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/haskell/10.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Haskell 对于应用于具有相同函数的不同输入的多态函数,为什么类型推断失败 我正在为C++的一个子集做翻译。翻译是用哈斯凯尔语写的_Haskell_Typechecking - Fatal编程技术网

Haskell 对于应用于具有相同函数的不同输入的多态函数,为什么类型推断失败 我正在为C++的一个子集做翻译。翻译是用哈斯凯尔语写的

Haskell 对于应用于具有相同函数的不同输入的多态函数,为什么类型推断失败 我正在为C++的一个子集做翻译。翻译是用哈斯凯尔语写的,haskell,typechecking,Haskell,Typechecking,表达式的Myeval函数返回一个新环境和一个值。我将这些值编码为一种称为Val的新类型。最简单的例子: data Val = I Integer | D Double 为了计算算术表达式,我想创建一个通用函数,将多态函数(如(+)或(*)应用于Val构造函数中包装的数字 我想要一个这样的函数: -- calculate :: Num a => (a -> a -> a) -> Val -> Val -> Val calculate f (I i1) (I i

表达式的My
eval
函数返回一个新环境和一个值。我将这些值编码为一种称为
Val
的新类型。最简单的例子:

data Val = I Integer | D Double
为了计算算术表达式,我想创建一个通用函数,将多态函数(如
(+)
(*)
应用于
Val
构造函数中包装的数字

我想要一个这样的函数:

-- calculate :: Num a => (a -> a -> a) -> Val -> Val -> Val
calculate f (I i1) (I i2) = I (f i1 i2)
calculate f (D d1) (D d2) = D (f d1 d2)
data NumDict a = NumDict {
        addition :: a -> a -> a
      , subtraction :: a -> a -> a
      , multiplication :: a -> a -> a
      , abs :: a -> a
      ...
      }

calculate' :: (∀ a . NumDict a -> a -> a -> a) -> Val -> Val -> Val
calculate' f (I i1) (I i2) = I (f ndict i1 i2)
 where ndict = NumDict ((+) :: Integer -> Integer -> Integer)
                       ((-) :: Integer -> Integer -> Integer)
                       ...
这会产生以下错误:

tmp/example.hs:4:32: error:
    • Couldn't match expected type ‘Double’ with actual type ‘Integer’
    • In the first argument of ‘D’, namely ‘(f d1 d2)’
      In the expression: D (f d1 d2)
      In an equation for ‘calculate’:
          calculate f (D d1) (D d2) = D (f d1 d2)
  |
4 | calculate f (D d1) (D d2) = D (f d1 d2)
  |                                ^^^^^^^

tmp/example.hs:4:34: error:
    • Couldn't match expected type ‘Integer’ with actual type ‘Double’
    • In the first argument of ‘f’, namely ‘d1’
      In the first argument of ‘D’, namely ‘(f d1 d2)’
      In the expression: D (f d1 d2)
  |
4 | calculate f (D d1) (D d2) = D (f d1 d2)
  |                                  ^^

tmp/example.hs:4:37: error:
    • Couldn't match expected type ‘Integer’ with actual type ‘Double’
    • In the second argument of ‘f’, namely ‘d2’
      In the first argument of ‘D’, namely ‘(f d1 d2)’
      In the expression: D (f d1 d2)
  |
4 | calculate f (D d1) (D d2) = D (f d1 d2)
  |                                     ^^
我不能对这件事耿耿于怀。我有两个问题:

  • 为什么这个程序无法进行类型检查
  • 如何正确执行
    计算

  • 我只对通用量化类型略知一二,因此如果这是问题的一部分,请温和地解释。

    您已经正确确定需要通用量化。事实上,你已经有了通用的量化——你的签名,就像任何多态签名一样,基本上是

    {-# LANGUAGE ExplicitForall, UnicodeSyntax #-}
    calculate :: ∀ a . Num a => (a -> a -> a) -> Val -> Val -> Val
    
    意思:每当有人想要使用这个函数时,他们就可以选择一些类型来预先输入
    a
    。例如,他们可以选择
    Int
    ,然后函数将专门用于

    calculate :: (Int -> Int -> Int) -> Val -> Val -> Val
    
    然后在运行时使用

    但是这对你没有用,因为你需要对不同的数字类型使用这个函数。没有一个单一的专业将涵盖所有这些领域

    解决方法:延迟选择类型。这是通过放置通用quantor
    (您也可以将其写入签名的combinator函数部分中的所有代码):

    {-# LANGUAGE Rank2Types #-}
    calculate :: (∀ a . Num a => a -> a -> a) -> Val -> Val -> Val
    
    这将进行打字检查。它确实需要
    -XRank2Types
    扩展,因为这是一个相当复杂的beast:现在您不能简单地将多态函数描述为一系列具有具体单态类型的专门化函数,而是函数需要准备好在运行时实例化,提供的函数包含数据结构中发生的任何类型

    也就是说,它需要向函数传递一个附加参数:一个包含
    Num
    类方法的“字典”。GHC生成的底层实现如下所示:

    -- calculate :: Num a => (a -> a -> a) -> Val -> Val -> Val
    calculate f (I i1) (I i2) = I (f i1 i2)
    calculate f (D d1) (D d2) = D (f d1 d2)
    
    data NumDict a = NumDict {
            addition :: a -> a -> a
          , subtraction :: a -> a -> a
          , multiplication :: a -> a -> a
          , abs :: a -> a
          ...
          }
    
    calculate' :: (∀ a . NumDict a -> a -> a -> a) -> Val -> Val -> Val
    calculate' f (I i1) (I i2) = I (f ndict i1 i2)
     where ndict = NumDict ((+) :: Integer -> Integer -> Integer)
                           ((-) :: Integer -> Integer -> Integer)
                           ...
    

    签名表明
    a
    可以是任何东西,但这里它应该是或
    Integer
    ,或
    Double
    。Willem:签名不意味着函数可以是任何函数,它接受
    Num
    的任何实例的两个输入并返回同一实例的值吗?与之类似,
    (+)
    的类型签名是exacly
    numa=>a->a->a
    。或者我遗漏了什么?不是每个数字类型都是
    整数
    ,或者
    双精度
    。当然可以,但您能否详细说明一下,当程序从未调用任何其他
    整数
    双精度
    时,为什么会导致类型检查错误?我的意思是,它不像
    map
    无法编译,因为它接受类型为
    (a->b)
    的函数,可以是任何类型。这里一定发生了其他事情,对吗?“运行时需要准备好实例化提供的函数”。。。你这是什么意思?给人的印象是运行时没有什么特别的事情发生。类型erasure@BenjaminHodgson类型擦除会发生,但不会擦除类型类约束。大约,
    calculate
    的第一个参数是一个函数,它为
    numa
    获取一个字典,然后再获取两个参数。字典在运行时由
    calculate
    选择并传递。我给答案加了一些解释。嗯,我还是觉得这个句子有点误导。字典在生成的代码中是清单。运行时系统本身不需要知道任何关于
    RankNTypes
    或类型类的信息。“它只是传递论点。”本雅米尼奥格森说,这句话的措辞确实很糟糕。我不是说运行时需要这样做,但它需要在运行时完成。