我有一堆数据结构,例如data Foo = Foo {a :: Type1, b :: Type2} deriving (Something)
,Type1
和Type2
总是不同(通常是原始类型,但它不相关),并且数量不同。
我来了一堆像
这样的功能justFooify :: Maybe Type1 -> Maybe Type2 -> Maybe Foo
justFooify f b =
| isNothing f = Nothing
| isNothing b = Nothing
| otherwise = Just $ Foo (fromJust f) (fromJust b)
有什么我想念的吗?在我编写的第三个这样的函数之后,我开始认为可能它可能太多了。
答案 0 :(得分:9)
你需要申请人!
import Control.Applicative
justFooify :: Maybe Type1 -> Maybe Type2 -> Maybe Foo
justFooify f b = Foo <$> f <*> b
或者您可以在此示例中使用liftA2
:
justFooify = liftA2 Foo
它的行为与liftM2
类似,但适用于Applicative
。如果您有更多参数,只需使用更多<*>
s:
data Test = Test String Int Double String deriving (Eq, Show)
buildTest :: Maybe String -> Maybe Int -> Maybe Double -> Maybe String -> Maybe Test
buildTest s1 i d s2 = Test <$> s1 <*> i <*> d <*> s2
什么是Applicative
?它们基本上是一个更强大的Functor
和一个不那么强大的Monad
,它们恰好介于两者之间。类型类的定义是
class Functor f => Applicative f where
pure :: a -> f a
(<*>) :: f (a -> b) -> f a -> f b
-- plus a few more things that aren't important right now
如果您的Applicative
也是Monad
,则pure
与return
相同(事实上,有些人认为return
不正确,我们应该只有pure
)。但<*>
运算符使它们比Functor
更强大。它为您提供了一种将函数放入数据结构的方法,然后将该函数应用于也包含在数据结构中的值。所以当你有像
> :t Test -- Our construct
Test :: String -> Int -> Double -> String -> Test
> :t fmap Test -- also (Test <$>), since (<$>) = fmap
fmap Test :: Functor f => f String -> f (Int -> Double -> String -> Test)
我们看到它在Functor
内部构造一个函数,因为Test
需要多个参数。因此Test <$> Just "a"
的类型为Maybe (Int -> Double -> String -> Test)
。仅使用Functor
和fmap
,我们无法对Maybe
内部应用任何内容,但我们可以使用<*>
。 <*>
的每个应用程序都将一个参数应用于内部Functor
,现在应将其视为Applicative
。
另一个方便的事情是,它适用于所有 Monads(目前定义他们的Applicative
实例)。这意味着列出IO
,一个参数的函数,Either e
,解析器等。例如,如果您从用户那里获得了构建Test
:
askString :: IO String
askInt :: IO Int
askDouble :: IO Double
-- whatever you might put here to prompt for it, or maybe it's read from disk, etc
askForTest :: IO Test
askForTest = Test <$> askString <*> askInt <*> askDouble <*> askString
它仍然有用。这是Applicatives的力量。
仅供参考,在GHC 7.10中将实施Functor-Applicative-Monad Proposal。这将改变Monad
的定义
class Monad m where
return :: a -> m a
(>>=) :: m a -> (a -> m b) -> m b
到
class Applicative m => Monad m where
return :: a -> m a
return = pure
(>>=) :: m a -> (a -> m b) -> m b
join :: m (m a) -> m a
(或多或少)。这将破坏一些旧的代码,但很多人对此感到兴奋,因为它意味着所有Monads都是Applicative并且所有Applicative都是Functors,我们将拥有这些代数对象的全部功能。