例如,在GHCi中键入:t ap
会得到结果
ap :: Monad m => m (a -> b) -> m a -> m b
如果我已经知道我要使用的Monad实例是((->) r)
,那么如何查询该特定实例的ap
类型?
答案 0 :(得分:12)
正如Lazersmoke所说,您可以使用GHC 8.0中引入的TypeApplications扩展名。
在GHCi中:
λ > :set -XTypeApplications
λ > import Control.Monad
λ > :t ap @((->) _)
ap @((->) _) :: (t -> a -> b) -> (t -> a) -> t -> b
答案 1 :(得分:10)
您可以使用visible type application功能指定参数类型。您可以以更具创造性的方式查看函数: 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:您实际上可以使用占位符而不是类型变量(请参阅another answer)。但是如果你想指定确切的名字,请使用上面的方法。
λ: :t ap @((->) _)
ap @((->) _) :: (t -> a -> b) -> (t -> a) -> t -> b
/ ADVANCED SECTION
关于这种方法还有一点要说:如果你的函数有多个类型参数并且你想指定一个参数,你应该做更多的事情。类型从左到右逐个传递,就像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
中显示为第三个参数,但应该作为第二个传递但是我我还没有意识到所有的魔法。
答案 2 :(得分:2)
这只是一个黑客,但你总是可以这样做:
: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