我无法通过自定义定义的递归数据类型定义返回。
数据类型如下:
数据A a = B a | C(A a)(A a)
但是,我不知道如何定义return语句,因为我无法弄清楚何时返回B值以及何时递归返回C.
感谢任何帮助!
答案 0 :(得分:14)
为此类型定义Monad
实例的一种方法是将其视为 free monad。实际上,这需要A a
使用一个二元运算符C
作为一个小语法,并且由a
构造函数嵌入的类型B
的值表示变量。这使return
B
构造函数,嵌入变量和>>=
执行替换的运算符。
instance Monad A where
return = B
B x >>= f = f x
C l r >>= f = C (l >>= f) (r >>= f)
不难看出(>>= B)
执行身份替换,并且替换的组合是关联的。
看到这个monad的另一个更“必要”的方法是它捕获了可以翻转硬币(或读取比特流或以其他方式访问一系列二元选择)的计算的想法。
data Coin = Heads | Tails
任何可以翻转硬币的计算都必须停止翻转并成为一个值(使用B
),或者掷硬币并在硬币出现时以一种方式继续(使用C
){ {1}}和另一个Heads
。翻转硬币的monadic操作告诉你出现了什么
Tails
coin :: A Coin
coin = C (B Heads) (B Tails)
的{{1}}现在可以看作是对硬币翻转计算进行排序,允许后续计算的选择取决于早期计算所提供的值。
如果你拥有无限的硬币流,那么(除了你非凡的好运之外)你也很幸运能够运行任何>>=
- 计算其值,如下所示
A
这种monad中的一般模式是有一个构造函数用于返回值(此处为A
)和一堆其他代表可能操作的选择以及计算结果可以继续的不同方式一项行动。这里data Stream x = x :> Stream x -- actually, I mean "codata"
flipping :: Stream Coin -> A v -> v
flipping _ (B v) = v
flipping (Heads :> cs) (C h t) = flipping cs h
flipping (Tails :> cs) (C h t) = flipping cs t
没有非递归参数和两个子树,所以我可以说它必须只有一个操作,并且它必须只有两个可能的结果,因此翻转一个硬币。
因此,它取代了带有变量和一个二元运算符的语法,或者它是一种对翻转硬币的计算进行排序的方法。哪种观点更好?嗯......他们是同一枚硬币的两面。
答案 1 :(得分:7)
return
的一个好的经验法则是使它成为可行的最简单的东西(当然,任何满足monad定律的定义都很好,但通常你想要的是具有最小结构的东西)。在这种情况下,它就像return = B
一样简单(现在写一个(>>=)
来匹配!)。
顺便说一下,这是一个free monad的例子 - 实际上,这是文档中给出的例子,所以我会让文档说明一切。