如何获取特定类型类实例的多态函数类型?

时间:2017-03-24 12:12:07

标签: haskell ghci lambdabot

例如,在GHCi中键入:t ap会得到结果

ap :: Monad m => m (a -> b) -> m a -> m b

如果我已经知道我要使用的Monad实例是((->) r),那么如何查询该特定实例的ap类型?

3 个答案:

答案 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"的内容,但这不适用于类型应用。你需要知道两件事:

  1. 类型顺序。
  2. 如何指定所需类型。
  3. 考虑下一个功能:

    λ: :t lift
    lift :: (Monad m, MonadTrans t) => m a -> t m a
    

    我们希望能够指定mt类型变量。由于 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
    

    嗯,这对我来说真的很神秘,为什么mforall中显示为第三个​​参数,但应该作为第二个传递但是我我还没有意识到所有的魔法。

答案 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