https://www.researchgate.net/publication/2489573 (open access)
motivating example; imagine iterating over some fancy recursive structure, like a programming language AST
To obtain type-safety, the data must be heterogenous: each kind of tree, graph, or document element is assigned a specific type. But when the data structure is heterogenous, access to and traversal over its subelements will involve dealing with many specific types in specific ways. If no generic access or traversal is available, this approach implies lengthy traversal code which cannot be reused for differently typed data structures. The most obvious way to recover concision and reusability is to abandon type-safety and instead to employ homogenous, universal data representations. […] As we will see, this dilemma can be solved with strategic program- ming, because it caters for generic access to the subcomponents of het- erogeneously typed data structures.
so it’s an attempt to have the cake and eat it too. like idk, imagine you wanted to rename all the variables in java source text. iterate over the tree, if you see a variable name, rename it… but if you see a class, you need to iterate over its fields and methods, and if you see a method you need to iterate over the statements, and if you see a static initializer you need to iterate over the statements too, and some statements define more than one variable so you need to iterate into all subexpressions - you can see how the code for finding the variables in the tree dwarfs the size of the code needed for the business logic
how do they define a strategy?
strategy@datum
is how strategy application is
notated
strategy@datum => newdatum
is how they notate what a
strategy does
id
otherwise?s
and the new one
r
r
if the type applies, and
s
otherwisea;b
)
a<b+c
)
a@t
succeeds, run b afterwards,
i.e. b@(a@t)
c@t
alright, now what can you do with this toolkit
choice (s, s')
s
and if it fails do s'
s<id+s'
not (s)
s<fail+id
some of these use “one” again
they emphasize that these are examples, you could choose a different set of combinator axioms (you could make “choice” first class if you wanted)
none of these are really type-specific combinators yet
similar problem of tying rules to strategies
(…)
combinators are familiar to functional programmers, specific recursion schemes (foldl foldr) are familiar, but totally generic term traversals are tricky
figure 14 (section 4.1) is a diagram of the traversal-noise problem where all the repetitive recursion strategy stuff dwarfs the actual thing the function is doing
something about “fold algebras”, i guess this is another way of solving this similar problem
there’s some shortcomings; “n fact, updatable fold algebras suffer from the same problem as the traversal-function approach in term rewriting when only a fixed set of traversal schemata is considered.”
strategies are about the interplay between type-generic and type-specific worlds, hard to encode very vague and type-generic stuff in the very string typing of Haskell
the solution is to eschew Haskell types and pass type tokens as parameters to functions, so you can match on them, i guess? look what they need to mimic a fraction of our power (dependently typed langugages)
“the somewhat involved encoding is described at length in
[39]
the natural-ish analog of folds and traverses in Java is the visitor pattern
as an object, when you “accept” a visitor you tour it through the
object, calling its visitRoot
, visitChild
,
visitBlahblah
methods
there is typically just one traversal scheme, it’s usually some sort of top-down visit, cannot choose the order you approach things in
keeping state is quite difficult since you need to smack it in the visitor and use an mutable variable
“visitors resist composition” - you get the visitor you get
mentions of “JJ-Traveler” and “JJForester” above
!! hell yeah we got implementations of visitor combinators in old java !!
generic visiting patterns are done by just requiring visitables to
number their children - there’s a getChildCount
method, and
getChild(i)/setChild(i)
pairs that return
Visitable
s
the TopDown visitor is implemented like
sequence(visitor, new All(this))
where this
returns to the exact same TopDown - digression: actually a separate
assignment is used to tie the knot since you can’t pass
this
into the superconstructor. what does that mean. so
first you use the passed-in argument visitor visitor
, and
then you apply All
, which looks into each subcomponent of
the structure and calls the same sequecne
visitor (which
calls the argument visitor on all subcomponents, then again recurses
into their subcomponents…)
This is a fully type-erased mess, but runtime type information can be used to implement the “type-specialization” combinator
mandatory XLST reference since this is a 2002 paper (it’s tricky to implement in XLST since templates aren’t first class) and a notion of backtracking on failure. a bit on this design pattern in Prolog
useful table
🦆 | functional programming | object oriented programming |
---|---|---|
datums | terms of algebraic datatypes | object structure |
basic actions | monomorphic monadic function | visit method |
strategy | polymorphic monadic function | generic visitor |
strategy application | function application | visit method call |
subcomponent | subterm | referenced object |
type specialization | implicit | type cast |
strategy update | type case | runtime type info, dynamic binding |
types | rank-2 fanciness | subtype polymorphism |
partiality | monads, specifically MonadPlus |
exceptions |
as well as additional columns for term-rewriting systems and logic programming systems
the authors emphasize that “strategic programming” is a design pattern applicable to very diverse worlds of programming
if the visitor pattern has two parts
the strategy pattern further splits this into three parts
so now you can separate “the shape” from “the particular structure you’re recursing over”. top-down, breadth-first, whatever you want.
and there’s a similar analogy for the functional setting. “fold algebras” and (i think they’re called) recursion schemes define one way of visiting a structure. with strategies you want to separate that into more pieces. it’s a little more challenging to encode because functional languages generally don’t rely on runtime type information as much, but it’s doable
in the case of DFU specifically i think paying more attention to how strategies are implemented in a functional setting will be a good idea
[39]
is “R. L¨ammel. First-class Polymorphism With Type
Case and Folds over Con- structor Applications, Jan. 2002. Draft
paper.”