【问题标题】:Nested applicative functors of different types in HaskellHaskell中不同类型的嵌套应用函子
【发布时间】:2018-12-08 07:23:31
【问题描述】:

我想制作不同类型的嵌套应用函子。例如,不同类型的嵌套简单函子(在 ghci 中)工作正常:

Prelude> ((+2) <$>) <$> (Just [1..4])
Just [3,4,5,6]

但是对于不同类型的应用函子:

Prelude> ((*) <$>)  <$> (Just [1,2,3]) <*> (Just [4,5,6,7])

<interactive>:56:1: error:
    * Couldn't match type `[Integer -> Integer]' with `[Integer] -> b'

不工作!我想得到这样的东西:

Just [4,5,6,7,8,10,12,14,12,15,18,21]

我知道应用函子在函子和单子之间处于中间位置。我可以将这个练习看作是关于 monad 转换器的主题之前的初步练习。

【问题讨论】:

    标签: haskell functor monad-transformers applicative


    【解决方案1】:

    除了嵌套提升和 fmap 之外,组合应用函子的另一个选项是 Data.Functor.Compose newtype:

    newtype Compose f g a = Compose { getCompose :: f (g a) }
    

    例如:

    ghci> let Compose result = (*) <$> Compose (Just [1,2,3]) <*> Compose (Just [4,5,6,7])
    ghci> result
    Just [4,5,6,7,8,10,12,14,12,15,18,21]
    

    Applicatives 表现得非常好,以至于单个 newtype 足以组成任何两种类型的实例。而且除了嵌套还有其他组合方式,比如ProductDay卷积:

    data Product f g a = Pair (f a) (g a)
    
    data Day f g a = forall b c. Day (f b) (g c) (b -> c -> a)
    

    Monads 的组合并不好,因此我们需要为每个 monad 提供一个不同的新类型,以便用第一个 monad 的能力来增强其他一些 monad。我们将这些新类型称为 monad 转换器。

    【讨论】:

      【解决方案2】:

      在这种情况下,您需要:

      liftA2 (*) <$> Just [1, 2, 3] <*> Just [4, 5, 6, 7]
      

      或者:

      liftA2 (liftA2 (*)) (Just [1, 2, 3]) (Just [4, 5, 6, 7])
      

      外层… &lt;$&gt; … &lt;*&gt; …liftA2 作用于Maybe,而内层作用于[]。如果您不知道这一点,您可以通过向 GHCi 询问您应该放在那里的内容类型来弄清楚,例如使用类型孔:

      :t _ <$> (Just [1 :: Int, 2, 3]) <*> (Just [4 :: Int, 5, 6, 7]) :: Maybe [Int]
      

      它回馈:

      _ :: [Int] -> [Int] -> [Int]
      

      您想要组合列表的行为是\ xs ys -&gt; (*) &lt;$&gt; xs &lt;*&gt; ys,可以缩写为liftA2 (*)((*) &lt;$&gt;)fmap (*) 不起作用,因为这只是您需要的一半:它在单个列表上运行(使用 Functor),而您想组合两个列表(使用 Applicative)。

      当然,liftA2 (liftA2 (*)) 适用于任何两个元素为数字的嵌套应用函子:

      (Applicative f, Applicative g, Num a)
        => f (g a) -> f (g a) -> f (g a)
      

      例如嵌套列表:

      liftA2 (liftA2 (*)) [[1], [2], [3]] [[4, 5, 6]]
      == [[4,5,6],[8,10,12],[12,15,18]]
      
      -- (Transposing the inputs transposes the output.)
      liftA2 (liftA2 (*)) [[1, 2, 3]] [[4], [5], [6]]
      == [[4,8,12],[5,10,15],[6,12,18]]
      

      Maybe的列表:

      liftA2 (liftA2 (*)) [Just 1, Nothing, Just 3] [Just 4, Nothing, Just 6]
      == [Just 4, Nothing, Just 6,
          Nothing, Nothing, Nothing,
          Just 12, Nothing, Just 18]
      

      甚至更奇特的东西,比如函数列表:

      ($ (3, 5)) <$> (liftA2 (+) <$> [fst, snd] <*> [snd, fst])
      == [fst (3, 5) + snd (3, 5),
          fst (3, 5) + fst (3, 5),
          snd (3, 5) + snd (3, 5),
          snd (3, 5) + fst (3, 5)]
      == [3+5, 3+3, 5+5, 5+3]
      == [8,6,10,8]
      

      【讨论】:

      • 这个变种也可以工作:(liftA2 . liftA2) (*) (Just [1..3]) (Just [4..7])
      【解决方案3】:

      我们也可以通过前奏函数非常直接地做到这一点。不过你的第一部分很好。

      ((*) &lt;$&gt;) &lt;$&gt; (Just [1,2,3]) 类型为Num a =&gt; Maybe [a -&gt; a]

      我们只需要将 Maybe monad 中的应用列表 fmap 到 Maybe monad 中的列表。所以一种方法可能是将第一部分绑定到(&lt;$&gt; Just [4, 5, 6, 7]) . (&lt;*&gt;) :: Num a =&gt; [a -&gt; b] -&gt; Maybe [b]

      ((*) <$>)  <$> (Just [1,2,3]) >>= (<$> Just [4,5,6,7]) . (<*>)
      

      屈服于

      Just [(1*),(2*),(3*)] >>= (<$> Just [4,5,6,7]) . (<*>)
      

      屈服于

      ([(1*),(2*),(3*)] <*>) <$> Just [4,5,6,7]
      

      屈服于

      Just [4,5,6,7,8,10,12,14,12,15,18,21] 
      

      【讨论】:

      • 虽然此解决方案适用于问题中的特定示例,但它一般不能回答问题,因为它要求Monad
      猜你喜欢
      • 1970-01-01
      • 2015-09-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-04-05
      • 2021-12-01
      相关资源
      最近更新 更多