【问题标题】:Type inference with GADTs使用 GADT 进行类型推断
【发布时间】:2017-03-16 03:47:01
【问题描述】:

在下面的代码中,我试图匹配 GADT 构造函数 Cons 以让编译器看到 xs 是非空的:

{-# LANGUAGE DataKinds           #-} 
{-# LANGUAGE GADTs               #-}
{-# LANGUAGE KindSignatures      #-} 
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeOperators       #-}

import Data.Typeable

data Foo (ts :: [*]) where
  Nil :: Foo '[]
  Cons :: (Typeable t) => Foo ts -> Foo ( t ': ts)

foo :: Foo xs -> IO ()
foo Nil = print "done"
foo (Cons rest :: Foo (y ': ys)) = do
  print $ show $ typeRep (Proxy::Proxy y)
  foo rest

很遗憾,这个简单的示例无法使用 GHC 8 进行编译:

• Couldn't match type ‘xs’ with ‘y : ys’
  ‘xs’ is a rigid type variable bound by
    the type signature for:
      foo :: forall (xs :: [*]). Foo xs -> IO ()
  Expected type: Foo (y : ys)
    Actual type: Foo xs
• When checking that the pattern signature: Foo (y : ys)
    fits the type of its context: Foo xs
  In the pattern: Cons rest :: Foo (y : ys)
  In an equation for ‘foo’:
      foo (Cons rest :: Foo (y : ys))
        = print $ (show $ typeRep (Proxy :: Proxy y))

我知道使用 GADT 进行类型推断可能会很棘手(例如,#9695#10195#10338),但这如此很简单...

我需要做些什么来让 GHC 相信,当我在 Cons 上匹配时,GADT 参数至少有一个元素?

【问题讨论】:

  • 为什么不简单地拥有foo :: Foo (x ': xs) -> ()
  • 当然这个例子是简化的,但是foo里面确实有一些递归调用。我需要匹配Cons 案例或Nil 案例(省略),当然还有Nil :: Foo '[]。我真的需要 GHC 根据模式匹配找出xs ~ (y ': ys)
  • @Alec 我改进了这个例子来说明我为什么要问我在问什么。当我过度简化示例时,我讨厌它......
  • 谢谢!作为旁注,this 在这里非常有用。 :)

标签: haskell ghc


【解决方案1】:

您只需要一个从Foo (t ': ts) 中提取Proxy t 的函数:

fooFstType :: Foo (t ': ts) -> Proxy t 
fooFstType _ = Proxy 

请注意,由于Foo 的类型参数是t ': ts,而不仅仅是ts,您可以引用表示类型签名中第一个元素的类型变量(而不是在正文中,不知何故, ScopedTypeVariables)。

你的函数变成了

foo :: Foo xs -> IO ()
foo Nil = print "done"
foo f@(Cons rest) = do
  print $ show $ typeRep (fooFstType f)
  foo rest

另一种可能性是将作品移到类型级别:

type family First (xs :: [k]) :: k where 
  First (x ': xs) = x 

foo :: forall xs . Foo xs -> IO ()
foo Nil = print "done"
foo (Cons rest) = do
  print $ show $ typeRep (Proxy :: Proxy (First xs))
  foo rest

【讨论】:

  • 我很惊讶这行得通,但它或多或少是我正在寻找的。它需要第二个功能是多么愚蠢!你知道这种行为是否记录在某处吗?
  • 如果“行为”是指首先发生错误的原因,这是类型检查工作方式的结果。不同的给定类型(Foo xsFoo (y:ys))在函数实际类型检查之前匹配,但当然我们只能在 GADT 模式匹配提供的上下文下统一它们。到统一给定类型时,该模式匹配还没有“发生”。 ScopedTypeVariables 不是解决此问题的正确工具 - 我们需要 visible type applications in patterns
  • SPJ 确认“这就是它的工作原理”here,这不是很令人满意,但似乎是公认的解决方案。双重模式匹配虽然很笨重......
猜你喜欢
  • 1970-01-01
  • 2012-11-13
  • 2022-11-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-01-02
相关资源
最近更新 更多