【问题标题】:Use cases for functor/applicative/monad instances for functions函数的 functor/applicative/monad 实例的用例
【发布时间】:2017-10-08 12:44:42
【问题描述】:

Haskell 在标准库中为函数(特别是部分应用类型 (->) a)定义了 FunctorApplicativeMonad 实例,围绕函数组合构建。

理解这些实例是一个很好的脑筋急转弯练习,但我的问题是关于这些实例的实际用途。我很高兴听到人们将这些用于一些实用代码的现实场景。

【问题讨论】:

  • Reader monad 基本上只是 (->) 的新类型包装器。
  • 我一直在使用它们。可能你自己不知道:. 只是fmap
  • @Bergi:当然,我想问题是为什么要使用它们而不是仅仅使用 (.)
  • @Bergi 组合独立于Functor 实例而存在;更准确的说法是fmap 就是.

标签: haskell functional-programming


【解决方案1】:

涉及函数的 Functor 和 Applicative 实例的常见模式是例如(+) <$> (*2) <*> (subtract 1)。当您必须使用单个值提供一系列函数时,这特别有用。在这种情况下,上面的内容等价于\x -> (x * 2) + (x - 1)。虽然这与LiftA2 非常接近,但您可以无限期地扩展此模式。如果你有一个 f 函数来接受 5 个参数,比如a -> a -> a -> a -> a -> b,你可以像f <$> (+2) <*> (*2) <*> (+1) <*> (subtract 3) <*> (/2) 那样做,并用一个值给它。就像下面的情况一样;

Prelude> (,,,,) <$> (+2) <*> (*2) <*> (+1) <*> (subtract 3) <*> (/2) $ 10
(12.0,20.0,11.0,7.0,5.0)

编辑:感谢@Will Ness 对我在另一个主题下的评论的重新评论,这是应用程序优于函数的美妙用法;

Prelude> let isAscending = and . (zipWith (<=) <*> drop 1)
Prelude> isAscending [1,2,3,4]
True
Prelude> isAscending [1,2,5,4]
False

【讨论】:

  • 感谢您的意见。因此,您发布的 5 元组示例。可以改写成(\x -&gt; (x+2, x*2, x+1, x-3, x/2)) 10——这里applicative的好处是不用重复x?
  • @Eli Bendersky 主要区别在于,在应用形式中,如果您知道主函数需要多少个参数,则整个事情在功能上是可分解的(您可以按照自己喜欢的方式动态组合参数)而在 lambda 形式中,您必须手头有一个可靠的 lambda 函数。
  • 你是说我们已经有了一个函数,比如(,,,,) 并且不必手动编写lambda?
  • @Eli Bendersky 是的...这整个事情可能是另一个函数定义,它采用应用函数(+2)、(*2)、(+1)、(减 3) 和 (/2 )` 作为参数,您可以将subtract 3 替换为subtract 1 并应用它。
  • 我想我可以看到你想说什么,但如果没有具体的用例,我很难想象 :)
【解决方案2】:

有时您希望将a -&gt; m b(其中mApplicative)形式的函数视为Applicatives 本身。这在编写验证器或解析器时经常发生。

一种方法是使用Data.Functor.Compose,它搭载(-&gt;) amApplicative 实例,为组合提供Applicative 实例:

import Control.Applicative
import Data.Functor.Compose

type Star m a b = Compose ((->) a) m b

readPrompt :: Star IO String Int
readPrompt = Compose $ \prompt -> do
    putStrLn $ prompt ++ ":"
    readLn

main :: IO ()
main = do
    r <- getCompose (liftA2 (,) readPrompt readPrompt) "write number"
    print r

还有其他方法,例如创建自己的新类型,或使用 base 或其他库中的 ready-made newtypes

【讨论】:

    【解决方案3】:

    这里是我用来解决 Diamond Kata 的绑定函数的应用程序。采用一个简单的函数来反映其输入,丢弃最后一个元素

    mirror :: [a] -> [a]
    mirror xs = xs ++ (reverse . init) xs
    

    让我们重写一下

    mirror xs = (++) xs ((reverse . init) xs)
    mirror xs = flip (++) ((reverse . init) xs) xs
    mirror xs = (reverse . init >>= flip (++)) xs
    mirror = reverse . init >>= flip (++)
    

    这是我对这个 Kata 的完整实现:​​https://github.com/enolive/exercism/blob/master/haskell/diamond/src/Diamond.hs

    【讨论】:

    • 由于ma &gt;&gt;= f 等于flip f &lt;*&gt; ma,您可以将自己限制为函数的Applicative 实例,即mirror = (++) &lt;*&gt; reverse . init。不使用(&gt;&gt;=)Monad 函数实例似乎是合理的。
    猜你喜欢
    • 2011-11-05
    • 2019-06-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-01-29
    • 2012-11-12
    相关资源
    最近更新 更多