如何在Applicative(Haskell)中映射RoseTree上的函数?

如何在Applicative(Haskell)中映射RoseTree上的函数?,haskell,applicative,Haskell,Applicative,如何将applicative应用于RoseTree,即将由连续应用函数创建的树组成的树返回给初始节点。以下是我编写的代码: {-# LANGUAGE DeriveFunctor, InstanceSigs #-} data RoseTree a = Nil | Node a [RoseTree a] deriving(Functor,Show) instance Applicative RoseTree where pure :: a -> RoseTree a pur

如何将applicative应用于RoseTree,即将由连续应用函数创建的树组成的树返回给初始节点。以下是我编写的代码:

{-# LANGUAGE DeriveFunctor, InstanceSigs #-}

data RoseTree a = Nil | Node a [RoseTree a] deriving(Functor,Show)

instance Applicative RoseTree where
    pure :: a -> RoseTree a
    pure x = Node x []

    (<*>) :: RoseTree (a -> b) -> RoseTree a -> RoseTree b
    (<*>) _ Nil = Nil
    (<*>) Nil _ = Nil
    (<*>) (Node f tree) (Node x subtrees) = Node (f x) (zipWith (<*>) tree subtrees)
我不确定我对纯和纯的定义有什么不对。以下是我得到的错误:

错误: 表达式'Node+1[]Node 7[节点1[],节点2[],节点3[节点4[]]失败' 应为:节点8[节点2[],节点3[],节点4[节点5[]] 但是得到:节点8[]

供参考的测试用例:

-- >>> (Node (+1) [Node (*2) []]) <*> Nil
-- Nil
--
-- >>> Nil <*> (Node 7 [Node 1 [], Node 2 [], Node 3 [Node 4 []]])
-- Nil
--
-- >>> (Node (+1) []) <*> (Node 7 [Node 1 [], Node 2 [], Node 3 [Node 4 []]])
-- Node 8 [Node 2 [],Node 3 [],Node 4 [Node 5 []]]
--
-- >>> (Node (+1) [Node (*2) []]) <*> (Node 5 [Node 2 [], Node 8 [Node 1 []]])
-- Node 6 [Node 3 [],Node 9 [Node 2 []],Node 10 [Node 4 [],Node 16 [Node 2 []]]]

类型可以有多个有效的应用程序实例,例如列表如何将一个实例直接放在[]上,另一个实例放在其新类型包装器ZipList上。您的函数似乎对应用程序实例有效,只是根据您的测试用例,它不是您想要的实例,也不是使用pure定义的实例

问题在于:

(<*>) (Node f tree) (Node x subtrees) = Node (f x) (zipWith (<*>) tree subtrees)
另外,虽然这使您的测试用例按预期工作,但我还没有严格验证它实际上是一个合法的实例。我简单地看了一下,看起来不错,但我也在凌晨1点写这篇文章。

类型可以有多个有效的应用程序实例,例如列表如何直接在[]上有一个实例,在其新类型包装器ZipList上有另一个实例。您的函数似乎对应用程序实例有效,只是根据您的测试用例,它不是您想要的实例,也不是使用pure定义的实例

问题在于:

(<*>) (Node f tree) (Node x subtrees) = Node (f x) (zipWith (<*>) tree subtrees) 另外,虽然这使您的测试用例按预期工作,但我还没有严格验证它实际上是一个合法的实例。我简单地看了一下,它看起来不错,但我也在凌晨1点写这篇文章。

我们可以将您的RoseTree视为特定monad transformer的特定应用。让我们将您自己的定义放在一个名为Rose的模块中,并派生RoseTree的读取和显示实例。现在我们可以开始幻想了。注意:您可能还不了解这里的所有内容。其中一些使用相当高级的GHC语言扩展。但我觉得这很有趣

我们将使用免费软件包中的。顾名思义,相对于Comonad类,它扮演着一个特殊的角色,但事实证明它对monad也很有用

下面是我们如何实现Show and Read而不必大惊小怪的方法:

-- Convert a `RoseTree` to the simple representation of one.
-- Note that the pattern synonyms make this really easy!
toBasicRose :: RoseTree a -> Rose.RoseTree a
toBasicRose Nil = Rose.Nil
toBasicRose (Node a ts) = Rose.Node a (map toBasicRose ts)

-- Convert the simple representation back to a `RoseTree`.
fromBasicRose :: Rose.RoseTree a -> RoseTree a
fromBasicRose Rose.Nil = Nil
fromBasicRose (Rose.Node a ts) = Node a (map fromBasicRose ts)

instance Show a => Show (RoseTree a) where
  showsPrec p = showsPrec p . toBasicRose
instance Read a => Read (RoseTree a) where
  readPrec = fmap fromBasicRose readPrec
你所有的测试用例都通过了

业绩说明 我担心所有的映射都会使节点模式变慢。但我刚刚检查了编译器中间语言,并确定GHC的重写规则实际上有效,无条件地消除了所有映射。

我们可以将您的RoseTree视为特定monad transformer的特定应用程序。让我们将您自己的定义放在一个名为Rose的模块中,并派生RoseTree的读取和显示实例。现在我们可以开始幻想了。注意:您可能还不了解这里的所有内容。其中一些使用相当高级的GHC语言扩展。但我觉得这很有趣

我们将使用免费软件包中的。顾名思义,相对于Comonad类,它扮演着一个特殊的角色,但事实证明它对monad也很有用

下面是我们如何实现Show and Read而不必大惊小怪的方法:

-- Convert a `RoseTree` to the simple representation of one.
-- Note that the pattern synonyms make this really easy!
toBasicRose :: RoseTree a -> Rose.RoseTree a
toBasicRose Nil = Rose.Nil
toBasicRose (Node a ts) = Rose.Node a (map toBasicRose ts)

-- Convert the simple representation back to a `RoseTree`.
fromBasicRose :: Rose.RoseTree a -> RoseTree a
fromBasicRose Rose.Nil = Nil
fromBasicRose (Rose.Node a ts) = Node a (map fromBasicRose ts)

instance Show a => Show (RoseTree a) where
  showsPrec p = showsPrec p . toBasicRose
instance Read a => Read (RoseTree a) where
  readPrec = fmap fromBasicRose readPrec
你所有的测试用例都通过了

业绩说明
我担心所有的映射都会使节点模式变慢。但我刚刚检查了编译器中间语言,并确定GHC的重写规则实际上已经生效,并且无条件地消除了所有映射。

@dfeuer然后pure id Nil将是底部,因为没有匹配的情况,并且它肯定是不合法的。第1、2、3点不能完全说服我。我的意思是,ZipList是一个应用程序,但它将每个函数应用于单个项,反之,以逐点方式,有效地将列表截断为相同的长度。也许我们可以实施一项合法的法律?ZipRoseTree应用程序实例,其中树被修剪为其最大的公共形状,然后应用程序像zip列表一样逐点运行。如果这也是合法的,那么问题就变成了:OP想要哪个实例?@chi这些观点来自提问者的测试用例。我很确定我们可以实现一个合法的ZipRoseTree,但我认为这样的东西不可能通过那些测试。啊,这是一个好的观点。我错过了测试用例,它们确实排除了一个zip树实例;你可能会觉得我的回答有趣和/或可怕。@dfeur然后纯id Nil将是底部,因为没有案例匹配,并且它肯定是不合法的。1、2、3点不能完全说服我。我的意思是,ZipList是一个应用程序,但它将每个函数应用于单个项,反之,以逐点方式,有效地将列表截断为相同的长度。也许我们可以实施一项合法的法律?ZipRoseTree应用程序实例,其中树被修剪为其最大的公共形状,然后应用程序像zip列表一样逐点运行。如果这也是合法的,那么问题就变成了:你会选择哪种情况
“你想要什么?”chi这些观点来自提问者的测试案例。我很确定我们可以实现一个合法的ZipRoseTree,但我认为这样的东西不可能通过那些测试。啊,这是一个好的观点。我错过了测试用例,它们确实排除了一个zip树实例;你可能会觉得我的回答有趣和/或可怕。
-- Convert a `RoseTree` to the simple representation of one.
-- Note that the pattern synonyms make this really easy!
toBasicRose :: RoseTree a -> Rose.RoseTree a
toBasicRose Nil = Rose.Nil
toBasicRose (Node a ts) = Rose.Node a (map toBasicRose ts)

-- Convert the simple representation back to a `RoseTree`.
fromBasicRose :: Rose.RoseTree a -> RoseTree a
fromBasicRose Rose.Nil = Nil
fromBasicRose (Rose.Node a ts) = Node a (map fromBasicRose ts)

instance Show a => Show (RoseTree a) where
  showsPrec p = showsPrec p . toBasicRose
instance Read a => Read (RoseTree a) where
  readPrec = fmap fromBasicRose readPrec