我最近为 list monad 实现了类似的东西:
diagonals :: [[(Integer, Integer)]]
diagonals =
let diagonal index =
do
number <- [0..index]
return (number, index - number)
in map diagonal (enumFrom 0)
newtype Enumerable a = Enumerable { list :: [a] }
instance Monad Enumerable where
return item = Enumerable [item]
enumerable1 >>= getEnumerable2 =
let
list1 = list enumerable1
diagonalItems diagonal =
do
(index1, index2) <- diagonal
guard (containsIndex list1 index1)
let item1 = genericIndex list1 index1
let list2 = list (getEnumerable2 item1)
guard (containsIndex list2 index2)
let item2 = genericIndex list2 index2
return item2
in Enumerable (concat (takeWhile (not . null) (map diagonalItems diagonals)))
不幸的是,你必须做一些newtype
恶作剧,因为你不能声明另一个实例Monad []
,但除此之外,它工作正常。就像是
list
(
do
item1 <- Enumerable [0..]
item2 <- Enumerable [1..]
item3 <- Enumerable [2..]
return (item1, item2, item3)
)
为您提供所有自然数三元组的无限列表,其中第二项大于或等于 1,第三项大于或等于 2。
我检查了一下,如果我没有犯错,它应该遵守所有的单子定律。它也适用于有限列表,在遇到完全空的对角线后它将停止尝试查找新项目。
我不熟悉流单子,所以我不能告诉你如果你在那里做类似的事情会发生什么。