GHCi 可以在 `:type <expr>` 命令中执行替换吗?

Can GHCi perform substitutions in `:type <expr>` commands?

GHCi :type <expr> 显示表达式的类型:

Prelude> :t (<*>)
(<*>) :: Applicative f => f (a -> b) -> f a -> f b

GHCi 能否在替换任何类型变量后显示结果?例如,让f = ((->) c),然后

:t (<*>) ::  ((->) c) (a->b) -> ((->) c) a -> ((->) c) b
(<*>) ::  ((->) c) (a->b) -> ((->) c) a -> ((->) c) b
  :: (c -> a -> b) -> (c -> a) -> c -> b

有没有办法让 GHCi 计算出这些替换?

是的,有几种方法。许多操作使它们的类型变量成为它们 API 的一部分,您可以通过打开正确的选项并以正确的方式询问来观察这一点。

> :set -fprint-explicit-foralls 
> :t +v (<*>)
(<*>)
  :: forall (f :: * -> *).
     Applicative f =>
     forall a b. f (a -> b) -> f a -> f b

这实际上看起来并没有那么特别,但说明了一些重要的事情:因为 foralld 类型变量没有用大括号括起来,所以它们可以成为类型应用程序的主题。 (但是你应该相信 "braces"/"not braces" 区分 only in :t +v queries!正常 :t munges type-application-availability in一种可能从 GHC 版本更改为 GHC 版本的脆弱方式。)

> :set -XTypeApplications
> :t (<*>) @((->) _)
(<*>) @((->) _)
  :: forall {w} {a} {b}. (w -> a -> b) -> (w -> a) -> w -> b

有时您会发现不能以这种方式使用类型应用程序的术语。例如:

> :t +v \f x -> f <*> x
\f x -> f <*> x
  :: forall {f :: * -> *} {a} {b}.
     Applicative f =>
     f (a -> b) -> f a -> f b

这里的大括号表示没有可用于类型应用的类型。我们运气不好吗?不,我们仍然可以使用类型孔来只填充我们关心的部分,然后向 GHC 询问其余部分。

> :set -XPartialTypeSignatures
> :set -Wno-partial-type-signatures
> :t (\f x -> f <*> x) :: ((_ -> _) -> _)
(\f x -> f <*> x) :: ((_ -> _) -> _)
  :: forall {w} {a} {b}. (w -> a -> b) -> (w -> a) -> w -> b