How to enumerate a recursive datatype in Haskell?

后端 未结 4 1407
北荒
北荒 2020-12-01 12:57

This blog post has an interesting explanation of how to use the Omega monad to enumerate an arbitrary grammar diagonally. He offers an example of how to do so, resulting in

4条回答
  •  伪装坚强ぢ
    2020-12-01 13:38

    My first ugly approach was:

    allTerms :: Omega T
    allTerms = do
      which <- each [ 1,2,3 ]
      if which == 1 then
        return A
      else if which == 2 then do
        x <- allTerms
        return $ B x
      else do
        x <- allTerms
        y <- allTerms
        return $ C x y
    

    But then, after some cleaning up I reached this one liner

    import Control.Applicative
    import Control.Monad.Omega
    import Control.Monad
    
    allTerms :: Omega T
    allTerms = join $ each [return A, B <$> allTerms, C <$> allTerms <*> allTerms]
    

    Note that order matters: return A has to be the first choice in the list above, or allTerms will not terminate. Basically, the Omega monad ensures a "fair scheduling" among choices, saving you from e.g. infiniteList ++ something, but does not prevent infinite recursion.


    An even more elegant solution was suggested by Crazy FIZRUK, exploiting the Alternative instance of Omega.

    import Control.Applicative
    import Data.Foldable (asum)
    import Control.Monad.Omega
    
    allTerms :: Omega T
    allTerms = asum [ pure A
                    , B <$> allTerms
                    , C <$> allTerms <*> allTerms
                    ]
    

提交回复
热议问题