如何获取特定类型 class 实例的多态函数类型?
How can i get the type of a polymorphic function for a specific type class instance?
例如,在 GHCi 中键入 :t ap
会得到结果
ap :: Monad m => m (a -> b) -> m a -> m b
如果我已经知道我将要使用的 Monad 实例是 ((->) r)
,我如何查询该特定实例的 ap
类型?
这只是一个 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
作为 GHC 8.0 中引入的 Lazersmoke said as a comment you can use the TypeApplications 扩展。
在 GHCi 中:
λ > :set -XTypeApplications
λ > import Control.Monad
λ > :t ap @((->) _)
ap @((->) _) :: (t -> a -> b) -> (t -> a) -> t -> b
您可以使用 visible type application 功能来指定参数类型。您可以以更有创意的方式查看函数:Haskell 中的函数不仅可以应用于某些类型的值,还可以应用于该值的类型。但是要传递类型,您应该以某种方式指定(在前面加上@
)您正在传递类型(因为类型不是Haskell[=73中的第一个class对象=] 还没有)。
这是它的工作原理:
λ: :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
中显示为 third 参数,但应该作为 传递second 但我仍然没有意识到所有的魔法。
例如,在 GHCi 中键入 :t ap
会得到结果
ap :: Monad m => m (a -> b) -> m a -> m b
如果我已经知道我将要使用的 Monad 实例是 ((->) r)
,我如何查询该特定实例的 ap
类型?
这只是一个 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
作为 GHC 8.0 中引入的 Lazersmoke said as a comment you can use the TypeApplications 扩展。
在 GHCi 中:
λ > :set -XTypeApplications
λ > import Control.Monad
λ > :t ap @((->) _)
ap @((->) _) :: (t -> a -> b) -> (t -> a) -> t -> b
您可以使用 visible type application 功能来指定参数类型。您可以以更有创意的方式查看函数:Haskell 中的函数不仅可以应用于某些类型的值,还可以应用于该值的类型。但是要传递类型,您应该以某种方式指定(在前面加上@
)您正在传递类型(因为类型不是Haskell[=73中的第一个class对象=] 还没有)。
这是它的工作原理:
λ: :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
中显示为 third 参数,但应该作为 传递second 但我仍然没有意识到所有的魔法。