例如,输入:t ap
GHCi 会给出结果
ap :: Monad m => m (a -> b) -> m a -> m b
如果我已经知道要使用的 Monad 实例 is ((->) r)
,我如何查询ap
该特定实例的类型?
正如Lazersmoke作为评论所说,您可以使用 GHC 8.0 中引入的TypeApplications扩展。
在 GHCi 中:
λ > :set -XTypeApplications
λ > import Control.Monad
λ > :t ap @((->) _)
ap @((->) _) :: (t -> a -> b) -> (t -> a) -> t -> b
您可以使用可见类型应用程序功能来指定参数类型。您可以以更有创意的方式看待函数:Haskell中的函数不仅可以应用于某些类型的值,还可以应用于该值的类型。但是要传递类型,您应该以某种方式指定(带有前置)您正在传递类型(因为类型还不是Haskell@
中的一等对象)。
所以这里是如何工作的:
λ: :set -XTypeApplications
λ: :t ap @((->) Int)
ap @((->) Int) :: (Int -> a -> b) -> (Int -> a) -> Int -> b
这种方法的唯一限制是您不能在 中使用类型变量ghci
,您应该使用特定类型(Int
而不是r
),但这没什么大不了的。
高级部分
好吧,实际上你可以,但这很棘手:
λ: :set -XExplicitForAll
λ: :set -XPartialTypeSignatures
λ: :set -XScopedTypeVariables
λ: :{
λ| foo :: forall r . _
λ| foo = ap @((->) r)
λ| :}
<interactive>:28:19: warning: [-Wpartial-type-signatures]
• Found type wildcard ‘_’
standing for ‘(r -> a -> b) -> (r -> a) -> r -> b’
λ: :t foo
foo :: (r -> a -> b) -> (r -> a) -> r -> b
UPD:您实际上可以使用占位符而不是类型变量(请参阅另一个答案)。但是,如果您想指定确切的名称,请使用上面的方法。
λ: :t ap @((->) _)
ap @((->) _) :: (t -> a -> b) -> (t -> a) -> t -> b
/高级部分
关于这种方法还有一件事要说:如果你的函数有多个类型参数并且你想指定一个确切的参数,你应该做更多的事情。类型从左到右一一传递,就像某些函数中的简单参数一样,如bar :: Int -> String -> Double
. 如果你想修复bar
你应该写的第一个参数,bar 5
如果你想修复第二个,那么,你可以写类似的东西,\n -> bar n "baz"
但这不适用于类型应用程序。你需要知道两件事:
考虑下一个功能:
λ: :t lift
lift :: (Monad m, MonadTrans t) => m a -> t m a
我们希望能够指定m
和t
键入变量。因为Haskell没有命名类型变量(还)你不能写:t lift {t=MaybeT}
或者很:t lift {m=IO}
遗憾。所以回到两件事。
要查看类型的顺序,您应该使用一些编译器选项。类型参数的顺序由指定forall
,您可以手动执行。否则类型参数将由编译器以某种方式排序。普通人看不到lift
函数类型的顺序,但如果你知道一些高级魔法,你可以:
λ: :set -fprint-explicit-foralls
λ: :t lift
lift
:: forall {t :: (* -> *) -> * -> *} {a} {m :: * -> *}.
(Monad m, MonadTrans t) =>
m a -> t m a
然后你应该使用@_
跳过一些类型:
λ: :t lift @MaybeT
lift @MaybeT
:: forall {a} {m :: * -> *}. Monad m => m a -> MaybeT m a
λ: :t lift @_ @IO
lift @_ @IO
:: forall {t :: (* -> *) -> * -> *} {a}.
MonadTrans t =>
IO a -> t IO a
λ: :t lift @_ @_ @Int
lift @_ @_ @Int
:: forall {t :: (* -> *) -> * -> *} {t1 :: * -> *}.
(Monad t1, MonadTrans t) =>
t1 Int -> t t1 Int
好吧,这对我来说真的很神秘,为什么m
显示为第三个参数,forall
但应该作为第二个参数传递,但我仍然不知道所有的魔法。
这只是一个 hack,但您总是可以执行以下操作:
:t ap . (id :: ((->) r a) -> ((->) r a))
或者
:t \x y -> (id :: ...) (ap x y)
有趣的是
Prelude Control.Monad> type Reader = (->) r
Prelude Control.Monad> :t ap . (id :: Reader r a -> Reader r a)
ap . (id :: Reader r a -> Reader r a)
:: Reader r (a -> b) -> (r -> a) -> r -> b
不同于
Prelude Control.Monad> :t \x y -> (id :: Reader r a -> Reader r a) (ap x y)
\x y -> (id :: Reader r a -> Reader r a) (ap x y)
:: (r -> a1 -> a) -> (r -> a1) -> Reader r a
在 ghc 认为的同义词中Reader r a