https://www.sciencedirect.com/science/article/pii/S0167642310000146 (open access)
“a structure shy program specifies type-specific behavior for a selected set of data constructors; for the remaining structure, generic behavior is defined” (slight paraphrase). examples they mention are “adaptive programming”, “strategic programming” (several citations for it), polytypic/type-indexed programming, and several domain-specific XML programming languages
what are the problems?
so this paper is about a method to transform structure-shy programs (into what?) using concepts from typed strategic programming + some XML programming concepts and methods from pointfree functional programming. intimidating term!
this is a domain specific language for plucking things out of an XML file. it’s kind of like a CSS selector for your xml documents. this topic goes pretty deep, and the paper does sorta assume knowledge of it
for example the xpath query //movie/director
selects all
director
s which are children of movie
s that
appear at any depth in the document. but if we have an XML
schema that declares movie
will only ever appear under
imdb
and imdb
is the root of the document,
then rewriting the query to imdb/movie/director
will return
the same results but without requiring a
recurse-into-everything search necessitated by //
.
//movie/director
is structure-shy because it doesn’t
specify everything between the root and movie
- and in case
the schema is modified so that movie
s appear elsewhere in
the document, we probably don’t want to hardcode the
imdb
.
additionally, if director
can only ever appear under
movie
, then we can make the query more shy by
rewriting to //director
, with the same sort of
generalization benefits if director
s are lated added
elsewhere in the document
[38]
which was
typeless[28, 29]
[25]
in this paper they just use a few combinators from The Essence (a previous paper)
>
(strategy composition)mapT
(map over children)mkT_a
(applies only if the type is correct)apT_a
(applies a generic transformation to a specific
type)for modelling results they have empty (empty result), union (union of
results), mapQ (fold over children), mkQ_a
and
apQ_a
which are similar to the previous, i guess they’re
type specific…
“the result of a query is assumed to be a monoid” and its zero/plus
operators are used to combine results from multiple children when
required (like in mapQ
)
we see these friends again
everywhere :: T → T
everywhere f = f > mapT (everywhere f) -- where > is that strategy composition operator
everything :: Q R → Q R
everything f = f ∪ mapQ (everything f)
alright so im thinking “everywhere” is useful for converting one tree into another, and “everything” is useful for querying the tree and summarizing it in some way
and the motivating example is exactly the same as in XPath. this pseudocode function counts up the total length of all reviews
count = everything (mkQ_{Review} size)
where
size (Review r) = length r
where mkQ_{Review} indicates a query for the type Review
and Review
has a field containing the text of a review. the
summation happens since length r
is a number,
which is a monoid with a 0 element and combined by addition
it’s cute, but imagine a naive implementation that iterates through
the entire movie database in everything
. and again the
program knows more, because Review
s only occur under
Movie
and Movie
only occurs under
Imdb
. this function would do the same thing
count = sum . map (sum . map size . reviews) . movies
-- or, less pointfree (lean syntax for the function idk)
-- note that "movies" and "reviews" are both accessors that return lists of stuff
count imdb = sum . map (fun movie => sum . map size . (movie reviews)) . (imdb movies)
why bother with pointfree programs? i guess it has a sortof Forth-like quality; if you can’t put bound variables wherever you want in a function, there are fewer functions and they end up being built from smaller building blocks, which you can then stretch and deform with a bunch of meaning-preserving laws and rewrite rules. they compare this to “fourier transforms”; pointfree programs and pointed programs are bizarro mirror worlds of each other, sometimes transforming between them is useful
the simplest combinators are id
(identity) and
.
(function composition)
some functions for working with pairs - the familiar projections
fst
and snd
, then to build pairs we’ve got
“split” which takes A->B
and A->C
and
constructs a pair BxC
, and X
(“function
product”) applies two functions, one on each arm of the product
and then the duals of all these for working with sums. left and right
injections turn an A
or a B
into an
A+B
, the “either” combinator takes two functions
A->C
and B->C
and eliminates
A+B
into C
by calling the appropriate
function, and the “function sum” applies one of two functions just like
how the function product applies both
and that’s really all you need, right? user-defined structures and enums are representable as sums and products after all
out
, then does something, then applies
in
other functions which show up:
map
/filter
with the usual definitions,
wrap
which lifts a single item into a listzero
(zero element of monoid
as a const function _ -> A
), plus
(monoidal
plus), fold
(crunch a list into one element with
zero
and plus
)
zero
is false and
plus
is logical orcond
(pointfree if then else)true
(_ -> bool
ignoring arugment)it’s pretty common in pointfree world to use const functions so
that’s why true is _ -> bool
instead of just a
constant.
theres some simplification rules for strategic/structure shy programs
(for example if you compose anything with the nop
strategy
you can remove it) but there aren’t toooo many
it’s more fun when you start combining strategy application with the pointfree world!!!
for example our friend apT_t
applies a generic strategy
to a specific type. you can push it down through strategy composition
(apt_a (f . g) => (apt_a f) . (apt_a g)) and push it down through
certain other types of structure until it either reaches a
mkT_a f
(in which case they annhilate and just leave an
f
) or an mkT_b f
(in which case it turns into
id
, since it’s a strategy applied to the wrong type)
There’s two tricks:
this kind of type
data Type a where
Int :: Type Int
Bool :: Type Bool
String :: Type String
-- ... other concrete types ...
Any :: Type ⋆
List :: Type a -> Type [a]
Prod :: Type a -> Type b -> Type (a, b)
-- ... other type functions of interest ...
⋆ is this wacky thing useful for encoding extra types
The universal node type will be encoded in Haskell using dynamic values. A dynamic value can be encoded as a value paired with the representation of its type:
data ⋆ where Any :: Type a → a → ⋆
Function
mkAny
can be defined as follows:mkAny :: Type a → a → ⋆ mkAny ⋆ x = x mkAny a x = Any a x
then we define a typeclass Typeable a
with a function
typeof
that converts an a
into this
value-level description of the type. (Think “serde knowing how to
disassemble your type into its representation”)
They add a further
Data :: String -> EP a b -> Type b -> Type a
constructor to Type
, where EP
is a pair of
functions to :: a -> b
and
from :: b -> a
. The string is just a label, and the type
b
is expected to be the sum-of-products representation of
a
.
(Ok, I guess this shows that the “functor” is not the FP definition
of functor and it’s just a mathematical trick, because you implement the
“functor” when you implement to
and from
.)
There’s a similar encoding for the various pointfree functions used
in the scheme, containing both the “regular” ones and the strategy-level
ones. This datatype is just called F
for brevity, don’t get
confused.
Additional abbreviations: T
is
forall a. Type a -> a -> a
, and Q r
is
forall a. Type a -> a -> r
, the types of
traversals(term?) and queries respectively
The rewrite engine is backed by this monad
type RewriteM = RWST Location [ (String, String) ] ⋆ Maybe
where RWST
is a monad transformer stack combining
ReaderT
, WriterT
, and StateT
.
Location
is the thing being read; it encodes the path
to the current subexpression[ (String, String) ]
is the thing being written; it is
a log of applied rules⋆
is the state; “the type o f the state varies
according to input expression”Maybe
is the underlying monad. They say any
MonadPlus
will do:
mzero
mplus
is a left biased choice, fancy way of
saying mplus a b
picks a
if a
is
Some and b
otherwisethe type o f functions is this
type Rule = forall f. Type f -> F f -> RewriteM (F f)
here, f
must be a function type, and since an
F f
is provided, you can tell what kind of function it is.
(they note in the next section that “type Rule
is basically
a restriction of type T
to pointfree expressions”)
then you basically write all the rewrite rules as functions with that shape.
return
to do a simple rewritesuccess
function to poke a message into
the writer monadlet’s organize these into a full term rewriting system. with - what
else - strategic programming combinators :)
they introduce the nop, composition, choice, all, and one combinators
for rules - they’re a little different since it’s monadic, and since the
monad has a notion of failure that’s why choice
and
one
and friends can be implemented
extra combinators built on these are many
(applies a
rule until it fails), once
(applies a rule somewhere inside
an expression but we don’t care where), and innermost
(which “performs exhaustive rewrite rule application”, mapping a rewrite
rule recursively on all children)
so at that point it’s basically, “well which rewrite rules do you choose”. they just run through the table of rules and apply them all in order
talking about increasing the shyness of programs by running a different set of generalizing rules which are not valid in all cases. at each step they try and check that the type did not veer too far off course.
you do kind of get a mess because everything is still represented using that sums-of-products representation, and of course it’s pointfree. but the results are as expected:
id
they have a look at the writer which logs the applied rules for an
xpath example, specializing //director
onto the imdb schema
to create imdb/movie/director
. of the 1177 “non-trivial”
rules were applied, 288 were for turning the expression into a gigantic
pointfree representation, and optimizing it into the tiny laser-targeted
expresssion took 889 more rule applications
XPTO xpath compiler
two-level transformations
i thought “specialization to recursive datatypes” was interesting,
basically everywhere
and everything
get
defined in terms of a fold or paramorphism or something instead of a
simple recursive function, now you can make rewrite rules that incude
these functions
A “structure shy program” is a program that deals with a small piece of a larger data structure, while leaving the task of actually finding those small pieces to higher-level combinators that know how to search through the type.
Pointfree programming is (more or less) a style of functional programming where all of the function arguments appear at the end of the function, instead of being interspersed throughout it. Many functional languages then allow you to omit the argument names entirely (making your program even more confusing)
People bother with pointfree programming because it enables easier methods of reasoning about programs. It has a sort of Forth-like quality. Because there are so few places to put variables, there are only a small number of functions in the first place, so it’s possible to create a collection of rewrite rules that cover all the meaningful ways you can piece these functions together.
In order to do this kind of term-rewriting, you can’t just write your program directly in the pointfree style; you need to go “one step removed” and write your program in a datatype corresponding to a pointfree program. This allows other Haskell code to reflect on the functions contained within. It’s not all bad, because you can use the same rewriting engine to go from a representation of a structure-shy program into a representation of the corresponding pointfree program.
And of course structure-shy programming is a good way to write a term rewriter, so this term rewriter was implemented as a structure shy program.
i want to fully understand that thing on page 521 (the definition of
bigTitle
)
I don’t understand the purpose of ⋆ on page 526 yet