Haskell标准库中为函数(特别是部分应用类型 (->) a
)定义了Functor
,Applicative
和Monad
实例,围绕函数组合构建。
理解这些实例是一种不错的头脑体操练习,但我在这里想问的是这些实例的实际用途。我很乐意听听人们在某些实际代码中使用这些实例的现实场景。
Haskell标准库中为函数(特别是部分应用类型 (->) a
)定义了Functor
,Applicative
和Monad
实例,围绕函数组合构建。
理解这些实例是一种不错的头脑体操练习,但我在这里想问的是这些实例的实际用途。我很乐意听听人们在某些实际代码中使用这些实例的现实场景。
(+) <$> (*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
(\x -> (x+2, x*2, x+1, x-3, x/2)) 10
--这里应用程序的优点是不需要重复使用x
? - Eli Bendersky(,,,,)
,不需要手写 lambda 吗? - Eli Bendersky(+2)
、(*2)
、(+1)
、(subtract 3)
和 ( /2)
作为参数,并且您可以将 subtract 3
替换为 subtract 1
并应用它。 - Redu(\x -> (x+2, x*2, x+1, x-3, x/2))
在使用时是硬编码的,而在应用风格中则不是。可以使用 applicative 风格创建一个新函数来动态更改“映射”函数。具体的用例可能不容易 spontaneously 想出,但这是一个很好的知识点需要记在心里。 - Redua -> m b
(其中 m
是一个 Applicative
)的函数视为 Applicative
。这通常发生在编写验证器或解析器时。Data.Functor.Compose
,它依赖于 (->) a
和 m
的 Applicative
实例来给出组合的 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
这里是我用于解决Diamond Kata的bind函数的一个应用实例。使用一个简单的函数来镜像其输入并丢弃最后一个元素。
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 >>= f
等于 flip f <*> ma
,因此您可以将自己限制在函数的 Applicative
实例上,即 mirror = (++) <*> reverse . init
。不使用 (>>=)
和函数的 Monad
实例似乎是合理的。 - Micha Wiedenmann
(->)
的一个新类型包装器。 - melpomene.
就是fmap
。 - BergiFunctor
实例无关;更准确地说,fmap
就是.
。 - chepner