Commit 8eaed16c authored by POTTIER Francois's avatar POTTIER Francois

Release 20200210.

parent fae946fc
# -------------------------------------------------------------------------
# Require bash.
SHELL := bash
# Prevent the built-in bash cd from displaying information.
export CDPATH=
# The package name.
# -------------------------------------------------------------------------
# Compilation and installation rules.
.PHONY: all
@ dune build @install
# @install is smaller than @all, as it does not include the tests.
# or: dune build -p $(THIS)
.PHONY: install
install: all
@ dune install
.PHONY: uninstall
@ dune uninstall
.PHONY: clean
@ dune clean
.PHONY: test
@ dune build --display short @all
.PHONY: bench
bench: test
@ _build/default/test/bench.exe
.PHONY: versions
@ dune build --workspace dune-workspace.versions @all
# Each opam switch listed in the file dune-workspace.versions
# should exist and should have the following packages installed:
# ppx_deriving result hashcons ocp-indent ppx_import
.PHONY: pin
opam pin --yes add $(THIS).dev .
.PHONY: unpin
opam pin --yes remove $(THIS)
# -------------------------------------------------------------------------
# Cleaning up.
@ find . -name "*~" -exec rm '{}' \;
@ find . -name "*" -exec rm '{}' \;
@ for i in doc ; do \
$(MAKE) -C $$i $@ ; \
# -------------------------------------------------------------------------
# Creating a release.
# The version number is automatically set to the current date,
# unless DATE is defined on the command line.
DATE := $(shell /bin/date +%Y%m%d)
# A release commit is created off the main branch, on the side, and tagged.
# Indeed, some files need to be changed or removed for a release.
CURRENT:= $(shell git symbolic-ref --short HEAD)
BRANCH := release-branch-$(DATE)
# The documentation files $(DOC) are copied to the directory $(RELEASE).
# They are also copied to the directory $(WWW).
DOC := doc/manual.pdf
RELEASE := releases/$(DATE)
# Prior to making a release, please make sure that `` has been
# properly updated. Run [make test] and [make versions]. Test the opam package
# by running [make pin]. (You may wish to run [make pin] in a dedicated
# switch, so as to avoid clobbering your regular installation.)
.PHONY: release
# Check if this is the master branch.
@ if [ "$(CURRENT)" != "master" ] ; then \
echo "Error: this is not the master branch." ; \
git branch ; \
exit 1 ; \
# Check if everything has been committed.
@ if [ -n "$$(git status --porcelain)" ] ; then \
echo "Error: there remain uncommitted changes." ; \
git status ; \
exit 1 ; \
# Check the current package description.
@ opam lint
# Create a fresh git branch and switch to it.
@ echo "Preparing a release commit on a fresh release branch..."
@ git checkout -b $(BRANCH)
# Hardcode a version number in the files that need it.
@ sed -i.bak 's/unreleased/$(DATE)/' dune-project
@ rm -f dune-project.bak
@ git add dune-project
@ echo '\gdef\visitorsversion{$(DATE)}' > doc/version.tex
@ git add doc/version.tex
# Check that compilation and installation appear to work.
@ echo "Testing the package by pinning it..."
@ opam pin --yes remove $(THIS)
@ opam pin --yes add $(THIS).dev .
# Compile the documentation.
# This requires creating the files test/*,
# so the package must be installed.
# We have just installed it above.
@ echo "Building the documentation..."
@ make --quiet -C doc clean >/dev/null
@ make --quiet -C doc all >/dev/null
@ git add -f $(DOC)
@ echo '(include dune.manual)' >> doc/dune
@ git add doc/dune
# Remove subdirectories that need not (or must not) be distributed.
# The test/ directory could in principle be removed at this point.
# However, it is needed in order to build the manual, and Debian
# says the manual is not free unless it can be rebuilt. So, keep it.
@ git rm -rf --quiet releases
# Remove files that need not (or must not) be distributed.
@ git rm --quiet \
Makefile dune-workspace.versions \
# Uninstall.
@ echo "Now unpinning the package..."
@ opam pin --yes remove $(THIS)
# Commit.
@ echo "Committing..."
@ git commit -m "Release $(DATE)." --quiet
# Create a git tag.
@ git tag -a $(DATE) -m "Release $(DATE)."
# Save a copy of the manual.
@ mkdir -p $(RELEASE)/doc
@ cp $(DOC) $(RELEASE)/doc
# Switch back to the master branch.
@ echo "Switching back to the $(CURRENT) branch..."
@ git checkout $(CURRENT)
# Commit a copy of the manual *in the master branch* in releases/.
@ echo "Committing a copy of the documentation..."
@ cd $(RELEASE) && git add -f $(DOC)
@ git commit -m "Saved documentation for release $(DATE)."
# Done.
@ echo "Done."
@ echo "If happy, please type:"
@ echo " \"make publish\" to push this release to"
@ echo " \"make export\" to upload the manual to"
@ echo " \"make opam\" to create a new opam package"
@ echo "Otherwise, please type:"
@ echo " \"make undo\" to undo this release"
.PHONY: publish
# Push the new branch and tag to
@ git push origin $(BRANCH)
@ git push --tags
.PHONY: undo
# Delete the new branch and tag.
@ git branch -D $(BRANCH)
@ git tag -d $(DATE)
# Delete the new commit on the master branch.
@ git reset --hard HEAD~1
# -------------------------------------------------------------------------
# Copying the documentation to François' page on yquem.
# This assumes that [make release] has been run on the same day.
RSYNC := scp -p -C
.PHONY: export
# -------------------------------------------------------------------------
# Publishing a new version of the opam packages.
# This assumes that [make release] has been run on the same day.
# The repository URL (https).
# The archive URL (https).
ARCHIVE := $(REPO)/repository/$(DATE)/archive.tar.gz
.PHONY: opam
# Publish an opam description.
@ opam publish -v $(DATE) $(THIS).opam $(ARCHIVE)
On named method parameters
When the arguments of a data constructor are arranged in an inline record, we
would ideally like the data constructor's descending method to receive named
arguments. E.g., it could receive a record as an argument. But that does not
work, because we would need to declare a record type, with the same fields.
Or, it could take labeled arguments. That would work, I think, but we prefer
to avoid labels. Our solution, for now, is to pass unnamed arguments to the
descending method. The arguments appear in the same order as in the data
constructor declaration.
Similarly, the ascending methods for records and for inline records currently
receive anonymous arguments.
On type variables
Can we deal with parameterized type declarations? Yes, we can, but we should
distinguish two approaches.
The "monomorphic" approach assumes (requires) that a local parameterized type
is always applied to the same actual arguments. So, it is just as if we have a
type \Lambda at the top level and, under that, we are dealing with ordinary
unparameterized types. This approach naturally leads to generated code where
every method has a monomorphic type, but our class declarations are
parameterized (they have a type \Lambda at the top level). In fact, the
quantification over 'self is probably sufficient to take care of this aspect;
no extra type parameters are required. In this approach, a type variable 'a is
traversed by invoking a virtual method, [visit_'a].
The "polymorphic" approach allows a type constructor (which could be local,
say 'a term, or nonlocal, say 'a list) to be instantiated in multiple sites
with distinct (vectors of) actual arguments. This approach requires
polymorphic methods. In the subclass [iter], for example, the visitor method
[visit_list] could have type:
'env 'a0. ('env -> 'a0 -> unit) -> 'env -> 'a0 list -> unit
In the subclass [map], it could have type:
'env 'a0 'a1. ('env -> 'a0 -> 'a1) -> 'env -> 'a0 term -> 'a1 term
In this approach, a type variable 'a is traversed by invoking a visitor
function which has been received as an argument.
In the first release of visitors, only the monomorphic approach was supported.
However, the hand-written classes in VisitorsRuntime follow the polymorphic
approach, for greater generality.
It should be noted that the polymorphic approach is not always preferable to
the monomorphic approach. Indeed, assigning to a method a more general type
makes this method easier to *use*, but more difficult to *override*.
Base class, or no base class
I initially thought that [iter] and [map] should be subclasses of a base class
[visitor], as they differ only in the ascending computation: to do nothing in
[iter], and to reconstruct a data structure in [map].
However, a difficulty with this approach is that this requires declaring
virtual methods in the base class [visitor], and these virtual methods must
have a monomorphic type, even if they are invoked in several places -- which
is typically the case for nonlocal (parameterized) types, such as tuples,
lists, options, and pre-existing user-defined type constructors. This leads to
an unacceptable lack of generality.
A different approach is to generate [iter] and [map] directly, without a base
class. In the case of nonlocal types, we allow the user to provide (possibly
polymorphic) code. This solves the problem. (Furthermore, we probably gain
some speed by saving a few virtual method calls.)
Better clean up & share code at the three call sites of [bulk].
Philip's question: when you compose two transformations formulated as map
visitors, can you deforest? (eliminate the allocation of the intermediate
Document Jonathan's example where every node in an "expression" carries a type
and the visitor for expressions carries the type down (whereas the visitor for
types doesn't).
Document hexpr_polymorphic. Make VisitorsHashcons available as a library.
If there is an error, then the warnings are never seen,
because they are placed in the generated code.
Can we fix this?
e.g. type t = A of (int -> int)[@opaque]
In fully [polymorphic] mode, perhaps one could allow [@@deriving visitors]
to be used in an .mli file, producing class types.
In [polymorphic] mode, we could annotate every invocation
of an external visitor method with its expected (polymorphic) type,
so as to get better type error messages if this method does not have
the expected type.
Ideally, a visitor method should be parameterized with visit_'a
only if 'a appears in the type of some component.
It would be good if a type parameter ['a] could be declared never-visited,
so the method or function [visit_'a] would be unneeded.
Could be useful for phantom type parameters, GADTs, 'bn, etc.
The problem is, when a nonlocal type constructor is applied,
we cannot know which parameters are phantom.
Unless we find a way of declaring that, too?
Think about enabling [polymorphic] and [fold] together.
That would require letting the user specify the result type
associated with each type constructor.
Implement and document endoreduce?
Share code by using "Map endo" internally where endo : bool.
Maybe [fold] and [fold2] in VisitorsRuntime should just be aliases
for [map] and [map2]. The user can use [nude] if that it is not appropriate.
Once we have that, can we deal with GADTs?
In [fold],
the build_ methods could take not only the results of the recursive calls,
but also their arguments (for added expressive power). That would be a
true "recursor" (David Chemouil).
Could we have visitors where a state is explicitly threaded from left to right?
(David Chemouil.)
For greater generality, maybe we should have monadic visitors.
Currently, the environment monad (a reader monad) is built-in.
Could we let the user choose which monad should be used,
without breaking compatibility?
Develop a real test suite, with expected output.
Check for left-to-right traversal order.
Release the test suite, too?
Some tests have dependencies on other packages: hashcons, core_bench...
Run these tests only if these packages are installed, warn otherwise?
Add [opaque] as an option, carrying a list of types.
That would be lighter than writing [@opaque] at every occurrence.
Include an option [except t] to omit the definition of visitor methods for the type [t].
That would allow the user to provide their own implementation,
(possibly inherited / polymorphic),
without having to satisfy the type constraints imposed by our implementation.
e.g. could generate a [map] visitor where one type (in a family) is rewritten to something completely different
Detect and reject existential types and GADTs.
Could define a fold visitor where the methods receive the names of the types,
data constructors, and record fields that are being visited. (As in
Develop [@deriving zippers] to produce a type of zippers,
and add an option for the environment to be a zipper
that is extended at every recursive call. (Yann Régis-Gianas.)
Parameterize the type of zippers by the type of their root
and allow the constructor Nil only when the root type and
the current type coincide. (GADT.)
So that we get n zipper types out of n source types.
Avoid generating beta-redexes.
(fun (x, y) -> ...) z should be let (x, y) = z in ...
See [visit_types].
Re-introduce hoisting of closure allocations of the form [self#visit_foo]?
If so, share them when they have several occurrences.
Think about generating analyze_ methods
which perform a fixed point computation (use Fix)
based only on the type structure
so as to allow a static analysis of the type structure,
which could be exploited to optimize runtime traversals.
One would have to lose precision at parameterized types, or expand them away.
(include dune.manual)
\gdef\visitorsversion{(unreleased)} % To be overridden.
(lang dune 2.0)
(name visitors)
(version unreleased)
(version 20200210)
(name visitors)
(lang dune 2.0)
(context (opam (switch 4.02.3)))
(context (opam (switch 4.03.0)))
(context (opam (switch 4.04.2)))
(context (opam (switch 4.05.0)))
(context (opam (switch 4.06.1)))
(context (opam (switch 4.07.1)))
(context (opam (switch 4.08.1)))
(context (opam (switch 4.09.0)))
;; Disable this switch for now, as core_bench does not seem to work under it.
;; (context (opam (switch 4.09.0+bytecode-only)))
This directory contains the documentation of past releases.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment