Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/haskell/8.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Haskell 为什么可以';GHC类型检查此函数是否涉及多态性和存在类型?_Haskell_Types - Fatal编程技术网

Haskell 为什么可以';GHC类型检查此函数是否涉及多态性和存在类型?

Haskell 为什么可以';GHC类型检查此函数是否涉及多态性和存在类型?,haskell,types,Haskell,Types,我有一些Haskell代码无法编译(使用GHC 8.0.2)。我想我理解这个基本问题,但我想更好地理解它,这样我以后就可以避免这个问题了 我的库与此类似: {-# language TypeFamilyDependencies #-} {-# language GADTs #-} {-# language RankNTypes #-} module Lib where type Key = Int class Handle m where type Connection m = c

我有一些Haskell代码无法编译(使用GHC 8.0.2)。我想我理解这个基本问题,但我想更好地理解它,这样我以后就可以避免这个问题了

我的库与此类似:

{-# language TypeFamilyDependencies #-}
{-# language GADTs #-}
{-# language RankNTypes #-}

module Lib where

type Key = Int

class Handle m where
    type Connection m = c | c -> m
    withConnection :: Connection m -> m a -> IO a

class (Handle m) => Data m where
    getKeyVal :: Key -> m String

data SomeConn where
    SomeConn :: (Data m) => Connection m -> SomeConn

useConnection :: SomeConn -> (forall m. Data m => m String) -> IO String
useConnection (SomeConn c) action = withConnection c action
其思想是,
datam
表示一类类似于
ReaderT(connectionm)IO
的monad。我希望用这个typeclass的方法编写泛型函数,并通过使用
SomeConn
(在运行时选择)包装的连接类型来确定确切的方法实例

现在输入以下代码

getKeyValWith :: SomeConn -> Key -> IO String
getKeyValWith c = (useConnection c). getKeyVal
给出了GHC 8.0.2中的以下错误:

• Couldn't match type ‘m0 String’
                 with ‘forall (m :: * -> *). Data m => m String’
  Expected type: m0 String -> IO String
    Actual type: (forall (m :: * -> *). Data m => m String)
                 -> IO String
• In the first argument of ‘(.)’, namely ‘useConnection c’
  In the expression: useConnection c . getKeyVal
  In an equation for ‘getKeyValWith’:
      getKeyValWith c = useConnection c . getKeyVal
奇怪的是,以下方法效果很好:

getKeyValWith c k = useConnection c (getKeyVal k)
不那么令人惊讶的是,这一点也是如此:

getKeyValWith (SomeConn c) = withConnection c . getKeyVal
是否有一个简单的规则来理解为什么GHC不喜欢第一个示例,但其他示例可以?当GHC试图编译第一个定义时,有没有办法向GHC询问更多关于它在做什么的信息?我理解这可能不是惯用的Haskell(有人称之为“存在/类型类反模式”)

编辑:


我应该补充的是,即使在第一个示例中显式添加类型
getKeyVal::Key->(Data m=>m String)
,我也会遇到同样的问题。我甚至可以用我选择的类型签名(类型检查)给这个函数起自己的名字,但是我得到了同样的错误。但我现在看到,即使我显式地添加类型,在GHCI中运行
:t
(带有
-XRankNTypes
),它也会返回浮动到左侧的原始类型。所以我想我理解GHC为什么对我犹豫不决。我可以强制GHC使用我选择的类型吗?

这是关于
的。它无法在函数之间传递多态参数,因此
f。如果
f
为秩2多态性,则g
不起作用。请注意以下工作:

(~.) :: ((∀ m. Data m => m String) -> z) -> (x -> (∀ m. Data m => m String))
          -> x -> z
(~.) f g x = f (g x)

getKeyValWith :: SomeConn -> Key -> IO String
getKeyValWith c = useConnection c ~. getKeyVal
理想情况下,
的类型如下

(.) :: ∀ c . ((∀ y . c y => y) -> z) -> x -> ((∀ y . c y => y) -> x)
               -> x -> z
从而涵盖所有特殊情况,如上述
~。
。但这是不可能的——在传统情况下,
cy=y~y需要推断出在任何给定情况下拾取的最薄弱的约束条件
c
–我很确定这在一般情况下是不可计算的

(一个有趣的问题是,如果编译器在类型检查之前尽可能多地内联
,就像现在使用
$
所做的那样,那么我们可以走多远。如果它进行了自动eta扩展,当然可以让
useConnection c.getKeyVal
工作,但是自动eta扩展通常不是一个好的idea、 ……)


通过将多态性参数包装在GADT中隐藏秩2多态性,就像您在
SomeConn
中所做的那样,这是常见的解决方法。

我已经读过,但内容似乎被
($)的讨论所掩盖
。我正在试图了解问题的一般情况,以及如何帮助GHC理解我的代码,或者让GHC帮助我理解问题。
(.getKeyVal)
的类型是
Data m=>(m String->c)->Key->c
useConnection c
的类型是
(对于所有m.Data m=>m String)->IO字符串
。您知道为什么类型为
m String->IO字符串
(对于所有m.Data m=>m String)->IO字符串
无法统一?后两种定义都不需要进行统一。可能会有帮助:这可能是@user2407038的近似副本,我确实看到了这一点,我编辑了我的问题,问了一个新问题:我能强迫GHC在
getKeyVal
上接受不同类型的签名吗?鉴于对我问题的编辑,在我看来,GHC根本不允许我说
getKeyVal::Key->(数据m=>m字符串)
(或者更确切地说,它会说,但GHCI拒绝给我一个带有
:t
的秩2类型)。你会说我的观察只是一个转移视线的问题吗?现在看来,GHC在特定上下文中出现之前,它的确切类型是
getKeyVal
。你总是可以从函数的结果中移出一个通用量词,这就是为什么查询GHCi以获得
:t\:Key->(∀ m.数据m=>m字符串)
给出
∀ m.Data m=>Key->m String
。但是如果类型变量没有出现在参数中,这也可以被反转,GHC实际上会很乐意这样做,所以没有问题。