可以认为这种情况如下: 应用程序动态加载模块,或者有一个用户选择的函数列表等。我们有一种机制来确定某个类型是否能成功地使用该模块中的函数。所以现在我们要调用该函数。我们需要强迫它拨打电话。该函数可以采用具体类型,也可以采用多态类型,下面只是一个类型类约束,我遇到了它的问题。
以下代码会导致以下错误。我认为可以通过指定具体类型来解决,但我不想这样做。该代码适用于作为类实例的任何类型。指定具体类型会破坏目的。
这是模拟程序的一部分,它不了解另一部分,并且不知道它正在处理的类型。我有一个单独的机制,允许我确保类型匹配正确,发送的值实际上是类型类的实例。这就是为什么在这种情况下,我不介意使用unsafeCoerce。但基本上我需要一种方法来告诉编译器我确实知道它没问题并且无论如何都要这样做,即使它不足以进行类型检查。
{-# 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
运行doTest1
[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
运行doTest2
[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
答案 0 :(得分:2)
我认为可以通过指定具体类型来解决,但我不想这样做。
unsafeCoerce
无法解决这个问题。在这种特殊情况下,编译器无法推断unsafeCoerce
的类型,因为test
仍然是多态的。即使只有HasString
的一个实例,类型系统也不会使用该事实来推断类型。
我没有足够的信息来了解您对此模式的特定应用,但我相对确定您需要重新考虑在程序中使用类型系统的方式。但如果你真的想这样做,你可能想要调查Data.Typeable
而不是unsafeCoerce
。
答案 1 :(得分:1)
稍微修改您的数据类型:
data Hider = forall a. HasString a => Hider a
以明显的方式使它成为类型类的实例:
instance HasString Hider where
toString (Hider x) = toString x
然后这应该可行,而不使用unsafeCoerce
:
doTest3 :: Hider -> IO ()
doTest3 hh = print $ toString hh
这意味着如果Hider
没有实现HasString
,您就不能再将值放入{{1}},但这可能是一件好事。
这个模式可能有一个名字,但是我无法想到它是什么样的。