improving data structures · giegerich and kurtz's purely functional suffix trees...
TRANSCRIPT
![Page 1: Improving Data Structures · Giegerich and Kurtz's Purely Functional Suffix Trees Data.SuffixTree.construct “abab” data FingerTree a = Empty | Single a | Deep !Int !(Digit a)](https://reader034.vdocuments.net/reader034/viewer/2022050518/5fa1a7475bc8a5406c4342e7/html5/thumbnails/1.jpg)
2008 Galois, Inc. All rights reserved.
Improving Data StructuresVisualization and Specialization
Don Stewart | wg2.8 | 2009-06-09
![Page 2: Improving Data Structures · Giegerich and Kurtz's Purely Functional Suffix Trees Data.SuffixTree.construct “abab” data FingerTree a = Empty | Single a | Deep !Int !(Digit a)](https://reader034.vdocuments.net/reader034/viewer/2022050518/5fa1a7475bc8a5406c4342e7/html5/thumbnails/2.jpg)
Functional Programming
Haiku or Karate?
![Page 3: Improving Data Structures · Giegerich and Kurtz's Purely Functional Suffix Trees Data.SuffixTree.construct “abab” data FingerTree a = Empty | Single a | Deep !Int !(Digit a)](https://reader034.vdocuments.net/reader034/viewer/2022050518/5fa1a7475bc8a5406c4342e7/html5/thumbnails/3.jpg)
2008 Galois, Inc. All rights reserved.
Motivation
Despite what you might suspect
Haskell programs are not yet always the fastest and shortest
GHC is smart, but not sufficiently smart
![Page 4: Improving Data Structures · Giegerich and Kurtz's Purely Functional Suffix Trees Data.SuffixTree.construct “abab” data FingerTree a = Empty | Single a | Deep !Int !(Digit a)](https://reader034.vdocuments.net/reader034/viewer/2022050518/5fa1a7475bc8a5406c4342e7/html5/thumbnails/4.jpg)
2008 Galois, Inc. All rights reserved.
<shootout>
![Page 5: Improving Data Structures · Giegerich and Kurtz's Purely Functional Suffix Trees Data.SuffixTree.construct “abab” data FingerTree a = Empty | Single a | Deep !Int !(Digit a)](https://reader034.vdocuments.net/reader034/viewer/2022050518/5fa1a7475bc8a5406c4342e7/html5/thumbnails/5.jpg)
2008 Galois, Inc. All rights reserved.
What can we do about it?
• Still more optimizations– Fusion (complexity changing!)– Constructor specialization– Domain-specific rewrite rules
• More and better parallelism
• Smarter runtime (constructor tag bits)
• Special purpose libraries– Data.Bytestring, Data.Binary
![Page 6: Improving Data Structures · Giegerich and Kurtz's Purely Functional Suffix Trees Data.SuffixTree.construct “abab” data FingerTree a = Empty | Single a | Deep !Int !(Digit a)](https://reader034.vdocuments.net/reader034/viewer/2022050518/5fa1a7475bc8a5406c4342e7/html5/thumbnails/6.jpg)
2008 Galois, Inc. All rights reserved.
What about regular data types?
• Custom types and optimizations are great, but lots of programs use standard polymorphic data types
• Is there anything we can improve there?
• We need a nice way to look at how data structures are actually represented
![Page 7: Improving Data Structures · Giegerich and Kurtz's Purely Functional Suffix Trees Data.SuffixTree.construct “abab” data FingerTree a = Empty | Single a | Deep !Int !(Digit a)](https://reader034.vdocuments.net/reader034/viewer/2022050518/5fa1a7475bc8a5406c4342e7/html5/thumbnails/7.jpg)
2008 Galois, Inc. All rights reserved.
A secret primop: unpackClosure#
unpackClosure# :: a → (# Addr#, Array# b, ByteArray# #)
• Added for the GHCi Debugger• Can write lots of interesting things with it:
– sizeOfClosure :: a → Int– hasUnboxedFields :: a → Bool– view :: a → Graph
• Smuggling runtime reflection into Haskell
![Page 8: Improving Data Structures · Giegerich and Kurtz's Purely Functional Suffix Trees Data.SuffixTree.construct “abab” data FingerTree a = Empty | Single a | Deep !Int !(Digit a)](https://reader034.vdocuments.net/reader034/viewer/2022050518/5fa1a7475bc8a5406c4342e7/html5/thumbnails/8.jpg)
2008 Galois, Inc. All rights reserved.
Everyday data types
<vacuum + cairo>
![Page 9: Improving Data Structures · Giegerich and Kurtz's Purely Functional Suffix Trees Data.SuffixTree.construct “abab” data FingerTree a = Empty | Single a | Deep !Int !(Digit a)](https://reader034.vdocuments.net/reader034/viewer/2022050518/5fa1a7475bc8a5406c4342e7/html5/thumbnails/9.jpg)
2008 Galois, Inc. All rights reserved.
Regular types in 3D
![Page 10: Improving Data Structures · Giegerich and Kurtz's Purely Functional Suffix Trees Data.SuffixTree.construct “abab” data FingerTree a = Empty | Single a | Deep !Int !(Digit a)](https://reader034.vdocuments.net/reader034/viewer/2022050518/5fa1a7475bc8a5406c4342e7/html5/thumbnails/10.jpg)
2008 Galois, Inc. All rights reserved.
Regular types in 3D
![Page 11: Improving Data Structures · Giegerich and Kurtz's Purely Functional Suffix Trees Data.SuffixTree.construct “abab” data FingerTree a = Empty | Single a | Deep !Int !(Digit a)](https://reader034.vdocuments.net/reader034/viewer/2022050518/5fa1a7475bc8a5406c4342e7/html5/thumbnails/11.jpg)
Deep[20]
One Deep[15] Four
1 One Empty Four 17 18 19 20
Node3[3] Node3[3] Node3[3] Node3[3] Node3[3]
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
data FingerTree a = Empty | Single a | Deep !Int
!(Digit a) (FingerTree (Node a)) !(Digit a)
data Digit a = One a | Two a a | Three a a a | Four a a a a
Hinze & Patterson's finger tree
Data.Sequence.fromList [1..20]
![Page 12: Improving Data Structures · Giegerich and Kurtz's Purely Functional Suffix Trees Data.SuffixTree.construct “abab” data FingerTree a = Empty | Single a | Deep !Int !(Digit a)](https://reader034.vdocuments.net/reader034/viewer/2022050518/5fa1a7475bc8a5406c4342e7/html5/thumbnails/12.jpg)
RandomAccessList[10]
(:)
(,) (:)
3 Node (,) []
1 Leaf Leaf 7 Node
2 3 4 Node Node
5 Leaf Leaf 8 Leaf Leaf
6 7 9 10
Okasaki's random access list:
Data.RandomAccessList.fromList [1..10]
data FingerTree a = Empty | Single a | Deep !Int
!(Digit a) (FingerTree (Node a)) !(Digit a)
data Digit a = One a | Two a a | Three a a a | Four a a a a
data RandomAccessList a = RandomAccessList !Int [(Int, CBTree a)]
data CBTree a = Leaf a | Node a !(CBTree a) !(CBTree a)
![Page 13: Improving Data Structures · Giegerich and Kurtz's Purely Functional Suffix Trees Data.SuffixTree.construct “abab” data FingerTree a = Empty | Single a | Deep !Int !(Digit a)](https://reader034.vdocuments.net/reader034/viewer/2022050518/5fa1a7475bc8a5406c4342e7/html5/thumbnails/13.jpg)
Node
(:)
(,) (:)
(,)
Leaf
(,)
[]
(:)
Sum[3]
(,)
'a'(:)
(:)
'b'(:)
'b'
'a'
(:)
Sum[2]
Giegerich and Kurtz's Purely Functional Suffix Trees
Data.SuffixTree.construct “abab”
data FingerTree a = Empty | Single a | Deep !Int
!(Digit a) (FingerTree (Node a)) !(Digit a)
data Digit a = One a | Two a a | Three a a a | Four a a a a
data STree a = Node [Edge a] | Leaf
type Edge a = (Prefix a, STree a)
newtype Prefix a = Prefix ([a], Length a)
data Length a = Exactly !Int | Sum !Int [a]
data STree a = Node [Edge a] | Leaf
type Edge a = (Prefix a, STree a)
data Length a = Exactly !Int | Sum !Int [a]
data STree a = Node [Edge a] | Leaf
type Edge a = (Prefix a, STree a)
data Length a = Exactly !Int | Sum !Int [a]
![Page 14: Improving Data Structures · Giegerich and Kurtz's Purely Functional Suffix Trees Data.SuffixTree.construct “abab” data FingerTree a = Empty | Single a | Deep !Int !(Digit a)](https://reader034.vdocuments.net/reader034/viewer/2022050518/5fa1a7475bc8a5406c4342e7/html5/thumbnails/14.jpg)
Hey's AVL tree library
Data.Tree.AVL.asTreeL [1..15]
data FingerTree a = Empty | Single a | Deep !Int
!(Digit a) (FingerTree (Node a)) !(Digit a)
data Digit a = One a | Two a a | Three a a a | Four a a a a
data STree a = Node [Edge a] | Leaf
type Edge a = (Prefix a, STree a)
data Length a = Exactly !Int | Sum !Int [a]
data STree a = Node [Edge a] | Leaf
type Edge a = (Prefix a, STree a)
data Length a = Exactly !Int | Sum !Int [a]
Z
Z 8Z
Z 4Z Z 12Z
Z 2ZZ6 Z Z 10Z Z 14Z
E135 7 9 11 13 15
data AVL e = E | N (AVL e) e (AVL e) | Z (AVL e) e (AVL e) | P (AVL e) e (AVL e)
![Page 15: Improving Data Structures · Giegerich and Kurtz's Purely Functional Suffix Trees Data.SuffixTree.construct “abab” data FingerTree a = Empty | Single a | Deep !Int !(Digit a)](https://reader034.vdocuments.net/reader034/viewer/2022050518/5fa1a7475bc8a5406c4342e7/html5/thumbnails/15.jpg)
2008 Galois, Inc. All rights reserved.
An Opportunity!
• Lots of parametrically polymorphic container types in common use
• All with (slow!) uniform representationdata Maybe a = Nothing | Just a
• But we “know” the type of 'a' statically!readInt :: ByteString Maybe Int→
• How much faster do things get if we could specialize data types at each use!?
![Page 16: Improving Data Structures · Giegerich and Kurtz's Purely Functional Suffix Trees Data.SuffixTree.construct “abab” data FingerTree a = Empty | Single a | Deep !Int !(Digit a)](https://reader034.vdocuments.net/reader034/viewer/2022050518/5fa1a7475bc8a5406c4342e7/html5/thumbnails/16.jpg)
2008 Galois, Inc. All rights reserved.
Challenge
Make parametrically polymorphic structures as efficient as monomorphic ones, when
used at a known type
Remove the uniform representation penalty
![Page 17: Improving Data Structures · Giegerich and Kurtz's Purely Functional Suffix Trees Data.SuffixTree.construct “abab” data FingerTree a = Empty | Single a | Deep !Int !(Digit a)](https://reader034.vdocuments.net/reader034/viewer/2022050518/5fa1a7475bc8a5406c4342e7/html5/thumbnails/17.jpg)
2008 Galois, Inc. All rights reserved.
Inspiration: Habit and DPH
• Data Parallel Haskell– List-like interface– Radical restructuring under the hood
(flattening)
• Habit: PSU/Galois “Systems Haskell”– Per-constructor representation annotations
• Whole-program compilers (a la JHC)– GHC Inliner is sort of there already...
![Page 18: Improving Data Structures · Giegerich and Kurtz's Purely Functional Suffix Trees Data.SuffixTree.construct “abab” data FingerTree a = Empty | Single a | Deep !Int !(Digit a)](https://reader034.vdocuments.net/reader034/viewer/2022050518/5fa1a7475bc8a5406c4342e7/html5/thumbnails/18.jpg)
2008 Galois, Inc. All rights reserved.
Goals: uniform improvements for polymorphic containers
• Specialize polymorphic containers for each element type
• Retain a user interface of regular parametric polymorphism
– Libraries should be mostly unchanged
• Set up uniform rules for specialization– Happy to sacrifice laziness for speed
• Open extn.: allow ad hoc representations
![Page 19: Improving Data Structures · Giegerich and Kurtz's Purely Functional Suffix Trees Data.SuffixTree.construct “abab” data FingerTree a = Empty | Single a | Deep !Int !(Digit a)](https://reader034.vdocuments.net/reader034/viewer/2022050518/5fa1a7475bc8a5406c4342e7/html5/thumbnails/19.jpg)
2008 Galois, Inc. All rights reserved.
No more uniform representations!(:)
(,) (:)
1 1 (,) (:)
2
1 (,) (:)
2 (,) (:)
3
1 (,) (:)
2 (,) []
3
Uniform list of pairs
[ (x,y) | x ← [1..3], y ← [1..x] ]
BEFORE
![Page 20: Improving Data Structures · Giegerich and Kurtz's Purely Functional Suffix Trees Data.SuffixTree.construct “abab” data FingerTree a = Empty | Single a | Deep !Int !(Digit a)](https://reader034.vdocuments.net/reader034/viewer/2022050518/5fa1a7475bc8a5406c4342e7/html5/thumbnails/20.jpg)
Specialized [(Int,Int)]
fromList [ pair x y | x ← [1..3], y ← [1..x] ]
ConsPairIntInt[1,1]
ConsPairIntInt[2,1]
ConsPairIntInt[2,2]
ConsPairIntInt[3,1]
ConsPairIntInt[3,2]
ConsPairIntInt[3,3]
EmptyPairIntInt
AFTER :)
![Page 21: Improving Data Structures · Giegerich and Kurtz's Purely Functional Suffix Trees Data.SuffixTree.construct “abab” data FingerTree a = Empty | Single a | Deep !Int !(Digit a)](https://reader034.vdocuments.net/reader034/viewer/2022050518/5fa1a7475bc8a5406c4342e7/html5/thumbnails/21.jpg)
2008 Galois, Inc. All rights reserved.
Mechanism is here!
• Type classes– Make decisions on a per-type basis– Open– Ad hoc
• Class-associated data types– Per-instance actual data types– Representation types added separately to
function on those types
![Page 22: Improving Data Structures · Giegerich and Kurtz's Purely Functional Suffix Trees Data.SuffixTree.construct “abab” data FingerTree a = Empty | Single a | Deep !Int !(Digit a)](https://reader034.vdocuments.net/reader034/viewer/2022050518/5fa1a7475bc8a5406c4342e7/html5/thumbnails/22.jpg)
2008 Galois, Inc. All rights reserved.
Self-optimizing tuples
{# LANGUAGE TypeFamilies, MultiParamTypeClasses #}
data (,) a b = (a, b)
class AdaptPair a b where
data Pair a b no representation yet
curry :: (Pair a b > c) > a > b > c
fst :: Pair a b > a
snd :: Pair a b > b
![Page 23: Improving Data Structures · Giegerich and Kurtz's Purely Functional Suffix Trees Data.SuffixTree.construct “abab” data FingerTree a = Empty | Single a | Deep !Int !(Digit a)](https://reader034.vdocuments.net/reader034/viewer/2022050518/5fa1a7475bc8a5406c4342e7/html5/thumbnails/23.jpg)
2008 Galois, Inc. All rights reserved.
Functions on adaptive types
uncurry :: (a > b > c) > ((a, b) > c)
uncurry f p = f (fst p) (snd p)
uncurry :: AdaptPair a b
=> (a > b > c) > (Pair a b > c)
uncurry f p = f (fst p) (snd p)
pair :: AdaptPair a b
=> a > b > Pair a b
pair = curry id
![Page 24: Improving Data Structures · Giegerich and Kurtz's Purely Functional Suffix Trees Data.SuffixTree.construct “abab” data FingerTree a = Empty | Single a | Deep !Int !(Digit a)](https://reader034.vdocuments.net/reader034/viewer/2022050518/5fa1a7475bc8a5406c4342e7/html5/thumbnails/24.jpg)
2008 Galois, Inc. All rights reserved.
Plugging in the representation type
instance AdaptPair Int Double where
We can use our unpacking tricks per type
data Pair Int Double
= PairIntDouble {# UNPACK #}!Int
{# UNPACK #}!Double
boilerplate views
fst (PairIntDouble a _) = a
snd (PairIntDouble _ b) = b
curry f x y = f (PairIntDouble x y)
![Page 25: Improving Data Structures · Giegerich and Kurtz's Purely Functional Suffix Trees Data.SuffixTree.construct “abab” data FingerTree a = Empty | Single a | Deep !Int !(Digit a)](https://reader034.vdocuments.net/reader034/viewer/2022050518/5fa1a7475bc8a5406c4342e7/html5/thumbnails/25.jpg)
2008 Galois, Inc. All rights reserved.
Non-uniform representations
![Page 26: Improving Data Structures · Giegerich and Kurtz's Purely Functional Suffix Trees Data.SuffixTree.construct “abab” data FingerTree a = Empty | Single a | Deep !Int !(Digit a)](https://reader034.vdocuments.net/reader034/viewer/2022050518/5fa1a7475bc8a5406c4342e7/html5/thumbnails/26.jpg)
2008 Galois, Inc. All rights reserved.
Joy
• Greater data density!• More strictness info for common types
– More CPR possible!– Should remove heap checks
• Interacts well with other optimizations– Fusion
• Can use ad hoc representation decisions
![Page 27: Improving Data Structures · Giegerich and Kurtz's Purely Functional Suffix Trees Data.SuffixTree.construct “abab” data FingerTree a = Empty | Single a | Deep !Int !(Digit a)](https://reader034.vdocuments.net/reader034/viewer/2022050518/5fa1a7475bc8a5406c4342e7/html5/thumbnails/27.jpg)
2008 Galois, Inc. All rights reserved.
Careful... smart compiler wanted
• Don't want any dictionaries left over– GHC already OK at removing them
• Will rely heavily on inlining– Fake whole program compiler
• Need a lot of instances– Increases compile times...
• Library functions as templates, not directly reused (inlined, then specialized)
![Page 28: Improving Data Structures · Giegerich and Kurtz's Purely Functional Suffix Trees Data.SuffixTree.construct “abab” data FingerTree a = Empty | Single a | Deep !Int !(Digit a)](https://reader034.vdocuments.net/reader034/viewer/2022050518/5fa1a7475bc8a5406c4342e7/html5/thumbnails/28.jpg)
2008 Galois, Inc. All rights reserved.
Creating instances
• Need instances for all combination of common element types
– SYB generics to derive them– Template Haskell now supports unpacking
pragmas and associated data types
• GHC gets a sore head when I enumerate all element types as instances
![Page 29: Improving Data Structures · Giegerich and Kurtz's Purely Functional Suffix Trees Data.SuffixTree.construct “abab” data FingerTree a = Empty | Single a | Deep !Int !(Digit a)](https://reader034.vdocuments.net/reader034/viewer/2022050518/5fa1a7475bc8a5406c4342e7/html5/thumbnails/29.jpg)
2008 Galois, Inc. All rights reserved.
Lists
class AdaptList a where
data List a
empty :: List a
cons :: a > List a > List a
null :: List a > Bool
head :: List a > a
tail :: List a > List a
![Page 30: Improving Data Structures · Giegerich and Kurtz's Purely Functional Suffix Trees Data.SuffixTree.construct “abab” data FingerTree a = Empty | Single a | Deep !Int !(Digit a)](https://reader034.vdocuments.net/reader034/viewer/2022050518/5fa1a7475bc8a5406c4342e7/html5/thumbnails/30.jpg)
2008 Galois, Inc. All rights reserved.
List functions
fromList :: AdaptList a => [a] > List a
fromList [] = empty
fromList (x:xs) = x `cons` fromList xs
(++) :: AdaptList a
=> List a > List a > List a
(++) xs ys
| null xs = ys
| otherwise = head xs `cons` tail xs ++ ys
![Page 31: Improving Data Structures · Giegerich and Kurtz's Purely Functional Suffix Trees Data.SuffixTree.construct “abab” data FingerTree a = Empty | Single a | Deep !Int !(Digit a)](https://reader034.vdocuments.net/reader034/viewer/2022050518/5fa1a7475bc8a5406c4342e7/html5/thumbnails/31.jpg)
2008 Galois, Inc. All rights reserved.
List functions
map :: (AdaptList a, AdaptList b)
=> (a > b) > List a > List b
map f as = go as
where
go xs
| null xs = empty
| otherwise = f (head xs) `cons` go (tail xs)
![Page 32: Improving Data Structures · Giegerich and Kurtz's Purely Functional Suffix Trees Data.SuffixTree.construct “abab” data FingerTree a = Empty | Single a | Deep !Int !(Digit a)](https://reader034.vdocuments.net/reader034/viewer/2022050518/5fa1a7475bc8a5406c4342e7/html5/thumbnails/32.jpg)
2008 Galois, Inc. All rights reserved.
List instances
instance AdaptList Int where
data List Int
= EmptyInt
| ConsInt {# UNPACK #}!Int (List Int)
empty = EmptyInt
cons = ConsInt
null EmptyInt = True
null _ = False
head EmptyInt = errorEmptyList "head"
head (ConsInt x _) = x
![Page 33: Improving Data Structures · Giegerich and Kurtz's Purely Functional Suffix Trees Data.SuffixTree.construct “abab” data FingerTree a = Empty | Single a | Deep !Int !(Digit a)](https://reader034.vdocuments.net/reader034/viewer/2022050518/5fa1a7475bc8a5406c4342e7/html5/thumbnails/33.jpg)
2008 Galois, Inc. All rights reserved.
Performance (preliminary)
• AdaptList a vs [a]– Pipelines of list functions: 15 – 30% faster– GC: 15 – 40% less allocation– More unboxing
• Need to be sure to have reliable dictionary removal
![Page 34: Improving Data Structures · Giegerich and Kurtz's Purely Functional Suffix Trees Data.SuffixTree.construct “abab” data FingerTree a = Empty | Single a | Deep !Int !(Digit a)](https://reader034.vdocuments.net/reader034/viewer/2022050518/5fa1a7475bc8a5406c4342e7/html5/thumbnails/34.jpg)
2008 Galois, Inc. All rights reserved.
Data.List.sum
sum :: (AdaptList a, Num a) => List a > a
sum l = go l 0
where
go xs !a
| null xs = a
| otherwise = go (tail xs) (a + head xs)
![Page 35: Improving Data Structures · Giegerich and Kurtz's Purely Functional Suffix Trees Data.SuffixTree.construct “abab” data FingerTree a = Empty | Single a | Deep !Int !(Digit a)](https://reader034.vdocuments.net/reader034/viewer/2022050518/5fa1a7475bc8a5406c4342e7/html5/thumbnails/35.jpg)
2008 Galois, Inc. All rights reserved.
Data.List.sum
Use at List Int type:
$wgo :: List Int > Int# > Int#
$wgo xs n = case xs of
EmptyInt > n
ConsInt x xs > $wgo xs (+# n x)
No unpacking. No views. No dictionaries.Elements aready in unboxed form!
![Page 36: Improving Data Structures · Giegerich and Kurtz's Purely Functional Suffix Trees Data.SuffixTree.construct “abab” data FingerTree a = Empty | Single a | Deep !Int !(Digit a)](https://reader034.vdocuments.net/reader034/viewer/2022050518/5fa1a7475bc8a5406c4342e7/html5/thumbnails/36.jpg)
2008 Galois, Inc. All rights reserved.
Trees and Sets
• Unpacking element types: obvious now• Other ad hoc representation changes
– Coalescing nodes in trees• Experiments: strong increase in density
– Non-representation of singletons– Bools to bits
![Page 37: Improving Data Structures · Giegerich and Kurtz's Purely Functional Suffix Trees Data.SuffixTree.construct “abab” data FingerTree a = Empty | Single a | Deep !Int !(Digit a)](https://reader034.vdocuments.net/reader034/viewer/2022050518/5fa1a7475bc8a5406c4342e7/html5/thumbnails/37.jpg)
2008 Galois, Inc. All rights reserved.
Non-uniform polymorphic containers
• So it is possible... generic approach to faster polymorphic structures
– Can we do this automatically?– Rules based on strictness information?
• CPR enabled, can we do it on sum types?• Treats inliner as poor man's whole
program optimizer• Nice: compiler extensions in the language
![Page 38: Improving Data Structures · Giegerich and Kurtz's Purely Functional Suffix Trees Data.SuffixTree.construct “abab” data FingerTree a = Empty | Single a | Deep !Int !(Digit a)](https://reader034.vdocuments.net/reader034/viewer/2022050518/5fa1a7475bc8a5406c4342e7/html5/thumbnails/38.jpg)
2008 Galois, Inc. All rights reserved.
Where is all this?
• On Hackage– cabal install vacuumcairo – cabal install adaptivecontainers
• Other structures appearing (finger trees)• BTW:
– cabal install fingertree– cabal install randomaccesslist– cabal install suffixtree– cabal install avltree
![Page 39: Improving Data Structures · Giegerich and Kurtz's Purely Functional Suffix Trees Data.SuffixTree.construct “abab” data FingerTree a = Empty | Single a | Deep !Int !(Digit a)](https://reader034.vdocuments.net/reader034/viewer/2022050518/5fa1a7475bc8a5406c4342e7/html5/thumbnails/39.jpg)
Thanks!