这里是使用可见类型应用程序的join 和bimap 的完整实例化。这很混乱,因为幕后发生了很多事情
joinBimap :: forall bi a a'. Bifunctor bi => (a -> a') -> (bi a a -> bi a' a')
joinBimap = join @((->) (a -> a')) @(bi a a -> bi a' a') (bimap @bi @a @a' @a @a')
这是join @((->) _) bimap在ghci中的输出,有和没有bimap
>> :set -XTypeApplications
>> import Control.Monad (join)
>> import Data.Bifunctor (Bifunctor(bimap))
>>
>> :t join @((->) _) bimap
.. :: Bifunctor p => (c -> b) -> p c c -> p b b
>> :t join @((->) _)
.. :: (_ -> _ -> a) -> _ -> a
join @((->) _) 类型的唯一合理实现是
joinReader :: (env -> env -> a) -> (env -> a)
joinReader (·) env = env · env
在 ghci 中引入类型变量很棘手。我们没有办法写像\@a @a' -> join @((->) (a -> a')) 这样的东西。
不向函数添加参数的一种方法是给它一个量化新类型变量的部分类型签名
>> :set -XScopedTypeVariables
>> :set -XPartialTypeSignatures -Wno-partial-type-signatures
>>
>> :t join @((->) (a -> a')) bimap :: forall a a'. _
.. :: Bifunctor p => (a -> a') -> p a a -> p a' a'
也可以使用一个代理对象,它必须被应用来获得预期的术语。如果类型变量不止一个,或者是 Type 以外的其他类型,则可以使用像 \(_ :: _ a a') -> .. 这样的代理对象。
>> :t (\(_ :: a) (_ :: a') -> join @((->) (a -> a'))) undefined undefined
.. :: ((a1 -> a') -> (a1 -> a') -> a2) -> (a1 -> a') -> a2
>>
>> import Data.Function ((&))
>> :t undefined & \(_ :: _ a a') -> join @((->) (a -> a')
.. :: ((a1 -> a') -> (a1 -> a') -> a2) -> (a1 -> a') -> a2