使用singletons库,这个简单的函数可以编译和工作。但是,ghci和ghc不同意它的类型签名,如果他们的任何一个建议被粘贴到代码中,它将无法编译。
GHC会接受哪种类型的签名? ghc-7.10.3,singletons-2.0.1
{-# LANGUAGE DataKinds, PolyKinds, TypeOperators, TypeFamilies, GADTs, UndecidableInstances, FlexibleContexts #-}
import Data.Proxy (KProxy(..))
import Data.Singletons
import Data.Singletons.Prelude
-- ghc rejects this type signature, but if I leave it off, ghci :t shows exactly this.
matches :: (Eq (DemoteRep 'KProxy), SingKind 'KProxy) => DemoteRep 'KProxy -> Sing a -> Bool
matches m s = m == fromSing s
t :: Sing True
t = sing
demo :: Bool
demo = matches True t
GHC的上述类型签名错误:
Couldn't match type ‘DemoteRep 'KProxy’ with ‘DemoteRep 'KProxy’
NB: ‘DemoteRep’ is a type function, and may not be injective
The kind variable ‘k2’ is ambiguous
Use -fprint-explicit-kinds to see the kind arguments
Expected type: DemoteRep 'KProxy -> Sing a -> Bool
Actual type: DemoteRep 'KProxy -> Sing a -> Bool
In the ambiguity check for the type signature for ‘matches’:
matches :: forall (k :: BOX)
(k1 :: BOX)
(k2 :: BOX)
(k3 :: BOX)
(a :: k3).
(Eq (DemoteRep 'KProxy), SingKind 'KProxy) =>
DemoteRep 'KProxy -> Sing a -> Bool
To defer the ambiguity check to use sites, enable AllowAmbiguousTypes
In the type signature for ‘matches’:
matches :: (Eq (DemoteRep KProxy), SingKind KProxy) =>
DemoteRep KProxy -> Sing a -> Bool
ghc -Wall
建议使用不同类型的签名,但BOX
内容也不会被接受:
Top-level binding with no type signature:
matches :: forall (k :: BOX) (a :: k).
(Eq (DemoteRep 'KProxy), SingKind 'KProxy) =>
DemoteRep 'KProxy -> Sing a -> Bool
答案 0 :(得分:6)
'KProxy
与价值级别的Proxy
类似:它有一个幻像。就像我们在价值级别使用幻像类型Proxy :: Proxy a
编写a
一样,我们必须启用种类签名,并在类型级别使用幻像类'KProxy :: KProxy k
编写k
。希望这个比喻具有一定的意义。这是它的样子:
{-# LANGUAGE DataKinds, PolyKinds, TypeOperators, TypeFamilies, GADTs, UndecidableInstances, FlexibleContexts, KindSignatures #-}
import Data.Proxy (KProxy(..))
import Data.Singletons
import Data.Singletons.Prelude
matches ::
( Eq (DemoteRep ('KProxy :: KProxy k))
, SingKind ('KProxy :: KProxy k)
) => DemoteRep ('KProxy :: KProxy k) -> Sing (a :: k) -> Bool
matches m s = m == fromSing s
此类型变量k
将同时出现在DemoteRep ...
和Sing ...
中,这样我们就可以输入m == fromSing s
。
GHCi,虽然很甜,而且通常很聪明,但不知道类型签名需要"另一个层次的间接"并且需要一个引入的类变量。
我会在这里提醒大多数意见,-fprint-explicit-kinds
是有帮助的:
λ> :t matches
matches
:: (Eq (DemoteRep * ('KProxy *)), SingKind * ('KProxy *)) =>
DemoteRep * ('KProxy *) -> Sing * a -> Bool
对我而言,并不是真正指出这里发生了什么。我只能通过使用方便的DemoteRep
命令查找'KProxy
,Sing
和:info
的签名来拼凑所有内容。
答案 1 :(得分:1)
麻烦的是你正在使用(基本上)一种类,但你并没有正确地确定种类。特别是,'KProxy
的三次出现都可以引用不同的种类。一种常见的方法是使用等式约束命名代理,并重复使用该名称。与为每个代理提供签名相比,这节省了一些打字:
matches :: (kproxy ~ ('KProxy :: KProxy k),
Eq (DemoteRep kproxy),
SingKind kproxy)
=> DemoteRep kproxy
-> Sing (a :: k) -> Bool
这清楚地表明代理应该都是相同的,特别是应该与传递给Sing
的类型具有相同的类型。