Haskell 如何解决此编译错误:不明确的类型变量'a1';在约束中

Haskell 如何解决此编译错误:不明确的类型变量'a1';在约束中,haskell,types,Haskell,Types,人们可以这样想: 应用程序动态加载一个模块,或者有一个功能列表供用户选择,等等。我们有一种机制来确定某个类型是否能成功地使用该模块中的某个功能。现在我们要调用这个函数。我们需要强迫它打电话。该函数可以采用具体的类型,也可以采用多态的类型,下面的例子就是我遇到问题的类型类约束 以下代码导致以下错误。我认为可以通过指定具体类型来解决这个问题,但我不想这样做。该代码用于处理作为该类实例的任何类型。指定一个具体类型无法达到目的 这是在模拟一个程序的一部分,它不知道另一部分,也不知道它所处理的内容的类型。

人们可以这样想: 应用程序动态加载一个模块,或者有一个功能列表供用户选择,等等。我们有一种机制来确定某个类型是否能成功地使用该模块中的某个功能。现在我们要调用这个函数。我们需要强迫它打电话。该函数可以采用具体的类型,也可以采用多态的类型,下面的例子就是我遇到问题的类型类约束

以下代码导致以下错误。我认为可以通过指定具体类型来解决这个问题,但我不想这样做。该代码用于处理作为该类实例的任何类型。指定一个具体类型无法达到目的

这是在模拟一个程序的一部分,它不知道另一部分,也不知道它所处理的内容的类型。我有一个单独的机制,允许我确保类型正确匹配,发送的值实际上是类型类的实例。这就是为什么在这种情况下,我不介意使用不安全的资源。但基本上,我需要一种方法来告诉编译器,我确实知道它是可以的,并且无论如何都要这样做,即使它不知道足够的内容来进行类型检查

{-# LANGUAGE ExistentialQuantification, RankNTypes, TypeSynonymInstances #-}
module Main where

import Unsafe.Coerce

main = do
  --doTest1 $ Hider "blue"
  doTest2 $ Hider "blue"

doTest1 :: Hider -> IO ()
doTest1 hh@(Hider h) =
  test $ unsafeCoerce h

doTest2 :: Hider -> IO ()
doTest2 hh@(Hider h) =
  test2 hh

test :: HasString a => a -> IO ()
test x = print $ toString x

test2 :: Hider -> IO ()
test2 (Hider x) = print $ toString (unsafeCoerce x)

data Hider = forall a. Hider a

class HasString a where
  toString :: a -> String

instance HasString String where
  toString = id
跑步测试1

[1 of 1] Compiling Main             ( Test.hs, Test.o )

Test.hs:12:3:
    Ambiguous type variable `a1' in the constraint:
      (HasString a1) arising from a use of `test'
    Probable fix: add a type signature that fixes these type variable(s)
    In the expression: test
    In the expression: test $ unsafeCoerce h
    In an equation for `doTest1':
        doTest1 hh@(Hider h) = test $ unsafeCoerce h
跑步测试2

[1 of 1] Compiling Main             ( Test.hs, Test.o )

Test.hs:12:3:
    Ambiguous type variable `a1' in the constraint:
      (HasString a1) arising from a use of `test'
    Probable fix: add a type signature that fixes these type variable(s)
    In the expression: test
    In the expression: test $ unsafeCoerce h
    In an equation for `doTest1':
        doTest1 hh@(Hider h) = test $ unsafeCoerce h
我认为可以通过指定具体类型来解决这个问题,但我不想这样做

尽管有了
unsafeccerce
,这是没有办法解决的。在这种特殊情况下,编译器无法推断
unsafeccerce
的类型,因为
test
仍然是多态的。即使只有一个
hastring
实例,类型系统也不会使用该事实来推断类型

关于这个模式的具体应用,我没有足够的信息,但我相对确信,您需要重新思考在程序中使用类型系统的方式。但是如果您真的想这样做,您可能需要查看
Data.Typeable
,而不是
unsafeccerce

我认为可以通过指定具体类型来解决这个问题,但我不想这样做

尽管有了
unsafeccerce
,这是没有办法解决的。在这种特殊情况下,编译器无法推断
unsafeccerce
的类型,因为
test
仍然是多态的。即使只有一个
hastring
实例,类型系统也不会使用该事实来推断类型


关于这个模式的具体应用,我没有足够的信息,但我相对确信,您需要重新思考在程序中使用类型系统的方式。但是,如果您真的想这样做,您可能需要查看
数据.Typeable
,而不是
unsafeccerce

稍微修改您的数据类型:

data Hider = forall a. HasString a => Hider a
以明显的方式使其成为类型类的实例:

instance HasString Hider where
    toString (Hider x) = toString x
那么这应该可以工作,而不需要使用
不安全的强制措施

doTest3 :: Hider -> IO ()
doTest3 hh = print $ toString hh
这意味着您不能再将值放入
Hider
,如果它不实现
hastring
,但这可能是一件好事


这个模式可能有一个名字,但我想不出它到底是什么。

稍微修改一下您的数据类型:

data Hider = forall a. HasString a => Hider a
以明显的方式使其成为类型类的实例:

instance HasString Hider where
    toString (Hider x) = toString x
那么这应该可以工作,而不需要使用
不安全的强制措施

doTest3 :: Hider -> IO ()
doTest3 hh = print $ toString hh
这意味着您不能再将值放入
Hider
,如果它不实现
hastring
,但这可能是一件好事


这种模式可能有一个名字,但我想不出它到底是什么。

对不起,这对我不起作用。这是一个过于简单的例子。在完整版本中,任何内容都可能被隐藏起来,但是您试图将非HaString类型强制为HaString类型——这将不可避免地导致Haskell设计用来避免的问题(如segfaults)。“unsafeccerce”中的“unsafeccerce”实际上代表“在没有自己证明这是安全的情况下使用它是不安全的”,而你是说你有证据证明它是不安全的。不要这样做。我在哪里说过我有证据证明它不安全?我想我说过我证明它是安全的。对不起,那对我不起作用。这是一个过于简单的例子。在完整版本中,任何内容都可能被隐藏起来,但是您试图将非HaString类型强制为HaString类型——这将不可避免地导致Haskell设计用来避免的问题(如segfaults)。“unsafeccerce”中的“unsafeccerce”实际上代表“在没有自己证明这是安全的情况下使用它是不安全的”,而你是说你有证据证明它是不安全的。不要这样做。我在哪里说过我有证据证明它不安全?我想我说过我证明了它是安全的。在真实的案例中,HasString不一定只有一个实例。我不明白你对Data.Typeable的建议是什么。那么,理论上不可能简单地推断出
不安全的类型h
是具体的,对吧?;)关于
Data.Typeable
:我自己从未使用过它,但我知道它可以帮助进行动态转换,这似乎是您想要做的。啊,对不起
Data.Dynamic
执行动态键入,并使用Typeable执行此操作。您可能想了解一下:)类型类的分派只是静态完成的问题吗?所以,在任何有类型类调用的地方,它必须能够在编译时解析为具体的类型,以便分派该调用?是否无法根据值的类型在运行时进行调度?我不认为数据。动态将帮助我,因为我认为它只适用于具体的类型。@taotree不,类型类分派是动态完成的。但是,必须有一个静态kn