binary-tree-0.1.0.0: src/Data/Tree/Binary/Internal.hs
{-# LANGUAGE CPP #-}
#if __GLASGOW_HASKELL__ >= 703
{-# LANGUAGE Safe #-}
#endif
-- |
-- Module : Data.Tree.Binary.Internal
-- Description : Common utility functions for the binary-tree package.
-- Copyright : (c) Donnacha Oisín Kidney 2018
-- License : MIT
-- Maintainer : mail@doisinkidney.com
-- Portability : portable
--
-- = WARNING
--
-- This module is considered __internal__.
--
-- The Package Versioning Policy __does not apply__.
--
-- This contents of this module may change __in any way whatsoever__
-- and __without any warning__ between minor versions of this package.
--
-- Authors importing this module are expected to track development
-- closely.
--
-- = Description
--
-- This module exports some utility functions common to both tree modules.
module Data.Tree.Binary.Internal
( -- * Drawing Trees
Drawing(..)
, toDrawing
, runDrawing
, drawTree
-- * State
, State(..)
, evalState
-- * Reimplementations for older GHCs
, Identity(..)
) where
import Prelude hiding (
#if MIN_VERSION_base(4,8,0)
Functor(..),Applicative, (<$>), foldMap, Monoid
#endif
)
#if MIN_VERSION_base(4,8,0)
import Data.Functor.Identity (Identity (..))
#endif
import Control.Applicative (Applicative (pure, (<*>)))
import Data.Functor (Functor (fmap))
bool :: a -> a -> Bool -> a
bool f _ False = f
bool _ t True = t
{-# INLINE bool #-}
--------------------------------------------------------------------------------
-- Drawing Trees
--------------------------------------------------------------------------------
-- | An abstract representation of a textual drawing of a tree.
data Drawing
= Nil
| NewLine !Drawing
| BottomLeft !Drawing
| BottomRight !Drawing
| TopLeft !Drawing
| TopRight !Drawing
| Vert !Drawing
| Split !Drawing
| Item !String Drawing
| Padding {-# UNPACK #-} !Int !Drawing
-- | A function to convert a drawing to a string.
runDrawing :: Drawing -> ShowS
runDrawing Nil = showString "╼\n"
runDrawing ys = go ys
where
go Nil st = st
go (NewLine xs) st = '\n' : go xs st
go (BottomLeft xs) st = '└' : go xs st
go (BottomRight xs) st = '┘' : go xs st
go (TopLeft xs) st = '┌' : go xs st
go (TopRight xs) st = '┐' : go xs st
go (Vert xs) st = '│' : go xs st
go (Split xs) st = '┤' : go xs st
go (Item x xs) st = x ++ go xs st
go (Padding i xs) st = pad i (go xs st)
pad 0 = id
pad n = showChar ' ' . pad (n-1)
{-# INLINE runDrawing #-}
-- | Given an uncons function for a binary tree, draw the tree in a structured,
-- human-readable way.
drawTree :: (a -> String) -> (t -> Maybe (a, t, t)) -> t -> ShowS
drawTree sf project = runDrawing . toDrawing sf project
{-# INLINE drawTree #-}
-- | Convert a tree to the Drawing type. This function is exposed so that users
-- may replace the call to 'runDrawing' in 'drawTree' with a more efficient
-- implementation that could use (for example) 'Text'.
toDrawing :: (a -> String) -> (t -> Maybe (a, t, t)) -> t -> Drawing
toDrawing sf project = maybe Nil root . project
where
go dir k len (x, l, r) = node dir len x (project l) (project r) k
-- Root special case (no incoming direction)
root (x, l, r) =
maybeAp (go True id xlen) ls $
Item xshw $ endc ls rs $ NewLine $ maybeAp (go False id xlen) rs Nil
where
xshw = sf x
xlen = length xshw
ls = project l
rs = project r
node up i x ls rs k b =
maybeAp (branch True) ls $
k $
pad i $
bool BottomLeft TopLeft up $
Item xshw $ endc ls rs $ NewLine $ maybeAp (branch False) rs b
where
xshw = sf x
xlen = length xshw
branch d
| d == up = go d (k . pad i) (xlen + 1)
| otherwise = go d (k . pad i . Vert) xlen
{-# INLINE branch #-}
{-# INLINE node #-}
endc Nothing Nothing b = b
endc (Just _) Nothing b = BottomRight b
endc Nothing (Just _) b = TopRight b
endc (Just _) (Just _) b = Split b
{-# INLINE endc #-}
pad i (Padding j xs) = Padding (i+j) xs
pad i xs = Padding i xs
{-# INLINE pad #-}
maybeAp _ Nothing y = y
maybeAp f (Just x) y = f x y
{-# INLINE maybeAp #-}
{-# INLINE toDrawing #-}
--------------------------------------------------------------------------------
-- State
--------------------------------------------------------------------------------
-- | A clone of Control.Monad.State.Strict, reimplemented here to avoid the
-- dependency.
newtype State s a = State
{ runState :: s -> (a, s)
}
instance Functor (State s) where
fmap f xs =
State
(\s ->
case runState xs s of
(x, s') -> (f x, s'))
{-# INLINE fmap #-}
instance Applicative (State s) where
pure x = State (\s -> (x, s))
{-# INLINE pure #-}
fs <*> xs =
State
(\s ->
case runState fs s of
(f, s') ->
case runState xs s' of
(x, s'') -> (f x, s''))
{-# INLINE (<*>) #-}
-- | Evaluate a stateful action.
evalState :: State s a -> s -> a
evalState xs s = fst (runState xs s)
{-# INLINE evalState #-}
--------------------------------------------------------------------------------
-- Identity
--------------------------------------------------------------------------------
#if !MIN_VERSION_base(4,8,0)
-- | A clone of Data.Functor.Identity, reimplemented here when it's not yet
-- included in base.
newtype Identity a = Identity {runIdentity :: a}
instance Functor Identity where
fmap f (Identity x) = Identity (f x)
instance Applicative Identity where
pure = Identity
Identity f <*> Identity x = Identity (f x)
#endif