Strictification of Circular Programs J.P. Fernandes 1 J. Saraiva 1 D. - - PowerPoint PPT Presentation

strictification of circular programs
SMART_READER_LITE
LIVE PREVIEW

Strictification of Circular Programs J.P. Fernandes 1 J. Saraiva 1 D. - - PowerPoint PPT Presentation

Strictification of Circular Programs J.P. Fernandes 1 J. Saraiva 1 D. Seidel 2 ander 2 J. Voigtl 1 University of Minho 2 University of Bonn IFIP WG 2.1 meeting #66 Multi-Traversal Programs data Tree a = Leaf a | Fork (Tree a ) (Tree a ) treemin


slide-1
SLIDE 1

Strictification of Circular Programs

J.P. Fernandes1

  • J. Saraiva1
  • D. Seidel2
  • J. Voigtl¨

ander2

1University of Minho 2University of Bonn

IFIP WG 2.1 meeting #66

slide-2
SLIDE 2

Multi-Traversal Programs data Tree a = Leaf a | Fork (Tree a) (Tree a) treemin :: Tree Int → Int treemin (Leaf n) = n treemin (Fork l r) = min (treemin l) (treemin r) replace :: Tree Int → Int → Tree Int replace (Leaf n) m = Leaf m replace (Fork l r) m = Fork (replace l m) (replace r m) run :: Tree Int → Tree Int run t = replace t (treemin t)

1

slide-3
SLIDE 3

Circular Programs [Bird 1984] The previous can be transformed into: repmin :: Tree Int → Int → (Tree Int, Int) repmin (Leaf n) m = (Leaf m, n) repmin (Fork l r) m = (Fork l′ r ′, min m1 m2) where (l′, m1) = repmin l m (r ′, m2) = repmin r m run :: Tree Int → Tree Int run t = let (nt, m) = repmin t m in nt Only one traversal!

2

slide-4
SLIDE 4

Circular Programs Other uses/appearances of circular programs:

◮ as attribute grammar realization

[Johnsson 1987, Kuiper & Swierstra 1987]

3

slide-5
SLIDE 5

Circular Programs Other uses/appearances of circular programs:

◮ as attribute grammar realization

[Johnsson 1987, Kuiper & Swierstra 1987]

◮ as algorithmic tool

[Jones & Gibbons 1993, Okasaki 2000]

3

slide-6
SLIDE 6

Circular Programs Other uses/appearances of circular programs:

◮ as attribute grammar realization

[Johnsson 1987, Kuiper & Swierstra 1987]

◮ as algorithmic tool

[Jones & Gibbons 1993, Okasaki 2000]

◮ as target for deforestation/fusion techniques

[V. 2004, Fernandes et al. 2007]

◮ . . .

3

slide-7
SLIDE 7

But:

4

slide-8
SLIDE 8

The Aim

repmin (Leaf n) m = (Leaf m, n) repmin (Fork l r) m = (Fork l′ r ′, min m1 m2) where (l′, m1) = repmin l m (r ′, m2) = repmin r m run t = let (nt, m) = repmin t m in nt treemin (Leaf n) = n treemin (Fork l r) = min (treemin l) (treemin r) replace (Leaf n) m = Leaf m replace (Fork l r) m = Fork (replace l m) (replace r m) run t = replace t (treemin t)

?

  • 5
slide-9
SLIDE 9

Getting Started Let us have a look at: repmin :: Tree Int → Int → (Tree Int, Int) repmin (Leaf n) m = (Leaf m, n) repmin (Fork l r) m = (Fork l′ r ′, min m1 m2) where (l′, m1) = repmin l m (r ′, m2) = repmin r m and try to learn something about it.

6

slide-10
SLIDE 10

Getting Started Let us have a look at: repmin :: Tree Int → Int → (Tree Int, Int) repmin (Leaf n) m = (Leaf m, n) repmin (Fork l r) m = (Fork l′ r ′, min m1 m2) where (l′, m1) = repmin l m (r ′, m2) = repmin r m and try to learn something about it. What better way to learn something about a function than looking at its inferred type?

6

slide-11
SLIDE 11

Dependency Analysis for Free It turns out that (from the given equations): repmin :: Tree Int → b → (Tree b, Int)

7

slide-12
SLIDE 12

Dependency Analysis for Free It turns out that (from the given equations): repmin :: Tree Int → b → (Tree b, Int) Very interesting: the second output cannot possibly depend on the second input!

7

slide-13
SLIDE 13

Dependency Analysis for Free It turns out that (from the given equations): repmin :: Tree Int → b → (Tree b, Int) Very interesting: the second output cannot possibly depend on the second input! Hence, for every t :: Tree Int and m1, m2: snd (repmin t m1) ≡ snd (repmin t m2)

7

slide-14
SLIDE 14

Dependency Analysis for Free It turns out that (from the given equations): repmin :: Tree Int → b → (Tree b, Int) Very interesting: the second output cannot possibly depend on the second input! Hence, for every t :: Tree Int and m1, m2: snd (repmin t m1) ≡ snd (repmin t m2) Indeed, for every t :: Tree Int and m: snd (repmin t m) ≡ snd (repmin t ⊥)

7

slide-15
SLIDE 15

Achieving Noncircularity run t = let (nt, m) = repmin t m in nt

8

slide-16
SLIDE 16

Achieving Noncircularity run t = let (nt, m) = repmin t m in nt run t = let (nt, ) = repmin t m ( , m) = repmin t m in nt

by referential transparency

  • 8
slide-17
SLIDE 17

Achieving Noncircularity run t = let (nt, m) = repmin t m in nt run t = let (nt, ) = repmin t m ( , m) = repmin t m in nt run t = let (nt, ) = repmin t m ( , m) = repmin t ⊥ in nt

by referential transparency

  • by snd (repmin t m) ≡ snd (repmin t ⊥)
  • 8
slide-18
SLIDE 18

Towards Efficiency Instead of having: ( , m) = repmin t ⊥

9

slide-19
SLIDE 19

Towards Efficiency Instead of having: ( , m) = repmin t ⊥ let us define a specialized function: repminsnd :: Tree Int → Int repminsnd t = snd (repmin t ⊥)

9

slide-20
SLIDE 20

Towards Efficiency Instead of having: ( , m) = repmin t ⊥ let us define a specialized function: repminsnd :: Tree Int → Int repminsnd t = snd (repmin t ⊥) which then lets us replace the above binding with: m = repminsnd t

9

slide-21
SLIDE 21

Towards Efficiency Instead of having: ( , m) = repmin t ⊥ let us define a specialized function: repminsnd :: Tree Int → Int repminsnd t = snd (repmin t ⊥) which then lets us replace the above binding with: m = repminsnd t Using fold/unfold-transformations, it is easy to derive a direct definition for repminsnd!

9

slide-22
SLIDE 22

Towards Efficiency Resulting definition: repminsnd :: Tree Int → Int repminsnd (Leaf n) = n repminsnd (Fork l r) = min (repminsnd l) (repminsnd r)

10

slide-23
SLIDE 23

Towards Efficiency Resulting definition: repminsnd :: Tree Int → Int repminsnd (Leaf n) = n repminsnd (Fork l r) = min (repminsnd l) (repminsnd r) Similarly, for (nt, ) = repmin t m: repminfst :: Tree Int → b → Tree b repminfst (Leaf n) m = Leaf m repminfst (Fork l r) m = Fork (repminfst l m) (repminfst r m)

10

slide-24
SLIDE 24

Final Program run :: Tree Int → Tree Int run t = let (nt, ) = repmin t m ( , m) = repmin t ⊥ in nt run :: Tree Int → Tree Int run t = repminfst t (repminsnd t)

by fst (repmin t m) ≡ repminfst t m, snd (repmin t ⊥) ≡ repminsnd t

  • 11
slide-25
SLIDE 25

A General Strategy

  • 1. Detect dependencies of outputs of a circular call
  • n its inputs. Preferrably, do this light-weight.

As far as possible, type-based [Kobayashi 2001].

12

slide-26
SLIDE 26

A General Strategy

  • 1. Detect dependencies of outputs of a circular call
  • n its inputs. Preferrably, do this light-weight.

As far as possible, type-based [Kobayashi 2001].

  • 2. Naively split the circular call into several ones,

each computing only one of the outputs. Exploit information from above to decouple these calls.

12

slide-27
SLIDE 27

A General Strategy

  • 1. Detect dependencies of outputs of a circular call
  • n its inputs. Preferrably, do this light-weight.

As far as possible, type-based [Kobayashi 2001].

  • 2. Naively split the circular call into several ones,

each computing only one of the outputs. Exploit information from above to decouple these calls.

  • 3. Specialize the different calls (using partial

evaluation, slicing, . . . ) to work only with those pieces of input and output that are relevant.

12

slide-28
SLIDE 28

A More Challenging Example: Breadth-First Numbering [Okasaki 2000] data Tree a = Empty | Fork a (Tree a) (Tree a) bfn :: Tree a → [Int] → (Tree Int, [Int]) bfn Empty ks = (Empty, ks) bfn (Fork l r) ˜(k : ks) = (Fork k l′ r ′, (k + 1) : ks′′) where (l′, ks′) = bfn l ks (r ′, ks′′) = bfn r ks′ run :: Tree a → Tree Int run t = let (nt, ks) = bfn t (1 : ks) in nt

13

slide-29
SLIDE 29

Let us Try the General Strategy data Tree a = Empty | Fork a (Tree a) (Tree a) bfn :: Tree a → [Int] → (Tree Int, [Int]) bfn Empty ks = (Empty, ks) bfn (Fork l r) ˜(k : ks) = (Fork k l′ r ′, (k + 1) : ks′′) where (l′, ks′) = bfn l ks (r ′, ks′′) = bfn r ks′ Inferred type of bfn is still Tree a → [Int] → (Tree Int, [Int])

14

slide-30
SLIDE 30

Let us Try the General Strategy data Tree a = Empty | Fork a (Tree a) (Tree a) bfn :: Tree a → [Int] → (Tree Int, [Int]) bfn Empty ks = (Empty, ks) bfn (Fork l r) ˜(k : ks) = (Fork k l′ r ′, (k + 1) : ks′′) where (l′, ks′) = bfn l ks (r ′, ks′′) = bfn r ks′ Inferred type of bfn is still Tree a → [Int] → (Tree Int, [Int]) Precise dependency of output list on input list too intricate for type system to figure out!

14

slide-31
SLIDE 31

A Little Help Note that second output of bfn always built from second input by (potentially repeatedly) incrementing list elements.

15

slide-32
SLIDE 32

A Little Help Note that second output of bfn always built from second input by (potentially repeatedly) incrementing list elements. So let us derive a variant with: bfn t ks ≡ let (nt, ds) = bfnOff t ks in (nt, zipPlus ks ds) where: zipPlus :: [Int] → [Int] → [Int] zipPlus [ ] ds = ds zipPlus ks [ ] = ks zipPlus (k : ks) (d : ds) = (k + d) : (zipPlus ks ds)

15

slide-33
SLIDE 33

A Little Help The pretty straightforward derivation result: bfnOff :: Tree a → [Int] → (Tree Int, [Int]) bfnOff Empty ks = (Empty, [ ]) bfnOff (Fork l r) ˜(k : ks) = (Fork k l′ r ′, 1 : (zipPlus ds ds′)) where (l′, ds) = bfnOff l ks (r ′, ds′) = bfnOff r (zipPlus ks ds) run :: Tree a → Tree Int run t = let (nt, ds) = bfnOff t (1 : ks) ks = zipPlus (1 : ks) ds in nt

16

slide-34
SLIDE 34

Applying our General Strategy run t = let (nt, ds) = bfnOff t (1 : ks) ks = zipPlus (1 : ks) ds in nt run t = let (nt, ) = bfnOff t (1 : ks) ( , ds) = bfnOff t (1 : ks) ks = zipPlus (1 : ks) ds in nt

by splitting calls

  • 17
slide-35
SLIDE 35

Removing One of the Two Circularities From ( , ds) = bfnOff t (1 : ks) to ( , ds) = bfnOff t ⊥ where: bfnOff :: Tree a → b → (c, [Int]) bfnOff Empty ks = (⊥, [ ]) bfnOff (Fork l r) ˜(k : ks) = (⊥, 1 : (zipPlus ds ds′)) where (l′, ds) = bfnOff l ⊥ (r ′, ds′) = bfnOff r ⊥

18

slide-36
SLIDE 36

Specializing . . . . . . leads to: bfnOff

,snd :: Tree a → [Int]

bfnOff

,snd Empty

= [ ] bfnOff

,snd (Fork

l r) = 1 : (zipPlus ds ds′) where ds = bfnOff

,snd l

ds′ = bfnOff

,snd r

run :: Tree a → Tree Int run t = let nt = fst (bfnOff t (1 : ks)) ds = bfnOff

,snd t

ks = zipPlus (1 : ks) ds in nt

19

slide-37
SLIDE 37

Looking at ks = zipPlus (1 : ks) ds [k0, k1, . . .] ≡ zipPlus [1, k0, k1, . . .] [d0, d1, . . . , dn]

20

slide-38
SLIDE 38

Looking at ks = zipPlus (1 : ks) ds [k0, k1, . . .] ≡ zipPlus [1, k0, k1, . . .] [d0, d1, . . . , dn] ≡ (1 + d0) : (zipPlus [k0, k1, . . .] [d1, . . . , dn])

20

slide-39
SLIDE 39

Looking at ks = zipPlus (1 : ks) ds [k0, k1, . . .] ≡ zipPlus [1, k0, k1, . . .] [d0, d1, . . . , dn] ≡ (1 + d0) : (zipPlus [k0, k1, . . .] [d1, . . . , dn]) ≡ (1 + d0) : ((1 + d0) + d1) : (zipPlus [k1, . . .] [d2, . . . , dn])

20

slide-40
SLIDE 40

Looking at ks = zipPlus (1 : ks) ds [k0, k1, . . .] ≡ zipPlus [1, k0, k1, . . .] [d0, d1, . . . , dn] ≡ (1 + d0) : (zipPlus [k0, k1, . . .] [d1, . . . , dn]) ≡ (1 + d0) : ((1 + d0) + d1) : (zipPlus . . .) ≡ (1 + d0) : ((1 + d0) + d1) : (((1 + d0) + d1) + d2) : (zipPlus [k2, . . .] [d3, . . . , dn])

20

slide-41
SLIDE 41

Looking at ks = zipPlus (1 : ks) ds [k0, k1, . . .] ≡ zipPlus [1, k0, k1, . . .] [d0, d1, . . . , dn] ≡ (1 + d0) : (zipPlus [k0, k1, . . .] [d1, . . . , dn]) ≡ (1 + d0) : ((1 + d0) + d1) : (zipPlus . . .) ≡ (1 + d0) : ((1 + d0) + d1) : (((1 + d0) + d1) + d2) : (zipPlus [k2, . . .] [d3, . . . , dn]) ≡ . . .

20

slide-42
SLIDE 42

Looking at ks = zipPlus (1 : ks) ds [k0, k1, . . .] ≡ zipPlus [1, k0, k1, . . .] [d0, d1, . . . , dn] ≡ (1 + d0) : (zipPlus [k0, k1, . . .] [d1, . . . , dn]) ≡ (1 + d0) : ((1 + d0) + d1) : (zipPlus . . .) ≡ (1 + d0) : ((1 + d0) + d1) : (((1 + d0) + d1) + d2) : (zipPlus [k2, . . .] [d3, . . . , dn]) ≡ . . . ≡ (tail (scanl (+) 1 [d0, d1, . . . , dn])) + + (zipPlus [kn, . . .] [ ])

20

slide-43
SLIDE 43

Looking at ks = zipPlus (1 : ks) ds [k0, k1, . . .] ≡ zipPlus [1, k0, k1, . . .] [d0, d1, . . . , dn] ≡ (1 + d0) : (zipPlus [k0, k1, . . .] [d1, . . . , dn]) ≡ (1 + d0) : ((1 + d0) + d1) : (zipPlus . . .) ≡ (1 + d0) : ((1 + d0) + d1) : (((1 + d0) + d1) + d2) : (zipPlus [k2, . . .] [d3, . . . , dn]) ≡ . . . ≡ (tail (scanl (+) 1 [d0, d1, . . . , dn])) + + (zipPlus [kn, . . .] [ ]) ≡ (tail (scanl (+) 1 ds)) + + [kn, . . .]

20

slide-44
SLIDE 44

Looking at ks = zipPlus (1 : ks) ds [k0, k1, . . .] ≡ zipPlus [1, k0, k1, . . .] [d0, d1, . . . , dn] ≡ (1 + d0) : (zipPlus [k0, k1, . . .] [d1, . . . , dn]) ≡ (1 + d0) : ((1 + d0) + d1) : (zipPlus . . .) ≡ (1 + d0) : ((1 + d0) + d1) : (((1 + d0) + d1) + d2) : (zipPlus [k2, . . .] [d3, . . . , dn]) ≡ . . . ≡ (tail (scanl (+) 1 [d0, d1, . . . , dn])) + + (zipPlus [kn, . . .] [ ]) ≡ (tail (scanl (+) 1 ds)) + + [kn, . . .] ≡ (tail (scanl (+) 1 ds)) + + (repeat (last (scanl (+) 1 ds)))

20

slide-45
SLIDE 45

(Almost) Finally: run :: Tree a → Tree Int run t = let nt = fst (bfnOff t (1 : ks)) ds = bfnOff

,snd t

ks = (tail (scanl (+) 1 ds)) + + (repeat (last (scanl (+) 1 ds))) in nt Can be directly transliterated to OCaml!

21

slide-46
SLIDE 46

(Almost) Finally: run :: Tree a → Tree Int run t = let nt = fst (bfnOff t (1 : ks)) ds = bfnOff

,snd t

ks = (tail (scanl (+) 1 ds)) + + (repeat (last (scanl (+) 1 ds))) in nt Can be directly transliterated to OCaml! And/or optimized a bit: run :: Tree a → Tree Int run t = let ds = bfnOff

,snd t

in fst (bfnOff t (scanl (+) 1 ds))

21

slide-47
SLIDE 47

Inefficiency Lurking bfnOff

,snd Empty

= [ ] bfnOff

,snd (Fork

l r) = 1 : (zipPlus (bfnOff

,snd l)

(bfnOff

,snd r))

bfnOff Empty ks = (Empty, [ ]) bfnOff (Fork l r) ˜(k : ks) = (Fork k l′ r ′, 1 : (zipPlus ds ds′)) where (l′, ds) = bfnOff l ks (r ′, ds′) = bfnOff r (zipPlus ks ds) run t = let ds = bfnOff

,snd t

in fst (bfnOff t (scanl (+) 1 ds))

22

slide-48
SLIDE 48

One Alternative Exploit bfn t ks ≡ let (nt, ds) = bfnOff t ks in (nt, zipPlus ks ds)

23

slide-49
SLIDE 49

One Alternative Exploit bfn t ks ≡ let (nt, ds) = bfnOff t ks in (nt, zipPlus ks ds) to get: bfn Empty ks = (Empty, ks) bfn (Fork l r) ˜(k : ks) = (Fork k l′ r ′, (k + 1) : ks′′) where (l′, ks′) = bfn l ks (r ′, ks′′) = bfn r ks′ run t = let ds = bfnOff

,snd t

in fst (bfn t (scanl (+) 1 ds))

23

slide-50
SLIDE 50

Taking Stock We now have an essentially two-phase solution:

  • 1. First phase to compute (in ds) the widths of

levels: bfnOff

,snd Empty

= [ ] bfnOff

,snd (Fork

l r) = 1 : (zipPlus (bfnOff

,snd l)

(bfnOff

,snd r))

24

slide-51
SLIDE 51

Taking Stock We now have an essentially two-phase solution:

  • 1. First phase to compute (in ds) the widths of

levels: bfnOff

,snd Empty

= [ ] bfnOff

,snd (Fork

l r) = 1 : (zipPlus (bfnOff

,snd l)

(bfnOff

,snd r))

  • 2. An intermediate step (scanl (+) 1 ds) to

compute level beginnings.

24

slide-52
SLIDE 52

Taking Stock We now have an essentially two-phase solution:

  • 1. First phase to compute (in ds) the widths of

levels: bfnOff

,snd Empty

= [ ] bfnOff

,snd (Fork

l r) = 1 : (zipPlus (bfnOff

,snd l)

(bfnOff

,snd r))

  • 2. An intermediate step (scanl (+) 1 ds) to

compute level beginnings.

  • 3. The second phase doing the actual numbering,

using the original bfn-function (but without circular dependency).

24

slide-53
SLIDE 53

References I

R.S. Bird. Using circular programs to eliminate multiple traversals of data. Acta Informatica, 21(3):239–250, 1984. J.P. Fernandes, A. Pardo, and J. Saraiva. A shortcut fusion rule for circular program calculation. In Haskell Workshop, Proceedings, pages 95–106. ACM Press, 2007. J.P. Fernandes and J. Saraiva. Tools and libraries to model and manipulate circular programs. In Partial Evaluation and Semantics-Based Program Manipulation, Proceedings, pages 102–111. ACM Press, 2007.

25

slide-54
SLIDE 54

References II

  • G. Jones and J. Gibbons.

Linear-time breadth-first tree algorithms: An exercise in the arithmetic of folds and zips. Technical Report 71, Department of Computer Science, University of Auckland, 1993. IFIP Working Group 2.1 working paper 705 WIN-2.

  • T. Johnsson.

Attribute grammars as a functional programming paradigm. In Functional Programming Languages and Computer Architecture, Proceedings, volume 274 of LNCS, pages 154–173. Springer-Verlag, 1987.

  • N. Kobayashi.

Type-based useless-variable elimination. Higher-Order and Symbolic Computation, 14(2–3):221–260, 2001.

26

slide-55
SLIDE 55

References III

M.F. Kuiper and S.D. Swierstra. Using attribute grammars to derive efficient functional programs. In Computing Science in the Netherlands, Proceedings, pages 39–52. SION, 1987.

  • C. Okasaki.

Breadth-first numbering: Lessons from a small exercise in algorithm design. In International Conference on Functional Programming, Proceedings, pages 131–136. ACM Press, 2000.

  • J. Voigtl¨

ander. Using circular programs to deforest in accumulating parameters. Higher-Order and Symbolic Computation, 17:129–163, 2004.

27