【问题标题】:Haskell function from (a -> [b]) -> [a -> b]来自 (a -> [b]) -> [a -> b] 的 Haskell 函数
【发布时间】:2012-12-07 14:44:32
【问题描述】:

我有一个函数seperateFuncs 这样

seperateFuncs :: [a -> b] -> (a -> [b])
seperateFuncs xs = \x -> map ($ x) xs

我想知道是否存在相反的情况,即是否有函数

joinFuncs :: (a -> [b]) -> [a -> b]

我认为不是(主要是因为列表不是固定长度的),但也许我会被证明是错误的。 那么问题是有一些数据类型f 有一个函数 :: (a -> f b) -> f (a -> b)?

【问题讨论】:

  • 我猜除了简单的解决方案。
  • 对于无限列表和已知长度的元组有这样的功能。基本上,输出的第 i 个元素是输入的第 i 个投影。
  • Applicative f => Monad f 可能会有不同的解释:每个f (a -> b) 都可以变成(a -> f b),但不一定反过来(此外,您可以按照以下方式实现(<*>) :: f (a -> b) -> (f a -> f b) (=<<) :: (a -> f b) -> (f a -> f b),但并非总是相反)。不过,不确定这一点。

标签: list function haskell


【解决方案1】:

您可以非常干净地将seperateFuncs 概括为Applicative(或Monad):

seperateFuncs :: (Applicative f) => f (a -> b) -> (a -> f b)
seperateFuncs f x = f <*> pure x

用point-free写成,你有seperateFuncs = ((. pure) . (&lt;*&gt;)),所以你基本上想要unap . (. extract),如果你用pointful写成下面的定义:

joinFuncs :: (Unapplicative f) => (a -> f b) -> f (a -> b)
joinFuncs f = unap f (\ g -> f (extract g))

这里我将Unapplictaive定义为:

class Functor f => Unapplicactive f where
    extract  :: f a -> a
    unap     :: (f a -> f b) -> f (a -> b)

要获得definitions given by leftaroundabout,您可以给出以下实例:

instance Unapplicative [] where
    extract = head
    unap f = [\a -> f [a] !! i | i <- [0..]]

instance Unapplicative ((->) c) where
    extract f = f undefined
    unap f = \x y -> f (const y) x

我认为对于任何不同于(-&gt;)f,很难想出一个“有用的”函数f :: (f a -&gt; f b) -&gt; f (a -&gt; b)

【讨论】:

  • 顺便说一句,与Matt Fenwick's answer 的要点相同:您可以将coap 视为g (f a) (f b) -&gt; f (g a b) 的一个实例,所以有点像sequenceA,您正在“转置”一个函子( f) 和一个双函子 ((-&gt;))。
【解决方案2】:

首先,你可以暴力破解这个功能:

joinFuncs f = [\x -> f x !! i | i<-[0..]]

但这显然很麻烦 - 结果列表始终是无限的,但只有在 length(f x) &gt; i 时使用 x 评估 ith 元素才会成功。

给出一个“真正的”解决方案

那么问题是有一些数据类型f 有一个函数:: (a -&gt; f b) -&gt; f (a -&gt; b)

考虑(-&gt;)c。这样,您的签名将显示为(a -&gt; (c-&gt;b)) -&gt; (c-&gt;(a-&gt;b)) 或等效的(a -&gt; c -&gt; b) -&gt; c -&gt; a -&gt; b,事实证明,这只是flip

当然,这有点微不足道,因为seperateFuncs 对这种类型具有相同的签名...

【讨论】:

    【解决方案3】:

    “是否有某种数据类型 f 具有函数 :: (a -> f b) -> f (a -> b)?”

    事实上,Traversable 类型类中有这个函数更通用的版本,它处理可交换函子:

    class (Functor t, Foldable t) => Traversable t where
    
      ... 
    
      sequenceA :: Applicative f => t (f b) -> f (t b)
    

    这与您的职能有什么关系?从你的类型开始,用一种类型替换,我们恢复sequenceA

    1. (a -&gt; f b) -&gt; f (a -&gt; b) ==> let t = (-&gt;) a
    2. t (f b) -&gt; f (t b)

    但是,这种类型有t 必须是Traversable 的约束——并且(-&gt;) a 没有Traversable 实例,这意味着这个操作通常不能用函数完成。尽管请注意“其他方向”——f (a -&gt; b) -&gt; (a -&gt; f b) 适用于所有功能和所有应用程序f

    【讨论】:

      【解决方案4】:

      我最近不得不考虑很多问题,这些问题简化为与您的问题非常相似的问题。这是我发现的概括。

      首先,这样做很简单(Tinctorius 指出):

      f2m :: Functor f => f (a -> b) -> a -> f b
      f2m f a = fmap ($a) f
      

      但一般来说这是不可能的:

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

      有人在#haskell irc 频道中向我解释了一个有见地的理解方式,即如果存在m2a 函数,ApplicativeMonad 之间就没有区别。为什么?好吧,我不是 100% 遵循它,但它是这样的:Monad m =&gt; a -&gt; m b 是带有一个参数的非常常见的一元动作类型,而 Applicative f =&gt; f (a -&gt; b) 也是非常常见的类型,因为不知道正确的名称,我称之为“适用的应用程序”。而Monad 可以做Applicative 不能做的事情这一事实与m2a 不存在的事实有关。

      现在,应用于您的问题:

      joinFuncs :: (a -> [b]) -> [a -> b]
      

      我怀疑同样的“Monad /= Applicative”论点(再次强调,我不完全理解)应该适用于此。我们知道Monad [] 实例可以做Applicative [] 实例不能做的事情。如果您可以编写具有指定类型的joinFuncs,那么与a -&gt; [b] 参数相比,[a -&gt; b] 结果在某种意义上必须“丢失信息”,因为否则Applicative []Monad [] 相同。 (我所说的“丢失”信息是指任何具有joinFuncs 类型的函数都不能有逆函数,因此可以保证消除某些函数对f, g :: a -&gt; [b] 之间的区别。极端情况是@987654343 @.)

      我确实发现我需要类似于m2a 的函数所以我发现的特殊情况是可以这样做:

      import Data.Map (Map)
      import qualified Data.Map as Map
      
      -- | Enumerate a monadic action within the domain enumerated by the 
      -- argument list.
      boundedM2a :: Monad m => (a -> m b) -> [a] -> m [(a,b)]
      boundedM2a f = mapM f'
          where f' a = do b <- f a
                          return (a, b)
      
      -- | The variant that makes a 'Map' is rather useful.
      boundedM2a' :: (Monad m, Ord a) => (a -> m b) -> [a] -> m (Map a b)
      boundedM2a' f = liftM Map.fromList . boundedM2a f
      

      请注意,除了我们枚举as 的要求之外,一个有趣的观察是,要做到这一点,我们必须在某种意义上“实现”结果;将其从函数/操作转换为某种列表、地图或表格。

      【讨论】:

      • 如果一个应用函子 F 有态射 (a -&gt; F b) -&gt; F(a -&gt; b) 那么 F 自动是一个单子并且有 (a -&gt; F b) -&gt; F a -&gt; F b。这是因为应用函子有F(a -&gt; b) -&gt; F a -&gt; F b。因此,态射(a -&gt; F b) -&gt; F(a -&gt; b) 不能是所有应用函子的一般属性——否则所有应用函子都是单子。
      【解决方案5】:

      问题“我可以使用类型签名joinFuncs :: (a -&gt; [b]) -&gt; [a -&gt; b] 的函数是不完整的,并且没有说明您希望此函数满足哪些法律。没有法律,您可以通过定义joinFuncs _ = [] 来解决这个问题(总是返回一个空列表) . 这个微不足道的函数满足所需的类型签名,但很可能没用。

      要求joinFuncs 有用的一种方法是强加非退化定律separateFuncs . joinFuncs == id。然后可以证明,对于这种类型签名,不可能实现joinFuncs

      这种类型签名的更一般情况是(a -&gt; f b) -&gt; f (a -&gt; b),其中f 是某个函子。我称这样的函子为“刚性的”。有关详细信息,请参阅此问题 Is this property of a functor stronger than a monad?

      所有刚性函子R满足R ()类型只有一个不同值的性质,即它等价于()。这让我们立即看到List 函子不是刚性的,因为List () 不等于()

      刚性函子最简单的重要示例是type R a = (a -&gt; p) -&gt; a,其中p 是固定类型。这样定义的函子R实际上是一个刚性monad。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-12-25
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-11-28
        • 2021-11-10
        相关资源
        最近更新 更多