【问题标题】:Applicative Instance for (Monad m, Monoid o) => m o?(Monad m, Monoid o) => m o? 的应用实例
【发布时间】:2013-08-17 23:15:31
【问题描述】:

对不起,糟糕的标题。我正在尝试为 Monad 创建一个 Applicative 的实例,以包装 Monoid 的类型。

instance (Monad m, Monoid o) => Applicative (m o) where
    pure x = return mempty
    xm <*> ym = do
        x <- xm
        y <- ym
        return $ x `mappend` y

这不起作用; GCHi 抱怨:

Kind mis-match
The first argument of `Applicative' should have kind `* -> *',
but `m o' has kind `*'
In the instance declaration for `Applicative (m o)'

我意识到我上面写的可能没有意义。这是上下文:我正在尝试使用A pattern for almost compositional functions 论文中描述的compos 抽象。采用这棵树(使用compos 的 GADT 版本;我已经简化了很多):

data Tree :: * -> * where
    Var :: String -> Expr
    Abs :: [String] -> Expr -> Expr
    App :: Expr -> [Expr] -> Expr

class Compos t where
    compos :: Applicative f => (forall a. t a -> f (t a)) -> t c  -> f (t c)

instance Compos Tree where
    compos f t =
        case t of
            Abs ps e -> pure Abs <*> pure ps <*> f e
            App e es -> pure App <*> f e <*> traverse f es
            _ -> pure t

我将编写许多函数,它们会下降树并返回一个说错误列表或一组字符串,同时还需要它下降时的状态(例如绑定环境),例如:

composFoldM :: (Compos t, Monad m, Monoid o) => (forall a. t a -> m o) -> t c -> m o
composFoldM f = ??? 

checkNames :: (Tree a) -> State (Set Name) [Error]
checkNames e =
    case e of
        Var n -> do
            env <- get
            -- check that n is in the current environment
            return $ if Set.member n env then [] else [NameError n]
        Abs ps e' -> do
            env <- get
            -- add the abstractions to the current environment
            put $ insertManySet ps env
            checkNames e'
        _ -> composFoldM checkNames e

data Error = NameError Name
insertManySet xs s = Set.union s (Set.fromList xs)

我认为这些都应该可以通过使composFoldM 使用compos 来抽象出(Monad m, Monoid o) =&gt; m o 结构。因此,要将其与compos 的 GADT Applicative 版本一起使用,请参见 the paper 的第 575/576 页。我想我需要为这个结构创建一个Applicative 实例。我该怎么做?还是我完全走错了路?

【问题讨论】:

    标签: haskell monads gadt applicative monoids


    【解决方案1】:

    您需要 Data.Functor.Constant 中的 Constant 应用程序,位于 transformers 包中,您可以找到 here

    Applicative 具有以下实例:

    instance (Monoid a) => Applicative (Constant a) where
        pure _ = Constant mempty
        Constant x <*> Constant y = Constant (x `mappend` y)
    

    然后,您可以使用来自Data.Functor.Compose(也在transformers 包中)的Compose 与任何其他应用程序组合Constant,您可以找到here

    Compose 有这个Applicative 实例:

    instance (Applicative f, Applicative g) => Applicative (Compose f g) where
        pure x = Compose (pure (pure x))
        Compose f <*> Compose x = Compose ((<*>) <$> f <*> x)
    

    然后您可以将Compose 您的Constant 应用程序与任何其他Applicative(如State)一起保持某些状态和正在运行的Monoid 计数。

    更一般地,您应该阅读论文The Essence of the Iterator Pattern,其中更详细地讨论了这些模式。

    【讨论】:

    • 这看起来像我需要的东西!但我如何实际使用它?我试图做composFoldM f = getCompose . compos (Compose . WrapMonad . Const . f) 之类的事情,但这不起作用。有没有关于如何组合函子的例子/解释?
    • 天哪。我终于通过试验和改进解决了这个问题。我想这就是你的学习方式!正确的是composFoldM f = liftM getConst . unwrapMonad . getCompose . compos (Compose . WrapMonad . liftM Const . f)。 :D
    • @CallumRogers 完全正确!这是 Haskell 的优点之一:类型检查器将始终引导您找到正确的解决方案。
    • 确实,我最喜欢 Haskell 的地方之一。
    • ConstantControl.Applicative 中也以Const 的形式存在。应该使用哪一个?
    猜你喜欢
    • 2014-05-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-10-13
    • 2023-04-10
    • 2019-06-17
    相关资源
    最近更新 更多