什么是类型:匹配m s = m == fromSing s?

时间:2016-03-28 05:32:41

标签: haskell singleton-type

使用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

2 个答案:

答案 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命令查找'KProxySing:info的签名来拼凑所有内容。

答案 1 :(得分:1)

麻烦的是你正在使用(基本上)一种类,但你并没有正确地确定种类。特别是,'KProxy的三次出现都可以引用不同的种类。一种常见的方法是使用等式约束命名代理,并重复使用该名称。与为每个代理提供签名相比,这节省了一些打字:

matches :: (kproxy ~ ('KProxy :: KProxy k),
            Eq (DemoteRep kproxy),
            SingKind kproxy)
        => DemoteRep kproxy
            -> Sing (a :: k) -> Bool

这清楚地表明代理应该都是相同的,特别是应该与传递给Sing的类型具有相同的类型。