【问题标题】:GHC type inference woesGHC 类型推断问题
【发布时间】:2012-02-15 21:49:23
【问题描述】:

问题。有没有办法让这段代码在没有显式类型签名的情况下工作?

代码。首先,我有一个实践中更好的替代MonadTrans 课程,灵感来自Data.Newtype。看起来是这样的,

{-# LANGUAGE FlexibleContexts, TypeFamilies #-}

module Alt.Control.Monad.Trans where

import Control.Monad

class (Monad ????, Monad (BaseMonad ????)) => MonadTrans (???? :: * -> *) where
    type BaseMonad ???? :: * -> *
    lift :: (BaseMonad ????) α -> ???? α

然后,我有一个带有方法foo 的类A,如果某个基本单子MA,那么任何转换后的单子T M 也是一个A。在代码中,

class A ???? where
    foo :: String -> ???? ()

instance (A (BaseMonad ????), MonadTrans ????) => A ???? where
    foo n = lift $ foo n

但是,如果我现在想为 foo 创建一个快捷方式并替换它的第一个参数,那么我需要一个显式类型签名,否则编译器的上下文堆栈会溢出。

minimize_call :: A ???? => ???? ()
minimize_call = foo "minimize"

有助于推理的可能信息。假设我们有一个关联类型B :: * -> *。我在想我想告诉编译器B 满足B t /= tB (B t) /= B t 等,即B 在某种程度上是“单调的”——追逐关联类型相当于删除新类型包装器,它应该知道它不能永远删除 newtype 包装器,因此需要将上下文 A 添加到签名中。

【问题讨论】:

  • 抱歉,我应该费心记住 为什么 我切换到备用 MonadTrans ... 现在,假设它产生更清晰的代码,但我认为有一个更重要的原因。
  • 有趣的问题。你为什么不想要一个显式的类型签名呢? minimize_call 不是必须是某个固定值,而不是多态常量(或者也许你可以让它成为多态,我不确定)?如果它有一些单一的固定类型,我宁愿记录它,如果没有,我宁愿记录那个。强迫读者在头脑中进行整个程序分析以找出minimize_call 的类型似乎有点适得其反。
  • @Ben,确实,在这种情况下,拥有minimize_call 的类型签名是一种很好的做法。但是,类型推断被破坏表明出现了问题(设计、编译器或与编译器的通信),并且可能会导致问题,更不用说难以理解的错误消息了。

标签: haskell ghc type-inference typeclass associated-types


【解决方案1】:

是的,有办法。为A 提供一个接地实例,并将NoMonomorphismRestriction 添加到语言杂注中(除了还必需的FlexibleInstancesUndecidableInstances)。

但是,A 类将无法使用。编译器无法知道永远不会有带有BaseMonad m = mMonadTrans 实例。因此它永远无法选择一个实例,因为它不知道是使用这里的实例还是另一个实例。

{-# LANGUAGE FlexibleContexts, TypeFamilies, FlexibleInstances, UndecidableInstances, NoMonomorphismRestriction #-}

module Trans (MonadTrans(..), A(..), minimize_call) where

import Control.Monad

class (Monad m, Monad (BaseMonad m)) => MonadTrans (m :: * -> *) where
    type BaseMonad m :: * -> *
    lift :: (BaseMonad m) α -> m α

class A m where
    foo :: String -> m ()


data Foo a = Bork

instance Monad Foo where
    return _ = Bork
    _ >>= _ = Bork

instance A Foo where
    foo _ = Bork


instance (A (BaseMonad m), MonadTrans m) => A m where
    foo n = lift $ foo n

-- minimize_call :: A m => m ()
minimize_call = foo "minimize"

与 ghc 6.12、7.0、7.2 和 7.4 一起编译。没有签名,minimize_call 必须得到一个单态类型,除非 MR 被关闭。这无论如何都行不通,因为约束A m 是不可默认的。因此,必须关闭 MR。但是随后类型检查器仍然追逐自己的尾巴,试图证明约束是可满足的。只有提升实例,它不能。如果你提供一个锚点,它就可以。

但是提供类型签名要好得多。

【讨论】:

  • 谢谢,但我不希望模块中有接地实例。事实上,可能有多个接地实例,这取决于我的编译器有多少后端,并且假设使用一个特定的后端是不可取的。我已经打开了FlexibleInstancesFlexibleContextsNoMonomorphismRestriction
  • 您可以使用未导出的虚拟单子,请参阅更新。 minimize_call 的推断类型是A m => m (),应该是,它不排除在其他地方使用不同的接地实例,只需要让类型检查器终止即可。
  • 实际上,你不能使用minimize_call,或者foo。带或不带类型签名。
  • 嗯,将看看您的“A 将无法使用”断言。我一直在成功使用这种模式,但可能存在一些细微的差异,或者我没有尝试通过A 的多个基本实例使其运行。
猜你喜欢
  • 2023-03-17
  • 2016-04-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多