Aky*_*Aky 6 haskell functional-programming pattern-matching functor
我一直试图通过在线书籍LYAH "向我学习一个Haskell" .
作者描述了Applicative类型的Functors的行为,它具有从一个仿函数中提取函数并将其映射到第二个仿函数的能力; 这是通过为Applicative类类声明的<*>函数:
class (Functor f) => Applicative f where
pure :: a -> f a
(<*>) :: f (a -> b) -> f a -> f b
Run Code Online (Sandbox Code Playgroud)
举个简单的例子,在下面的实现中,Maybe类型是Applicative的一个实例:
instance Applicative Maybe where
pure = Just
Nothing <*> _ = Nothing
(Just f) <*> something = fmap f something
Run Code Online (Sandbox Code Playgroud)
前面提到的行为的一个例子:
ghci> Just (*2) <*> Just 10 -- evaluates to Just 20
Run Code Online (Sandbox Code Playgroud)
所以<*>运算符从第一个操作数"提取"(*2)函数并将其映射到第二个操作数.
现在在Applicative类型中,<*>的两个操作数都属于同一类型,所以我认为为什么不尝试实现这种行为的泛化,其中两个操作数是不同类型的Functors,所以我可以评估这样的东西:
Just (2*) <*:*> [1,2,3,4] -- should evaluate to [2,4,6,8]
Run Code Online (Sandbox Code Playgroud)
所以这就是我提出的:
import Control.Applicative
class (Applicative f, Functor g) => DApplicative f g where
pure1 :: a -> f a
pure1 = pure
(<*:*>) :: f ( a -> b ) -> g a -> g b -- referred below as (1)
instance DApplicative Maybe [] where -- an "instance pair" of this class
(Just func) <*:*> g = fmap func g
main = do putStrLn(show x)
where x = Just (2*) <*:*> [1,2,3,4] -- it works, x equals [2,4,6,8]
Run Code Online (Sandbox Code Playgroud)
现在,虽然上述工作,但我想知道我们能否做得更好; 是否可以在DApplicative fg本身的声明中为<*:*>提供可应用于各种f&g对的默认实现?这引出了以下问题:是否有一种方法可以在不同数据类型的构造函数上进行模式匹配?
我希望我的问题有道理,而且我不只是在胡说八道(如果我是,请不要太苛刻;我只是一个FP初学者,在他的睡觉时间之后......)
这确实有意义,但最终在目前的形式上并不是特别有用.问题正是你所注意到的:没有办法提供一个默认的东西,它可以用不同的类型做出明智的事情,或者通常转换f为g.因此,您需要编写的实例数量会出现二次爆炸.
你没有完成DApplicative实例.这是一个完整的实现Maybe和[]:
instance DApplicative Maybe [] where -- an "instance pair" of this class
(Just func) <*:*> g = fmap func g
Nothing <*:*> g = []
Run Code Online (Sandbox Code Playgroud)
这两者相结合的行为Maybe和[],因为Just它做你所期望的,但Nothing它没有返回,一个空列表.
因此,而不是写DApplicative它有两个不同的类型,如果你有两个applicatives相结合的方法f,并g成一个单一的类型?如果您概括此操作,则可以使用Applicative新类型的标准.
这可以使用Applicatives的标准配方来完成
liftAp :: f (g (a -> b)) -> f (g a) -> f (g b)
liftAp l r = (<*>) <$> l <*> r
Run Code Online (Sandbox Code Playgroud)
但改为让我们改变Maybe:
import Control.Applicative
newtype MaybeT f a = MaybeT { runMaybeT :: f (Maybe a) }
instance (Functor f) => Functor (MaybeT f) where
fmap f (MaybeT m) = MaybeT ((fmap . fmap) f m)
instance (Applicative f) => Applicative (MaybeT f) where
pure a = MaybeT (pure (pure a))
(MaybeT f) <*> (MaybeT m) = MaybeT ( (<*>) <$> f <*> m)
Run Code Online (Sandbox Code Playgroud)
现在,您只需要一种方法将内部应用程序中的某些内容f转换为组合应用程序MaybeT f:
lift :: (Functor f) => f a -> MaybeT f a
lift = MaybeT . fmap Just
Run Code Online (Sandbox Code Playgroud)
这看起来像很多样板,但ghc可以自动导出几乎所有的样板.
现在您可以轻松使用组合功能:
*Main Control.Applicative> runMaybeT $ pure (*2) <*> lift [1,2,3,4]
[Just 2,Just 4,Just 6,Just 8]
*Main Control.Applicative> runMaybeT $ MaybeT (pure Nothing) <*> lift [1,2,3,4]
[Nothing,Nothing,Nothing,Nothing]
Run Code Online (Sandbox Code Playgroud)
Nothing的这种行为可能会令人惊讶,但如果你认为列表代表了不确定性,你可能会看到它如何有用.如果你想要么返回的双重行为,Just [a]或者Nothing,你只需要一个转化列表ListT Maybe a.
这与我写的实例并不完全相同DApplicative.原因是因为类型. DApplicative将一个转换f成一个g.只有当你知道具体的f和g.为了概括它,结果需要结合两者的行为,f并且正g如此实现那样.
所有这些都适用于Monads.转换类型例如MaybeT由monad变换器库(例如mtl)提供.