GHC无法绑定多态功能而不将其单态化

时间:2019-01-21 15:53:53

标签: haskell rank-n-types

在原始代码中,可以看到我只是将一个表达式提取到绑定中,这是haskell声称应该总是可能的基本事情之一。

最小情况(在freenode上@dminuoso的帮助下创建)

我希望g保持多态性(以便可以将其提供给其他期望它的函数):

{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE ScopedTypeVariables #-}

main = do
    let a :: forall t. Functor t => t () = undefined
    let g :: forall u. Functor u => u () = a
    pure ()

错误:

source_file.hs:6:44:
    Couldn't match expected type ‘forall (u :: * -> *).
                                  Functor u =>
                                  u ()’
                with actual type ‘t0 ()’
    When checking that: t0 ()
      is more polymorphic than: forall (u :: * -> *). Functor u => u ()
    In the expression: a

原始问题(动机)发布在#haskell IRC上:

{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE ScopedTypeVariables #-}


class Monad m => Monad' m where

instance Monad' IO where

f1 :: (forall m . Monad' m => m Int) -> IO Int
f1 g = do
    let a :: forall n . Monad' n => n Int = g
    a

-- this works
--f1 :: (forall m . Monad' m => m Int) -> IO Int
--f1 g = do
--    g

main = print $ "Hello, world!"

但是我得到了

source_file.hs:12:45:
    Couldn't match expected type ‘forall (n :: * -> *).
                                  Monad' n =>
                                  n Int’
                with actual type ‘m0 Int’
    When checking that: m0 Int
      is more polymorphic than: forall (n :: * -> *). Monad' n => n Int
    In the expression: g

基于https://ghc.haskell.org/trac/ghc/ticket/12587,我尝试显式应用类型以帮助类型检查器:

{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeApplications #-}

class Monad m => Monad' m where

instance Monad' IO where

f1 :: (forall m . Monad' m => m Int) -> IO Int
f1 g = do
    let a :: forall n . Monad' n => n Int = g @n
    a

main = print $ "Hello, world!"

但是我得到:

main.hs:13:48: error: Not in scope: type variable ‘n’

4 个答案:

答案 0 :(得分:1)

freenode上的@Lears指出,将类型声明放在之上,该声明对其进行了修复,因此可能是GHC中的某种错误。即

{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE ScopedTypeVariables #-}

main = do
    let a :: forall t. Functor t => t () = undefined
    let g :: forall u. Functor u => u () = a
    putStrLn "success"

失败

但是

{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE ScopedTypeVariables #-}

main = do
    let a :: forall t. Functor t => t () = undefined
    let g :: forall u. Functor u => u ()
        g = a
    putStrLn "success"

成功。

答案 1 :(得分:1)

documentation on pattern type signatures的相关位是这样的:

  

与表达式和声明类型签名不同,模式类型签名不是隐式概括的。

因此,模式签名(如您所写的)是一种特殊情况,即泛化(这是将单态类型t提到范围外的类型变量a转换为多态类型forall a. t)未完成。您可以使用任何其他形式的类型签名来恢复一般化过程。例如代替

let foo :: forall a. a = undefined

尝试:

let foo :: forall a. a
    foo = undefined

答案 2 :(得分:1)

第1部分

善良

写作时会发生什么

a :: forall f. Functor f => f ()
a = _

?具体来说,GHC正在寻找哪种类型的表达式来填补空缺(_)?您可能会认为它正在寻找forall f. Functor f => f (),但这并不完全正确。相反,a实际上更像一个函数,GHC隐式地插入两个参数:一个名为f且类型为* -> *的类型参数,以及一个约束{{1}的实例。 }(未命名,就像所有实例一样)。

Functor f

GHC在a :: forall f. Functor f => f () a @f {Functor f} = _ -- @-syntax is a currently proposed extension, {}-syntax is fake type f :: * -> *; instance Functor f)的上下文中寻找。这与在{em> f ()的上下文中寻找(A -> B) -> C -> D和寻找D 之间是一样的。如果我直接拥有f :: A -> B; c :: C,则在第一种情况下,我只能写solution :: (A -> B) -> C -> D,但是在第二种情况下,我必须写出solution

坏人

这不是你写的时候发生的事情

solution f c

由于您使用的是模式类型签名,而不是普通的模式签名,因此GHC不再为您隐式绑定类型变量和实例。现在,GHC诚实且真实地希望您用a :: forall f. Functor f => f () = _ 来填充_。这很难,我们很快就会看到...

(我真的不认为Daniel Wagner所引用的内容在这里是相关的。我相信这只是指两者之间的差异(当forall f. Functor f => f ()处于打开状态且ScopedTypeVariables不在范围内时) type a隐含的意思是5 :: Num a => a的方式,而5 :: forall a. Num a => a 不是的意思是g :: a -> a = id的方式。)

第2部分

写作时会发生什么

g :: forall a. a -> a = id

?具体来说,它是什么类型?您可能会认为它是undefined ,但这并不完全正确。是的,没错,{em>本身forall a. a的类型为undefined,但是GHC不允许您自己全部写forall a. a。取而代之的是,表达式中undefined的每次出现都始终应用于类型参数。上面的内容被隐式重写为

undefined

,然后创建一个新的统一变量(实际上没有名称)undefined @_a0 。该表达式的类型为_a0。如果我在需要_a0的上下文中使用此表达式,则Int必须等于_a0,并且GHC设置Int,将表达式重写为

_a0 := Int

(由于undefined @Int 可以 set 设置为某种变量,因此称为统一变量。它位于“我们的”内部控制之下。上方_a0 不能” 可以设置为任何值。它是给定的,并且位于“他们”(用户)的外部控制之下,从而使其成为skolem变量。)

第3部分

善良

通常,类型变量的自动绑定和统一变量的自动应用效果很好。例如,这两种方法都可以正常工作:

f
undefined :: forall a. a

因为它们分别扩展为

bot :: forall a. a
bot = undefined
(\@a -> undefined @a) :: forall a. a

中等

这样做的时候

bot :: forall a. a
bot @a = undefined @a

,您使某些非常奇怪的事情发生。如前所述,带有a :: forall f. Functor f => f () = undefined 的模式类型签名不会引入任何内容。 RHS上的forall实际上必须是undefined。统一变量的隐式应用仍然存在:

forall f. Functor f => f ()

a :: forall f. Functor f => f () = undefined @_a0 ,因此undefined @_a0 :: _a0必须成立。因此,GHC必须设置_a0 ~ (forall f. Functor f => f ())。通常,这是一个禁忌,因为这是强制性的多态性:将类型变量设置为包含_a0 := (forall f. Functor f => f ())的类型。但是,在足够陈旧的GHC中,某些功能允许这样做。也就是说,forall的类型不是undefined,而是forall (a :: *). a,其中forall (a :: OpenKind). a允许这种不可思议性。这意味着您的代码将通过

OpenKind

如果你写

a :: forall f. Functor f => f () = undefined @(forall f. Functor f => f ())

,它将不起作用,因为a :: forall f. Functor f => f () = bot bot的魔术酱不同。此外,这在较新的GHC中将不起作用,后者已消除了这种不可思议的强制性多态性。 (我说这是一件好事)。

坏人

您对undefined的定义,即使具有模式签名,的确确实具有所需的类型a。该问题现在位于forall f. Functor f => f ()中:

g
同样,

g :: forall f. Functor f => f () = a 不会绑定gtype f :: * -> *。同时,instance Functor f被应用于一些隐式内容:

a

但是... RHS现在具有类型g :: forall f. Functor f => f () = a @_f0 {_} ,而LHS希望它具有类型_f0 ()。这些看起来不一样。因此,输入错误。

由于您不能停止将forall f. Functor f => f ()隐式应用到类型变量而只写a,因此必须在g = a中允许类型变量的隐式绑定:

g

这有效。

答案 3 :(得分:-1)

如果我打了一个打孔,GHC会给我这个:

foo.hs:11:45: error:
    • Cannot instantiate unification variable ‘t0’
      with a type involving foralls:
        forall (b :: * -> *). Monad' b => b Int
        GHC doesn't yet support impredicative polymorphism
    • In the expression: _
      In a pattern binding:
        a :: forall (b :: * -> *). Monad' b => b Int = _
      In the expression:
        do let a :: forall b. Monad' b => b Int = _
           a
   |
11 |     let a :: forall b . Monad' b => b Int = _
   |          

鉴于评论GHC doesn't yet support impredicative polymorphism,您可能无法尝试。