首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Haskell中“联接bimap`”的签名

Haskell中“联接bimap`”的签名
EN

Stack Overflow用户
提问于 2021-07-28 04:17:37
回答 3查看 139关注 0票数 1

在关于代码战争的解决方案中,我遇到了以下表达式:

代码语言:javascript
复制
join bimap

其中有join :: Monad m => m (m a) -> m abimap :: Bifunctor p => (a -> b) -> (c -> d) -> p a c -> p b d。结果表达式的类型为:Bifunctor p => (c -> d) -> p c c -> p d d

我可以猜测bimap的类型可以用(->) (a->b) ((->) (c->d) p a c -> p b d)形式编写,但我不知道p a c是如何转换为p c c的,而p b d是如何转换为p d d的。请给我一些提示,如何解开这个谜题。

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2021-07-28 04:40:03

首先,让我们看看应用于函数的join类型。假设您有一个函数f :: t -> u -> v;或者,等效地说,是f :: (->) t ((->) u v)。通过比较这两种类型,我们可以尝试将其与join :: Monad m => m (m a) -> m a统一起来:

代码语言:javascript
复制
           (->) t ((->) u v)
Monad m => m      (m      a) -> m a

因此,我们可以尝试通过设置m ~ (->) ta ~ v来统一类型。

代码语言:javascript
复制
(->) t ((->) u v)
(->) t ((->) t v) -> (->) t v

但是有一个问题:为了使这些类型匹配,我们还需要t ~ u!因此,我们可以得出结论,只有当前两个参数具有相同的类型时,join才能应用于一个函数--如果它们不是,那么只有在有方法使它们相等的情况下,才能将join应用于该函数。

现在,想想bimap :: Bifunctor p => (a -> b) -> (c -> d) -> p a c -> p b d。通常情况下,abcdp可能是任何类型。但是,如果您想将join应用于bimap,这就增加了一个约束,即bimap的前两个参数必须具有相同的类型:即(a -> b) ~ (c -> d)。由此我们可以得出a ~ cb ~ d的结论。但是,当然,这意味着p a c必须与p a a相同,p b d必须与p b b相同,从而解决了这个难题。

票数 5
EN

Stack Overflow用户

发布于 2021-07-28 17:08:26

下面是使用可见类型应用程序对joinbimap的完整实例化。它很混乱,因为在幕后发生了很多事情

代码语言:javascript
复制
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

代码语言:javascript
复制
>> :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 @((->) _)类型的唯一合理实现是

代码语言:javascript
复制
joinReader :: (env -> env -> a) -> (env -> a)
joinReader (·) env = env · env

在ghci中引入类型变量是很棘手的。我们没有办法编写类似\@a @a' -> join @((->) (a -> a'))的东西。

一种不向函数添加参数的方法是给它一个部分类型签名,以量化新类型变量。

代码语言:javascript
复制
>> :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') -> ..这样的代理对象。

代码语言:javascript
复制
>> :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
票数 0
EN

Stack Overflow用户

发布于 2021-07-28 17:16:56

类型派生纯粹是机械的事情:

代码语言:javascript
复制
join foo x = foo x x
=>
join bimap x = bimap x x 
  :: ( ((a->b)~(c->d)) => p a c -> p b d ) 
  ~  (   (a~c, b~d)    => p a c -> p b d ) 
  ~  p c c -> p d d

再一次,慢慢来:

代码语言:javascript
复制
bimap :: Bifunctor p => (a -> b) -> (c -> d) -> p a c -> p b d
bimap (x :: a -> b) (y :: c -> d) :: Bifunctor p => p a c -> p b d
                     x :: a -> b
----------------------------------
                         a~c, b~d
----------------------------------
bimap (x :: a -> b) (x :: a -> b) :: Bifunctor p => p c c -> p d d

你问,为什么join foo x = foo x x是?我们甚至不知道这个foo是什么?但是我们看到join foo的结果是一个函数,因为我们将它应用于x。有了功能,

代码语言:javascript
复制
join :: Monad m        => m      (m      a)  -> m      a
     :: Monad ((->) r) => (->) r ((->) r a)  -> (->) r a
     :: Monad ((->) r) => (r  -> (r  ->  a)) -> (r ->  a)
     :: Monad ((->) r) => (r  ->  r  ->  a ) ->  r ->  a 
     foo               :: (r  ->  r  ->  a ) 
---------------------------------------------
join foo                                     ::  r ->  a

join foo是一个函数,因此foo是一个函数,foo :: r -> r -> a

代码语言:javascript
复制
join foo                                         x =   a
  where
  a = foo                  x      x -- :: a

在这里,我们甚至从函数的类型导出了函数的join实现。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/68554342

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档