I recently applied something like this for a monad list:
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)))
Unfortunately, you need to make a little newtype
shenanigans, because you cannot declare another instance of Monad []
, but other than that it works fine. Sort of
list ( do item1 <- Enumerable [0..] item2 <- Enumerable [1..] item3 <- Enumerable [2..] return (item1, item2, item3) )
gives you an endless list of all triples of a natural number whose second element is greater than or equal to 1, and the third element is greater than or equal to 2.
I checked, and if I was not mistaken, he must obey all the laws of the monad. It also works for leaf lists, where it will stop trying to find new items after it encounters a completely empty diagonal.
I am not familiar with the stream monad, so I canβt tell you what will happen if you do something like that too.
source share