是什么原因造成这种类型的歧义?

问题描述 投票:3回答:1

我有两个比较简单的类。MSet:

{-# Langage MultiParamTypeClasses #-}

-- A generalization of G set to Magmas
class MSet a b where
  (+>>) :: a -> b -> a

instance MSet Integer Integer where
  (+>>) = (+)

-- (+>>) constrained to a Magma
(<<+>>) ::
  ( MSet a a
  )
    => a -> a -> a
(<<+>>) = (+>>)

当我启动ghci并尝试测试这些时,我遇到了一个问题。

*Main> 1 <<+>> 2
3
*Main> 1 +>> 2

<interactive>:31:1: error:
    • Could not deduce (MSet a b0)
      from the context: (MSet a b, Num a, Num b)
        bound by the inferred type for ‘it’:
                   forall a b. (MSet a b, Num a, Num b) => a
        at <interactive>:31:1-7
      The type variable ‘b0’ is ambiguous
    • In the ambiguity check for the inferred type for ‘it’
      To defer the ambiguity check to use sites, enable AllowAmbiguousTypes
      When checking the inferred type
        it :: forall a b. (MSet a b, Num a, Num b) => a

(+>>) 当它被限制在一个 Magma 却时时含糊不清。

现在我可以做了。

*Main> :set -XFlexibleContexts
*Main> 1 +>> (2 :: Integer)
3

但我不明白这是怎么回事 也不明白为什么这个注解会有帮助 我真的不明白类型检查器是如何消除 (<<+>>). 如果我再添加一个实例,比如说 Int它继续发挥作用,即使它似乎是它应该是模糊的。1 是一个 IntInteger.

为什么其中一个会出错,另外两个不会?

haskell type-inference ambiguous-types
1个回答
1
投票

本质上--你要求GHC解决的是 "什么是类型的 12 言下之意 1 +>> 2而你的类型意味着答案是模棱两可的。

深入

<<+>>

什么类型的 1 <<+>> 2? 为什么?(Num a, MSet a a) => a 当然,因为GHC必须能够将字面值转化为数值(Num)然后 +>> 他们(MSet)的类型签名,以及 <<+>> 说,这两个字元将具有相同的类型。

当你要求GHCi打印出 1 <<+>> 2? 它试图默认 aInteger,它的成功是因为 Num IntegerMSet Integer Integer. 然后,它将评估类型缺省的表达式。

这就是 Int 实例并没有引入歧义--GHCi并没有试图推断要使用的具体实例,而是推断类型,然后默认变量,只留下实例检查。

+>>

什么是类型的 1 +>> 2? 嗯... (Num a, Num b, MSet a b) => a...,似乎是。显然,你需要 MSet但不再有保证说 ab 统一。更不幸的是。b 没有出现在术语的类型中--这就是类型歧义的来源。类型系统不知道选择哪种类型。b 来使用。

当你要求GHCi打印出 1 +>> 2? 它先是推断术语的类型,然后得到上面的类型--现在它在尝试默认之前就遇到了类型推断错误。aInteger.

为什么修复的方法有效?

添加类型信息可以防止错误

> 1 +>> (2 :: Integer) -- fine

因为这些变化消除了《公约》的模糊性。b. GHC不需要推断 b,所以它不会引起推理错误。

奇怪的是,我并不完全理解其中的原因,在为 a 似乎也可以防止错误的发生

> (1 :: Integer) +>> 2 -- fine
> (1 +>> 2) :: Integer -- fine

虽然我怀疑这是另一个GHCi特有的技巧,默认为 bInteger(1 +>>) :: (Num b, MSet Integer b) => b -> Integer. 不过,不要引用我的话。

有了基金

可以使用以下方法消除类型歧义 FunctionalDependencies

class MSet a b | a -> b where
  ...

虽然这似乎不符合你的用例。这解决了推理问题,因为在 (Num a, Num b, MSet a b) => a,知道 a 就可以推断出 b 基金会在 MSet. 后来,当GHCi默认为 aInteger,它可以只查找类型的 b.

© www.soinside.com 2019 - 2024. All rights reserved.