【问题标题】:Using Applicative instance of composed functors使用组合函子的应用实例
【发布时间】:2021-02-23 13:59:47
【问题描述】:

我有两种形式的功能

foo :: Int -> IO String

bar :: Int -> IO Integer

它基本上存在于由(->) IntIO 组成的函子中。 现在,有一个类型的函数

baz :: String -> Integer -> Float

我想使用类似的应用语法将其提升到 Int -> IO _ 上下文

foobarbaz :: Int -> IO Float
foobarbaz = baz <$> foo <*> bar

如果我这样做,编译器会对我大喊大叫

Couldn't match type `IO String' with `[Char]'
      Expected type: Int -> String
        Actual type: Int -> IO String

好像它试图仅将应用程序实例用于(-&gt;) Int

我认为应用函子是组合的,因此我可以将应用实例用于组合函子。我错了吗?还是应该只向编译器提供更多信息?

我也尝试启用TypeApplications 来明确指定我想使用的函子,但我意识到我不能写(-&gt;) Int (IO _)。真的有办法吗?

【问题讨论】:

    标签: haskell applicative


    【解决方案1】:

    我认为应用仿函数是组合的,因此我可以将应用实例用于组合仿函数。

    没错,但是假设我们取f ~ (-&gt;) Int,那么类型是(&lt;$&gt;) :: (a -&gt; b) -&gt; (Int -&gt; a) -&gt; (Int -&gt; b)(&lt;*&gt;) :: (Int -&gt; (a -&gt; b)) -&gt; (Int -&gt; a) -&gt; (Int -&gt; b),但这与类型不匹配,因为baz需要String和@ 987654328@,而foo 返回一个IO Stringbar 一个IO Integer

    你可以利用liftA2 :: Applicative f =&gt; (a -&gt; b -&gt; c) -&gt; f a -&gt; f b -&gt; f c解除baz函数来使用IO动作的结果:

    import Control.Applicative(liftA2)
    
    foobarbaz :: Int -> IO Float
    foobarbaz = liftA2 baz <$> foo <*> bar

    liftA2 将在此处将baz :: String -&gt; Integer -&gt; Float 转换为liftA2 baz :: IO String -&gt; IO Integer -&gt; IO Float。因此,这是一个与foobar 的输出类型匹配的函数。

    【讨论】:

    • 谢谢!这是有道理的,基本上我当时需要提升一个仿函数。我也可以(liftA2 (liftA2 baz)) foo bar。有没有什么特别的原因导致编译器不能只用一次电梯就能做到这一点?
    • @marcosh:有一个电梯可以通过(-&gt; Int),但另一个可以通过IO。但是你是对的,你可以使用liftA2 (liftA2 baz) foo bar。提升总是提升“一个”应用级别(并且由于类型系统,它始终是最外层的)。因此它不会“递归”到更多级别,因为这可能会导致歧义。
    • @marcosh 要一次性完成,您可以使用Data.Functor.Compose 显式组合函子,代价是一些新类型的包装/展开。 hackage.haskell.org/package/base-4.14.0.0/docs/…
    猜你喜欢
    • 2016-10-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-04-08
    • 2021-10-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多