当我在构造函数中创建一个带有符号的新类型时,如果启用了IncoherentInstances,则只有在编译时填充符号时才会选择该类型的正确实例....
{-# LANGUAGE DataKinds, GADTs, KindSignatures, FlexibleInstances, IncoherentInstances #-}
import GHC.TypeLits
data Object:: Symbol -> * where
Object :: Object sy
instance Show (Object "dog") where
show _ = "dog"
instance Show (Object x) where
show _ = "other"
main = do
let name = "dog"
print (undefined :: Object "dog") -- outputs "dog", as expected
print (undefined :: Object "cat") -- outputs "other", as expected
print (undefined :: Object name) -- outputs "other", I expected "dog"
有没有办法在运行时提供字符串符号值?如果不允许这样做,为什么它甚至可以编译(即,如果除了默认情况下没有解决任何问题,有人甚至想在第三次打印中使用该赋值?)
答案 0 :(得分:4)
Haskell [GHC]有两个完全独立的名称空间:术语和类型[和种类]。这就是你一直看到这种事情的原因:
data Foo a = Foo a | Bar Int
声明(除其他外)两个单独的名称Foo
;一个是类型级名称Foo
,它是一个类型构造函数,另一个是术语级名称Foo
,它是一个数据构造函数。同样,在
foo :: a -> a
foo a = a
有两个单独的名称a
:一个是类型级别名称a
,它是一个类型变量,另一个是术语级别名称{{1这是一个术语变量。
两个级别之间的仅交互是键入判断:一个级别的名称可能在上面的级别中具有已知的分类。例如,术语级别的名称a
按类型级别的Just
类型进行分类;然而,即使在这里,名称也不会互动。如果要连接术语级别名称和类型级别名称,则必须执行一些very exotic trickery。
因此,当您撰写a -> Maybe a
时,您应该将其视为let name = "dog" in undefined :: Object name
之类的内容;很明显,let TermLevel.name = "dog" in undefined :: Object TypeLevel.name
没有帮助,你也可以只写let
。由于有很多实例符合这种类型,并且你告诉GHC你并不关心它在这种情况下选择了哪个实例,所以它继续选择了一个。它不是你想要的那个,但这是用不连贯的实例支付的价格。