关联类型没有得到解决,不知道为什么

时间:2016-02-11 01:03:17

标签: haskell

这是一个简化的示例,显示了我得到的错误,我不明白。它还有点长,对不起。

--
--  Test of XMaybe and XMonad
--

{-# LANGUAGE DataKinds #-}
{-# LANGUAGE PolyKinds #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE MultiParamTypeClasses #-}

module Main (main) where

-------------------------------------------------------------------------------

class XMonad (tc :: k -> * -> *) (af :: k) (bf :: k) where
  type XResultT tc af bf :: k
  (>>~)   :: tc af a -> (a -> tc bf b) -> tc (XResultT tc af bf) b

-------------------------------------------------------------------------------

data XMaybe :: Bool -> * -> * where
  XNothing ::      XMaybe False a
  XJust    :: a -> XMaybe True  a

instance XMonad XMaybe False False where
  type XResultT XMaybe False False = False
  (>>~) _ _ = XNothing

instance XMonad XMaybe True False where
  type XResultT XMaybe True False = False
  (>>~) _ _ = XNothing

instance XMonad XMaybe False True where
  type XResultT XMaybe False True = False
  (>>~) _ _ = XNothing

instance XMonad XMaybe True True where
  type XResultT XMaybe True True = True
  (>>~) (XJust x) f = f x

-------------------------------------------------------------------------------

fromXMaybe :: XMaybe t a -> Maybe a
fromXMaybe (XJust x) = Just x
fromXMaybe XNothing  = Nothing

-------------------------------------------------------------------------------

class TestClass a where
  type Enabled a :: Bool
  queryVal :: a -> XMaybe (Enabled a) Int
  queryGet :: a -> XMaybe (Enabled a) (a -> Int)

-------------------------------------------------------------------------------

newtype Test = Test Int  deriving Show

instance TestClass Test where
  type Enabled Test = True
  queryVal (Test x) = XJust x
  queryGet _        = XJust (\(Test x) -> x)

-------------------------------------------------------------------------------

test :: (TestClass a, Show a) => a -> IO ()
test x0 = do
  putStrLn "--------------------"
  putStr   "  x0 = "
  print    x0

  --  This is OK
  let x1 = queryVal x0
  putStr   "  x1 = "
  print $ fromXMaybe x1

  --  This is OK
  let x2 = queryGet x0

  --  This gives a compiler error - (Enabled a) isn't evaluated, so the
  --  instance of XMonad is undetermined, so >>~ is rejected. But AFAICT that
  --  (Enabled a) *should* be evaluated - which TestClass instance doesn't seem
  --  ambiguous...
  --
  --  1.  The instance specifies "type Enabled Test = True"
  --  2.  If it was ambiguous, how come the earlier queryGet call works fine?
  --      Comment out the following two lines and this compiles and runs. Isn't
  --      an ambiguous result type automatically a compile-time error?
  --
  let x3 = queryGet x0 >>~ \get ->
           XJust $ get x0

  putStrLn "--------------------"

-------------------------------------------------------------------------------

main :: IO ()
main = test (Test 3)

-------------------------------------------------------------------------------

以下是GHC 7.10.3 ......

的具体错误
[1 of 1] Compiling Main             ( test03.hs, test03.o )

test03.hs:91:24:
    Could not deduce (XMonad XMaybe (Enabled a) 'True)
      arising from a use of ‘>>~’
    from the context (TestClass a, Show a)
      bound by the type signature for
                 test :: (TestClass a, Show a) => a -> IO ()
      at test03.hs:67:9-43
    In the expression: queryGet x0 >>~ \ get -> XJust $ get x0
    In an equation for ‘x3’:
        x3 = queryGet x0 >>~ \ get -> XJust $ get x0
    In the expression:
      do { putStrLn "--------------------";
           putStr "  x0 = ";
           print x0;
           let x1 = queryVal x0;
           .... }

基本上,由于尚未评估关联类型,因此尚未推断出类实例。但是,触发问题的>>~运算符只是从第一个参数中获取该类型信息。相同的关联类型似乎只对完全相同的类型进行了评估,从queryValqueryGet确定结果类型,只要该结果不作为第一个参数传递给{{ 1}}。

从一些乱七八糟的东西我不久前使用类型级自然功能,我感觉我的>>~函数缺少一个约束 - 类似于test - 但是到目前为止我不能说这个存在。

那么 - 我做错了什么?

来自何处的说明 - KnownBool (Enabled a)XMaybe类似,但具有静态解析的类型级知识,即MaybeXJust是否适用。它可以是XNothing实例,所有法律都受到尊重,它应该消除所有混乱,但它不像Monad那样工作 - 顺序操作必须全部{{1或者所有Maybe,因此混合将是类型错误而不是XJust结果。所以XNothing是一个monad-ish类,它允许一个额外的类型参数变化,XNothing是一个bind-ish运算符。由于歧义问题,没有相应的XMonad - 现在我只是使用>>~构造函数。

由于只有一个实例,return类可能是多余的,并且当然需要更加通用(尽管缺少XJust打破了这种普遍性),但我想要看看类型变化的monadish-things旅程结束的地方 - 可能是IxMonad,可能有类似但略有不同的东西,尽管目前我对XMonad的了解不多于return存在。

与此同时,我陷入了这个编译错误。

根据以下@dfeuer的回答和评论,但未使用IxMonad解决方案,因为我认为KnownBool无论如何应该知道XMaybe ...

Bool

1 个答案:

答案 0 :(得分:2)

问题是GHC指出的。 test中的a是多态的,因此在测试中无法确定Enabled a实际上是什么,因此无法选择{ {1}}实例。你也没有XMonad约束来抽象它,因此它不会编译。你可以很容易地使用那种已知的XMonad"你提到:

Bool

添加这样的上下文可以让您在data SBool (b :: Bool) where SFalse :: SBool 'False STrue :: SBool 'True class KnownBool (b :: Bool) where knownBool :: SBool b instance KnownBool 'False where knownBool = SFalse instance KnownBool 'True where knownBool = STrue 上进行模式匹配,以显示knownBool类型参数,从而显示相应的Bool实例。你甚至可以使用两个XMonad约束为所有可能的东西编写一个XMonad实例。

旁注

如果您永远不会使用某个参数,请将其设为代理:

KnownBool

这样可以更轻松地调用class TestClass a where type Enabled a :: Bool queryVal :: a -> XMaybe (Enabled a) Int queryGet :: proxy a -> XMaybe (Enabled a) (a -> Int) 函数。

即使你想要queryGet的多个实例(我能想到的主要原因是获得某些内联行为),你仍然可以减少它们的数量:

XMonad