Search code examples
haskellgeneric-programminguniplate

Can uniplate's `universeBi` be used to retrieve nodes in a breadth-first fashion?


Is it possible to use Uniplate's universeBi to get the output in breadth-first-order? It appears the results are returned in a depth-first fashion. I'm wondering how I can use uniplate to retrieve the universeBi in a breadth-first fashion.

To illustrate, consider the following toy program:

{-# LANGUAGE DeriveDataTypeable #-}

import Data.Data
import Data.Generics.Uniplate.Data

data A = A B Int deriving (Data, Typeable)
data B = B Int   deriving (Data, Typeable)

val :: A
val = A (B 1) 2

ints :: [Int]
ints = universeBi val

I get:

*Main> ints
[1,2]

But this is depth-first, as 1 is obtained from the B node. I'd rather get it in the breadth-first order, i.e., receive [2,1]. Is this achievable in uniplate?


Solution

  • You can dig into the structure of the Str returned by biplate:

    layers :: Str a -> [[a]]
    layers Zero = []
    layers (One x) = [[x]]
    layers (Two f x) = catLayers (layers f) ([] : layers x)
      where catLayers [] ys = ys
            catLayers xs [] = xs
            catLayers (x : xs) (y : ys) = (x ++ y) : catLayers xs ys
    layersBi :: Biplate from to => from -> [[to]]
    layersBi = layers . fst . biplate
    breadthBi :: Biplate from to => from -> [to]
    breadthBi = concat . layersBi
    

    So now

    breadthBi (A (B 1) 2) :: [Int]
    -- = [2, 1]
    

    and

    data Tree a = Branch (Tree a) a (Tree a) | Leaf deriving (Data, Typeable)
    --       4
    --   2       6
    -- 1   3   5   7
    example = Branch (Branch (Branch Leaf 1 Leaf) 2 (Branch Leaf 3 Leaf)) 4 (Branch (Branch Leaf 5 Leaf) 6 (Branch Leaf 7 Leaf))
    (layersBi :: Data a => Tree a -> [[a]]) example
    -- = [[],[4],[2,6],[1,3,5,7]]
    

    I'm not sure if it's actually guaranteed that Str exactly reflects the structure of the data type, but it appears to. You could instead cook something out of the Data primitives if you have to.