Maybe参数的通用数据构造函数

mar*_*rco 3 haskell

我有一大堆的数据结构的像data Foo = Foo {a :: Type1, b :: Type2} deriving (Something)Type1Type2是始终不同(一般是原始类型,但它是不相关的),并在不同的数字.

我来这里有很多功能

justFooify :: Maybe Type1 -> Maybe Type2 -> Maybe Foo
justFooify f b =
  | isNothing f = Nothing
  | isNothing b = Nothing
  | otherwise   = Just $ Foo (fromJust f) (fromJust b)
Run Code Online (Sandbox Code Playgroud)

有什么我想念的吗?第三次这样的功能之后,我写了我来想,也许它可能是太多了.

bhe*_*ilr 9

你需要申请人!

import Control.Applicative

justFooify :: Maybe Type1 -> Maybe Type2 -> Maybe Foo
justFooify f b = Foo <$> f <*> b
Run Code Online (Sandbox Code Playgroud)

或者您可以liftA2在此示例中使用:

justFooify = liftA2 Foo
Run Code Online (Sandbox Code Playgroud)

它表现得像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
Run Code Online (Sandbox Code Playgroud)

什么是Applicatives?他们基本上是一个更强大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
Run Code Online (Sandbox Code Playgroud)

如果你Applicative也是一个Monad,那么pure就是一样的return(事实上​​,有些人觉得有return不正确,我们应该只有pure).然而,<*>运营商使他们比Functors 更强大.它为您提供了一种将函数放入数据结构的方法,然后将该函数应用于也包含在数据结构中的值.所以当你有类似的东西时

> :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)
Run Code Online (Sandbox Code Playgroud)

我们看到它在a中构造一个函数Functor,因为Test它需要多个参数.所以Test <$> Just "a"有型Maybe (Int -> Double -> String -> Test).只是Functorfmap,我们不能在内部应用任何东西Maybe,但<*>我们可以.每个应用程序都<*>将一个参数应用于该内部Functor,现在应该将其视为一个Applicative.

关于它的另一个方便的事情是它适用于所有 Monad(当前定义它们的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
Run Code Online (Sandbox Code Playgroud)

它仍然有效.这是Applicatives的力量.


仅供参考,在GHC 7.10中,将实施Functor-Applicative-Monad提案.这将改变Monadfrom 的定义

class Monad m where
    return :: a -> m a
    (>>=) :: m a -> (a -> m b) -> m b
Run Code Online (Sandbox Code Playgroud)

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
Run Code Online (Sandbox Code Playgroud)

(或多或少).这将打破一些旧代码,但许多人对此感到兴奋,因为它意味着所有Monads都是Applicative并且所有Applicative都是Functors,我们将拥有这些代数对象的全部功能.