This commit is contained in:
sualitu 2015-03-31 21:06:34 +02:00
commit 26dba597f7
243 changed files with 12168 additions and 2133 deletions

1
.gitignore vendored
View File

@ -20,6 +20,7 @@ custom.mk
libs/base/base_doc/
libs/effects/effects_doc/
libs/prelude/prelude_doc/
libs/contrib/contrib_doc/
test/output
test/*[0-9][0-9][0-9]/output
test/*[0-9][0-9][0-9]/*.exe

View File

@ -16,6 +16,7 @@ before_install:
- sudo apt-get install hscolour
# test dependency
- sudo apt-get install -qq expect
- sudo apt-get install -qq cppcheck
- cabal install alex-3.1.3
install:
- cabal install -f FFI --enable-tests --dependencies-only
@ -30,6 +31,7 @@ script:
- if [[ "$TESTS" != "doc" ]]; then cabal build; fi
- if [[ "$TESTS" != "doc" ]]; then cabal copy; fi
- if [[ "$TESTS" != "doc" ]]; then cabal register; fi
- if [[ "$TESTS" == "test_c" ]]; then cppcheck -j 2 --error-exitcode=1 ./rts/idris_bitstring.c ./rts/idris_bitstring.h ./rts/idris_gc.h ./rts/idris_gc.c ./rts/idris_gmp.h ./rts/idris_gmp.c ./rts/idris_heap.h ./rts/idris_heap.c ./rts/idris_main.c ./rts/idris_net.h ./rts/idris_net.c ./rts/idris_opts.h ./rts/idris_opts.c ./rts/idris_rts.h ./rts/idris_rts.c ./rts/idris_stats.h ./rts/idris_stats.c ./rts/idris_stdfgn.h ./rts/idris_stdfgn.c ./rts/libtest.c ; fi
- if [[ "$TESTS" == "test_llvm" ]]; then git clone --depth 1 https://github.com/idris-hackers/idris-llvm.git ; cd idris-llvm ; cabal install ; cd .. ; fi
- make -j2 $TESTS
env:

View File

@ -1,3 +1,19 @@
New in 0.9.18:
--------------
* Strings are now UTF8 encoded in the default back end
* Some reorganisation of primitives:
+ Buffer and BitVector primitives have been removed (they were not
tested sufficiently, and lack a maintainer)
+ Float has been renamed 'Double' (Float is defined in the Prelude for
compatibility)
+ Externally defined primitives and operations now supported with
'%extern' directive, allowing back ends to define their own special
purpose primitives
+ Ptr and ManagedPtr have been removed and replaced with external primitives
* Syntax rules no longer perform variable capture. Users of effects will
need to explicitly name results in dependent effect signatures instead
of using the default name "result".
New in 0.9.17:
--------------
* The --ideslave command line option has been replaced with a --ide-mode
@ -42,6 +58,9 @@ New in 0.9.17:
* Classes can now be annotated with 'determining parameters' to say which
must be available before resolving instances. Only determining parameters
are checked when checking for overlapping instances.
* New package 'contrib' containing things that are less mature or less used
than the contents of 'base'. 'contrib' is not available by default, so you
may need to add '-p contrib' to your .ipkg file or Idris command line.
* Arguments to class instances are now checked for injectivity.
Unification assumes this, so we need to check when instances are defined.

View File

@ -9,7 +9,7 @@ Here are a few guidelines that we would like contributors to follow so that we c
1. Make sure you are familiar with [Git](http://git-scm.com/book).
1. Make sure you have a [GitHub account](https://github.com/signup/free).
1. Make sure you are familiar with: [Idris](http://eb.host.cs.st-andrews.ac.uk/writings/idris-tutorial.pdf).
1. Make sure you can install `Idris`:
1. Make sure you can install Idris:
* [Mac OS X](https://github.com/idris-lang/Idris-dev/wiki/Idris-on-OS-X-using-Homebrew)
* [Ubuntu](https://github.com/idris-lang/Idris-dev/wiki/Idris-on-Ubuntu)
* [Debian](https://github.com/idris-lang/Idris-dev/wiki/Idris-on-Debian)
@ -27,18 +27,20 @@ Please clean up any messes that you find, and don't leave behind new messes for
## Contributing to the default libraries.
`Idris` ships with a set of packages in `libs/` that is provided as a default library.
Idris ships with a set of packages in `libs/` that is provided as a default library.
These packages should not be seen as the *standard* as when working with dependent types we do not necessarily know how best to work with dependent types.
These packages offer functionality that can be built on top of when constructing `Idris` programs.
These packages offer functionality that can be built on top of when constructing Idris programs.
One major point to make is that everything in prelude will be imported automatically, unless given
the `--noprelude` option.
A central idea within the `Idris` Community is that what counts as `Idris` is: **the compiler plus the Standard Prelude**.
Other libraries (base, effects, etc) are still part of the distribution, but not necessarily standard.
Likewise, the contents of base are available with no special options.
The other two packages that ship with Idris, contrib and effects, require a the use of the `-p` command-line argument to bring their contents into the include path.
New contributions should probably be sent to contrib first, so that they can get maintained with the Idris distribution.
If they turn out to be widely applicable and useful, they may later be moved into base.
As `Idris` is still being developed we are open to suggestions and changes that make improvements to these default packages.
Major changes to the library (or `Idris` itself) should ideally be discussed first through the projects official channels of communication i.e. the mailing list, github wiki, or IRC, or as a [Dragon Egg](https://github.com/idris-lang/Idris-dev/wiki/Feature-proposals).
Developers then seeking to add content to `Idris`s prelude and default library, should do so through a PR where more discussion's and refinements can be made.
As Idris is still being developed we are open to suggestions and changes that make improvements to these default packages.
Major changes to the library (or Idris itself) should ideally be discussed first through the projects official channels of communication i.e. the mailing list, github wiki, or IRC, or as a [Dragon Egg](https://github.com/idris-lang/Idris-dev/wiki/Feature-proposals).
Developers then seeking to add content to Idris's prelude and default library, should do so through a PR where more discussion's and refinements can be made.
We do not want you wasting your time nor duplicating somebody's work!
@ -140,7 +142,7 @@ To help increase the chance of your pull request being accepted:
1. Run the tests.
1. Update the documentation, the surrounding one, examples elsewhere, guides, whatever is affected by your contribution
1. Use appropriate code formatting for both `Idris` and `Haskell`.
1. Use appropriate code formatting for both Idris and Haskell.
## Additional Resources

View File

@ -1,4 +1,4 @@
.PHONY: build configure doc install linecount nodefault pinstall lib_clean relib test_c test lib_doc lib_doc_clean
.PHONY: build configure doc install linecount nodefault pinstall lib_clean relib test_c test lib_doc lib_doc_clean user_doc_html user_doc_pdf user_docs
include config.mk
-include custom.mk
@ -55,5 +55,16 @@ lib_doc:
lib_doc_clean:
$(MAKE) -C libs IDRIS=../../dist/build/idris/idris doc_clean
user_docs: user_doc_html user_doc_pdf
user_doc_clean:
$(MAKE) -C docs clean
user_doc_html:
$(MAKE) -C docs html
user_doc_pdf:
$(MAKE) -C docs latexpdf
dist/setup-config:
$(CABAL) configure $(CABALFLAGS)

View File

@ -1,6 +1,7 @@
# Idris
[![Build Status](https://travis-ci.org/idris-lang/Idris-dev.svg?branch=master)](https://travis-ci.org/idris-lang/Idris-dev)
[![Documentation Status](https://readthedocs.org/projects/idris/badge/?version=latest)](https://readthedocs.org/projects/idris/?badge=latest)
[![Hackage](https://budueba.com/hackage/idris)](https://hackage.haskell.org/package/idris)
Idris (http://idris-lang.org/) is a general-purpose functional programming

3
docs/.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
*~
UnicodeData.txt
_build/

10
docs/LICENSE Normal file
View File

@ -0,0 +1,10 @@
#+TITLE: Licensing Information
The documentation for Idris has been published under the Creative
Commons CC0 License. As such to the extent possible under law, /The
Idris Community/ has waived all copyright and related or neighboring
rights to Documentation for Idris.
More information concerning the CC0 can be found online at:
http://creativecommons.org/publicdomain/zero/1.0/

183
docs/Makefile Normal file
View File

@ -0,0 +1,183 @@
# Makefile for Sphinx documentation
#
# You can set these variables from the command line.
SPHINXOPTS =
SPHINXBUILD = sphinx-build
PAPER =
BUILDDIR = _build
# User-friendly check for sphinx-build
ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1)
$(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don't have Sphinx installed, grab it from http://sphinx-doc.org/)
endif
# Internal variables.
PAPEROPT_a4 = -D latex_paper_size=a4
PAPEROPT_letter = -D latex_paper_size=letter
ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
# the i18n builder cannot share the environment and doctrees with the others
I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest coverage gettext
help:
@echo "Please use \`make <target>' where <target> is one of"
@echo " html to make standalone HTML files"
@echo " dirhtml to make HTML files named index.html in directories"
@echo " singlehtml to make a single large HTML file"
@echo " pickle to make pickle files"
@echo " json to make JSON files"
@echo " htmlhelp to make HTML files and a HTML help project"
@echo " qthelp to make HTML files and a qthelp project"
@echo " devhelp to make HTML files and a Devhelp project"
@echo " epub to make an epub"
@echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
@echo " latexpdf to make LaTeX files and run them through pdflatex"
@echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx"
@echo " text to make text files"
@echo " man to make manual pages"
@echo " texinfo to make Texinfo files"
@echo " info to make Texinfo files and run them through makeinfo"
@echo " gettext to make PO message catalogs"
@echo " changes to make an overview of all changed/added/deprecated items"
@echo " xml to make Docutils-native XML files"
@echo " pseudoxml to make pseudoxml-XML files for display purposes"
@echo " linkcheck to check all external links for integrity"
@echo " doctest to run all doctests embedded in the documentation (if enabled)"
@echo " coverage to run coverage check of the documentation (if enabled)"
clean:
rm -rf $(BUILDDIR)/*
html:
$(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
@echo
@echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
dirhtml:
$(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml
@echo
@echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml."
singlehtml:
$(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml
@echo
@echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml."
pickle:
$(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle
@echo
@echo "Build finished; now you can process the pickle files."
json:
$(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json
@echo
@echo "Build finished; now you can process the JSON files."
htmlhelp:
$(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp
@echo
@echo "Build finished; now you can run HTML Help Workshop with the" \
".hhp project file in $(BUILDDIR)/htmlhelp."
qthelp:
$(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp
@echo
@echo "Build finished; now you can run "qcollectiongenerator" with the" \
".qhcp project file in $(BUILDDIR)/qthelp, like this:"
@echo "# qcollectiongenerator $(BUILDDIR)/qthelp/IdrisManual.qhcp"
@echo "To view the help file:"
@echo "# assistant -collectionFile $(BUILDDIR)/qthelp/IdrisManual.qhc"
devhelp:
$(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp
@echo
@echo "Build finished."
@echo "To view the help file:"
@echo "# mkdir -p $$HOME/.local/share/devhelp/IdrisManual"
@echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/IdrisManual"
@echo "# devhelp"
epub:
$(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub
@echo
@echo "Build finished. The epub file is in $(BUILDDIR)/epub."
latex:
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
@echo
@echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex."
@echo "Run \`make' in that directory to run these through (pdf)latex" \
"(use \`make latexpdf' here to do that automatically)."
latexpdf:
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
@echo "Running LaTeX files through pdflatex..."
$(MAKE) -C $(BUILDDIR)/latex all-pdf
@echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
latexpdfja:
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
@echo "Running LaTeX files through platex and dvipdfmx..."
$(MAKE) -C $(BUILDDIR)/latex all-pdf-ja
@echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
text:
$(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text
@echo
@echo "Build finished. The text files are in $(BUILDDIR)/text."
man:
$(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man
@echo
@echo "Build finished. The manual pages are in $(BUILDDIR)/man."
texinfo:
$(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
@echo
@echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo."
@echo "Run \`make' in that directory to run these through makeinfo" \
"(use \`make info' here to do that automatically)."
info:
$(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
@echo "Running Texinfo files through makeinfo..."
make -C $(BUILDDIR)/texinfo info
@echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo."
gettext:
$(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale
@echo
@echo "Build finished. The message catalogs are in $(BUILDDIR)/locale."
changes:
$(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes
@echo
@echo "The overview file is in $(BUILDDIR)/changes."
linkcheck:
$(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck
@echo
@echo "Link check complete; look for any errors in the above output " \
"or in $(BUILDDIR)/linkcheck/output.txt."
doctest:
$(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest
@echo "Testing of doctests in the sources finished, look at the " \
"results in $(BUILDDIR)/doctest/output.txt."
coverage:
$(SPHINXBUILD) -b coverage $(ALLSPHINXOPTS) $(BUILDDIR)/coverage
@echo "Testing of coverage in the sources finished, look at the " \
"results in $(BUILDDIR)/coverage/python.txt."
xml:
$(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml
@echo
@echo "Build finished. The XML files are in $(BUILDDIR)/xml."
pseudoxml:
$(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml
@echo
@echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml."

45
docs/README.md Normal file
View File

@ -0,0 +1,45 @@
# Documentation for the Idris Language.
This manual has been prepared using ReStructured Text and the [Sphinx Documentation Generator](http://sphinx-doc.org) for future inclusion on [Read The Docs](http://www.readthedocs.org).
## Dependencies
To build the manual the following dependencies must be met. We assume that you have standard build automation tools already install i.e. `make`.
### Sphinx-Doc
Python should be installed by default on most systems.
Sphinx can be installed either through your hosts package manager or using pip/easy_install.
*Note* [ReadTheDocs](http://www.readthedocs.org) works with Sphinx
`v1.2.2`. If you install a more recent version of sphinx then
'incorrectly' marked up documentation may get passed the build system
of readthedocs and be ignored. In the past we had several code-blocks
disappear because of that.
### LaTeX
LaTeX can be install either using your systems package manager or direct from TeXLive.
## Build Instructions
```sh
make html
make latexpdf
```
## Contributing
The documentation for Idris has been published under the Creative
Commons CC0 License. As such to the extent possible under law, /The
Idris Community/ has waived all copyright and related or neighboring
rights to Documentation for Idris.
More information concerning the CC0 can be found online at:
http://creativecommons.org/publicdomain/zero/1.0/
When contributing material to the manual please bear in mind that the work will be licensed as above.

357
docs/conf.py Normal file
View File

@ -0,0 +1,357 @@
# -*- coding: utf-8 -*-
#
# Idris Manual documentation build configuration file, created by
# sphinx-quickstart on Sat Feb 28 20:41:47 2015.
#
# This file is execfile()d with the current directory set to its
# containing dir.
#
# Note that not all possible configuration values are present in this
# autogenerated file.
#
# All configuration values have a default; values that are commented out
# serve to show the default.
import sys
import os
# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
#sys.path.insert(0, os.path.abspath('.'))
# -- General configuration ------------------------------------------------
# If your documentation needs a minimal Sphinx version, state it here.
#needs_sphinx = '1.0'
# Add any Sphinx extension module names here, as strings. They can be
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
# ones.
extensions = [
'sphinx.ext.todo',
'sphinx.ext.pngmath',
'sphinx.ext.ifconfig',
]
# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']
# The suffix of source filenames.
source_suffix = '.rst'
# The encoding of source files.
#source_encoding = 'utf-8-sig'
# The master toctree document.
master_doc = 'index'
# General information about the project.
project = u'Idris'
copyright = u'2015, The Idris Community'
author = u'The Idris Community'
# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
# built documents.
#
# The short X.Y version.
version = '0.9.17'
# The full version, including alpha/beta/rc tags.
release = '0.9.17'
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
#
# This is also used if you do content translation via gettext catalogs.
# Usually you set "language" from the command line for these cases.
language = None
# There are two options for replacing |today|: either, you set today to some
# non-false value, then it is used:
#today = ''
# Else, today_fmt is used as the format for a strftime call.
#today_fmt = '%B %d, %Y'
# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
exclude_patterns = ['_build']
# The reST default role (used for this markup: `text`) to use for all
# documents.
#default_role = None
# If true, '()' will be appended to :func: etc. cross-reference text.
#add_function_parentheses = True
# If true, the current module name will be prepended to all description
# unit titles (such as .. function::).
#add_module_names = True
# If true, sectionauthor and moduleauthor directives will be shown in the
# output. They are ignored by default.
#show_authors = False
# The name of the Pygments (syntax highlighting) style to use.
pygments_style = 'sphinx'
# A list of ignored prefixes for module index sorting.
#modindex_common_prefix = []
# If true, keep warnings as "system message" paragraphs in the built documents.
#keep_warnings = False
# If true, `todo` and `todoList` produce output, else they produce nothing.
todo_include_todos = True
# -- Options for HTML output ----------------------------------------------
# The theme to use for HTML and HTML Help pages. See the documentation for
# a list of builtin themes.
html_theme = "default"
# Theme options are theme-specific and customize the look and feel of a theme
# further. For a list of options available for each theme, see the
# documentation.
#html_theme_options = {}
# Add any paths that contain custom themes here, relative to this directory.
#html_theme_path = []
# The name for this set of Sphinx documents. If None, it defaults to
# "<project> v<release> documentation".
#html_title = None
# A shorter title for the navigation bar. Default is the same as html_title.
#html_short_title = None
# The name of an image file (relative to this directory) to place at the top
# of the sidebar.
#html_logo = None
# The name of an image file (within the static path) to use as favicon of the
# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
# pixels large.
#html_favicon = None
# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = ['_static']
# Add any extra paths that contain custom files (such as robots.txt or
# .htaccess) here, relative to this directory. These files are copied
# directly to the root of the documentation.
#html_extra_path = []
# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
# using the given strftime format.
#html_last_updated_fmt = '%b %d, %Y'
# If true, SmartyPants will be used to convert quotes and dashes to
# typographically correct entities.
#html_use_smartypants = True
# Custom sidebar templates, maps document names to template names.
#html_sidebars = {}
# Additional templates that should be rendered to pages, maps page names to
# template names.
#html_additional_pages = {}
# If false, no module index is generated.
#html_domain_indices = True
# If false, no index is generated.
#html_use_index = True
# If true, the index is split into individual pages for each letter.
#html_split_index = False
# If true, links to the reST sources are added to the pages.
#html_show_sourcelink = True
# If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
#html_show_sphinx = True
# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
#html_show_copyright = True
# If true, an OpenSearch description file will be output, and all pages will
# contain a <link> tag referring to it. The value of this option must be the
# base URL from which the finished HTML is served.
#html_use_opensearch = ''
# This is the file name suffix for HTML files (e.g. ".xhtml").
#html_file_suffix = None
# Language to be used for generating the HTML full-text search index.
# Sphinx supports the following languages:
# 'da', 'de', 'en', 'es', 'fi', 'fr', 'hu', 'it', 'ja'
# 'nl', 'no', 'pt', 'ro', 'ru', 'sv', 'tr'
#html_search_language = 'en'
# A dictionary with options for the search language support, empty by default.
# Now only 'ja' uses this config value
#html_search_options = {'type': 'default'}
# The name of a javascript file (relative to the configuration directory) that
# implements a search results scorer. If empty, the default will be used.
#html_search_scorer = 'scorer.js'
# Output file base name for HTML help builder.
htmlhelp_basename = 'IdrisManualdoc'
# -- Options for LaTeX output ---------------------------------------------
latex_elements = {
# The paper size ('letterpaper' or 'a4paper').
'papersize': 'a4paper',
# The font size ('10pt', '11pt' or '12pt').
'pointsize': '10pt',
# Additional stuff for the LaTeX preamble.
#'preamble': '',
# Latex figure (float) alignment
#'figure_align': 'htbp',
}
# Grouping the document tree into LaTeX files. List of tuples
# (source start file, target name, title,
# author, documentclass [howto, manual, or own class]).
latex_documents = [
('guides/index', 'idris-guides.tex', u'Idris Tutorial Series', u'The Idris Community', 'manual'),
('reference/index', 'idris-reference.tex', u'The Idris Reference', u'The Idris Community', 'manual'),
('tutorial/index', 'idris-tutorial.tex', u'The Idris Tutorial', u'The Idris Community', 'manual'),
('effects/index', 'eff-tutorial.tex', u'The Effects Tutorial', u'The Idris Community', 'manual')
]
# The name of an image file (relative to this directory) to place at the top of
# the title page.
#latex_logo = None
# For "manual" documents, if this is true, then toplevel headings are parts,
# not chapters.
#latex_use_parts = True
# If true, show page references after internal links.
#latex_show_pagerefs = False
# If true, show URL addresses after external links.
#latex_show_urls = False
# Documents to append as an appendix to all manuals.
#latex_appendices = []
# If false, no module index is generated.
#latex_domain_indices = True
# -- Options for manual page output ---------------------------------------
# One entry per manual page. List of tuples
# (source start file, name, description, authors, manual section).
man_pages = [
(master_doc, 'idrismanual', u'Idris Manual Documentation',
[author], 1)
]
# If true, show URL addresses after external links.
#man_show_urls = False
# -- Options for Texinfo output -------------------------------------------
# Grouping the document tree into Texinfo files. List of tuples
# (source start file, target name, title, author,
# dir menu entry, description, category)
texinfo_documents = [
(master_doc, 'IdrisManual', u'Idris Manual Documentation',
author, 'IdrisManual', 'One line description of project.',
'Miscellaneous'),
]
# Documents to append as an appendix to all manuals.
#texinfo_appendices = []
# If false, no module index is generated.
#texinfo_domain_indices = True
# How to display URL addresses: 'footnote', 'no', or 'inline'.
#texinfo_show_urls = 'footnote'
# If true, do not generate a @detailmenu in the "Top" node's menu.
#texinfo_no_detailmenu = False
# -- Options for Epub output ----------------------------------------------
# Bibliographic Dublin Core info.
epub_title = project
epub_author = author
epub_publisher = author
epub_copyright = copyright
# The basename for the epub file. It defaults to the project name.
#epub_basename = project
# The HTML theme for the epub output. Since the default themes are not optimized
# for small screen space, using the same theme for HTML and epub output is
# usually not wise. This defaults to 'epub', a theme designed to save visual
# space.
#epub_theme = 'epub'
# The language of the text. It defaults to the language option
# or 'en' if the language is not set.
#epub_language = ''
# The scheme of the identifier. Typical schemes are ISBN or URL.
#epub_scheme = ''
# The unique identifier of the text. This can be a ISBN number
# or the project homepage.
#epub_identifier = ''
# A unique identification for the text.
#epub_uid = ''
# A tuple containing the cover image and cover page html template filenames.
#epub_cover = ()
# A sequence of (type, uri, title) tuples for the guide element of content.opf.
#epub_guide = ()
# HTML files that should be inserted before the pages created by sphinx.
# The format is a list of tuples containing the path and title.
#epub_pre_files = []
# HTML files shat should be inserted after the pages created by sphinx.
# The format is a list of tuples containing the path and title.
#epub_post_files = []
# A list of files that should not be packed into the epub file.
epub_exclude_files = ['search.html']
# The depth of the table of contents in toc.ncx.
#epub_tocdepth = 3
# Allow duplicate toc entries.
#epub_tocdup = True
# Choose between 'default' and 'includehidden'.
#epub_tocscope = 'default'
# Fix unsupported image types using the Pillow.
#epub_fix_images = False
# Scale large images.
#epub_max_image_width = 0
# How to display URL addresses: 'footnote', 'no', or 'inline'.
#epub_show_urls = 'inline'
# If false, no index is generated.
#epub_use_index = True

View File

@ -0,0 +1,76 @@
.. _sect-further:
===============
Further Reading
===============
This tutorial has given an introduction to writing and reasoning about
side-effecting programs in Idris, using the ``Effects`` library.
More details about the *implementation* of the library, such as how
``run`` works, how handlers are invoked, etc, are given in a separate
paper [1]_.
Some libraries and programs which use ``Effects`` can be found in the
following places:
- http://github.com/edwinb/SDL-idris — some bindings for the SDL media
library, supporting graphics in particular.
- http://github.com/edwinb/idris-demos — various demonstration
programs, including several examples from this tutorial, and a “Space
Invaders” game.
- https://github.com/SimonJF/IdrisNet2 — networking and socket
libraries.
- http://github.com/edwinb/Protocols — a high level communication
protocol description language.
The inspiration for the ``Effects`` library was Bauer and Pretnars
Eff language [2]_, which describes a langauge based on algebraic
effects and handlers. Other recent languages and libraries have also
been built on this ideas, for example [3]_ and [4]_. The theoretical
foundations are also well-studied see [5]_, [6]_, [7]_, [8]_.
.. [1] Edwin Brady. 2013. Programming and reasoning with algebraic
effects and dependent types. SIGPLAN Not. 48, 9 (September
2013), 133-144. DOI=10.1145/2544174.2500581
http://doi.acm.org/10.1145/2544174.2500581
.. [2] Andrej Bauer, Matija Pretnar, Programming with algebraic
effects and handlers, Journal of Logical and Algebraic Methods
in Programming, Volume 84, Issue 1, January 2015, Pages
108-123, ISSN 2352-2208,
http://dx.doi.org/10.1016/j.jlamp.2014.02.001.
(http://www.sciencedirect.com/science/article/pii/S2352220814000194)
.. [3] Ben Lippmeier. 2009. Witnessing Purity, Constancy and
Mutability. In Proceedings of the 7th Asian Symposium on
Programming Languages and Systems (APLAS '09), Zhenjiang Hu
(Ed.). Springer-Verlag, Berlin, Heidelberg,
95-110. DOI=10.1007/978-3-642-10672-9_9
http://dx.doi.org/10.1007/978-3-642-10672-9_9
.. [4] Ohad Kammar, Sam Lindley, and Nicolas Oury. 2013. Handlers in
action. SIGPLAN Not. 48, 9 (September 2013),
145-158. DOI=10.1145/2544174.2500590
http://doi.acm.org/10.1145/2544174.2500590
.. [5] Martin Hyland, Gordon Plotkin, John Power, Combining effects:
Sum and tensor, Theoretical Computer Science, Volume 357,
Issues 13, 25 July 2006, Pages 70-99, ISSN 0304-3975,
http://dx.doi.org/10.1016/j.tcs.2006.03.013.
(http://www.sciencedirect.com/science/article/pii/S0304397506002659)
.. [6] Paul Blain Levy. 2004. Call-By-Push-Value: A
Functional/Imperative Synthesis (Semantics Structures in
Computation, V. 2). Kluwer Academic Publishers, Norwell, MA,
USA.
.. [7] Plotkin, Gordon, and Matija Pretnar. "Handlers of algebraic
effects." Programming Languages and Systems. Springer Berlin
Heidelberg, 2009. 80-94.
.. [8] Pretnar, Matija. "Logic and handling of algebraic effects." (2010).

328
docs/effects/depeff.rst Normal file
View File

@ -0,0 +1,328 @@
.. _sect-depeff:
=================
Dependent Effects
=================
In the programs we have seen so far, the available effects have remained
constant. Sometimes, however, an operation can *change* the available
effects. The simplest example occurs when we have a state with a
dependent type—adding an element to a vector also changes its type, for
example, since its length is explicit in the type. In this section, we
will see how the library supports this. Firstly, we will see how states
with dependent types can be implemented. Secondly, we will see how the
effects can depend on the *result* of an effectful operation. Finally,
we will see how this can be used to implement a type-safe and
resource-safe protocol for file management.
Dependent States
----------------
Suppose we have a function which reads input from the console, converts
it to an integer, and adds it to a list which is stored in a ``STATE``.
It might look something like the following:
.. code-block:: idris
readInt : { [STATE (List Int), STDIO] } Eff ()
readInt = do let x = trim !getStr
put (cast x :: !get)
But what if, instead of a list of integers, we would like to store a
``Vect``, maintaining the length in the type?
.. code-block:: idris
readInt : { [STATE (Vect n Int), STDIO] } Eff ()
readInt = do let x = trim !getStr
put (cast x :: !get)
This will not type check! Although the vector has length ``n`` on entry
to ``readInt``, it has length ``S n`` on exit. The library allows us to
express this as follows:
.. code-block:: idris
readInt : { [STATE (Vect n Int), STDIO] ==>
[STATE (Vect (S n) Int), STDIO] } Eff ()
readInt = do let x = trim !getStr
putM (cast x :: !get)
The notation ``{ xs ==> xs } Eff a`` in a type means that the operation
begins with effects ``xs`` available, and ends with effects ``xs``
available. We have used ``putM`` to update the state, where the ``M``
suffix indicates that the *type* is being updated as well as the value.
It has the following type:
.. code-block:: idris
putM : y -> { [STATE x] ==> [STATE y] } Eff ()
Result-dependent Effects
------------------------
Often, whether a state is updated could depend on the success or
otherwise of an operation. In our ``readInt`` example, we might wish to
update the vector only if the input is a valid integer (i.e. all
digits). As a first attempt, we could try the following, returning a
``Bool`` which indicates success:
.. code-block:: idris
readInt : { [STATE (Vect n Int), STDIO] ==>
[STATE (Vect (S n) Int), STDIO] } Eff Bool
readInt = do let x = trim !getStr
case all isDigit (unpack x) of
False => pure False
True => do putM (cast x :: !get)
pure True
Unfortunately, this will not type check because the vector does not get
extended in both branches of the ``case``!
::
MutState.idr:18:19:When elaborating right hand side of Main.case
block in readInt:
Unifying n and S n would lead to infinite value
Clearly, the size of the resulting vector depends on whether or not the
value read from the user was valid. We can express this in the type:
.. code-block:: idris
readInt : { [STATE (Vect n Int), STDIO] ==>
{ok} if ok then [STATE (Vect (S n) Int), STDIO]
else [STATE (Vect n Int), STDIO] } Eff Bool
readInt = do let x = trim !getStr
case all isDigit (unpack x) of
False => pure False
True => do putM (cast x :: !get)
pure True
The notation ``{ xs ==> res xs } Eff a`` in a type means that the
effects available are updated from ``xs`` to ``xs``, *and* the
resulting effects ``xs`` may depend on the result of the operation
``res``, of type ``a``. Here, the resulting effects are computed from
the result ``ok``—if ``True``, the vector is extended, otherwise it
remains the same.
When using the function, we will naturally have to check its return
value in order to know what the new set of effects is. For example, to
read a set number of values into a vector, we could write the following:
.. code-block:: idris
readN : (n : Nat) ->
{ [STATE (Vect m Int), STDIO] ==>
[STATE (Vect (n + m) Int), STDIO] } Eff ()
readN Z = pure ()
readN {m} (S k) = case !readInt of
True => rewrite plusSuccRightSucc k m in readN k
False => readN (S k)
The ``case`` analysis on the result of ``readInt`` means that we know in
each branch whether reading the integer succeeded, and therefore how
many values still need to be read into the vector. What this means in
practice is that the type system has verified that a necessary dynamic
check (i.e. whether reading a value succeeded) has indeed been done.
.. note::
Only ``case`` will work here. We cannot use ``if/then/else``
because the ``then`` and ``else`` branches must have the same
type. The ``case`` construct, however, abstracts over the value
being inspected in the type of each branch.
File Management
---------------
A practical use for dependent effects is in specifying resource usage
protocols and verifying that they are executed correctly. For example,
file management follows a resource usage protocol with the following
(informally specified) requirements:
- It is necessary to open a file for reading before reading it
- Opening may fail, so the programmer should check whether opening was
successful
- A file which is open for reading must not be written to, and vice
versa
- When finished, an open file handle should be closed
- When a file is closed, its handle should no longer be used
These requirements can be expressed formally in , by creating a
``FILE_IO`` effect parameterised over a file handle state, which is
either empty, open for reading, or open for writing. The ``FILE_IO``
effects definition is given below. Note that this
effect is mainly for illustrative purposes—typically we would also like
to support random access files and better reporting of error conditions.
.. code-block:: idris
module Effect.File
import Effects
import Control.IOExcept
FILE_IO : Type -> EFFECT
data OpenFile : Mode -> Type
open : String -> (m : Mode) ->
{ [FILE_IO ()] ==>
{ok} [FILE_IO (if ok then OpenFile m else ())] } Eff Bool
close : { [FILE_IO (OpenFile m)] ==> [FILE_IO ()] } Eff ()
readLine : { [FILE_IO (OpenFile Read)] } Eff String
writeLine : { [FILE_IO (OpenFile Write)] } Eff ()
eof : { [FILE_IO (OpenFile Read)] } Eff Bool
instance Handler FileIO IO
In particular, consider the type of ``open``:
.. code-block:: idris
open : String -> (m : Mode) ->
{ [FILE_IO ()] ==>
{ok} [FILE_IO (if ok then OpenFile m else ())] } Eff Bool
This returns a ``Bool`` which indicates whether opening the file was
successful. The resulting state depends on whether the operation was
successful; if so, we have a file handle open for the stated purpose,
and if not, we have no file handle. By ``case`` analysis on the result,
we continue the protocol accordingly.
.. _eff-readfile:
.. code-block:: idris
readFile : { [FILE_IO (OpenFile Read)] } Eff (List String)
readFile = readAcc [] where
readAcc : List String -> { [FILE_IO (OpenFile Read)] }
Eff (List String)
readAcc acc = if (not !eof)
then readAcc (!readLine :: acc)
else pure (reverse acc)
Given a function ``readFile``, above, which reads from
an open file until reaching the end, we can write a program which opens
a file, reads it, then displays the contents and closes it, as follows,
correctly following the protocol:
.. code-block:: idris
dumpFile : String -> { [FILE_IO (), STDIO] } Eff ()
dumpFile name = case !(open name Read) of
True => do putStrLn (show !readFile)
close
False => putStrLn ("Error!")
The type of ``dumpFile``, with ``FILE_IO ()`` in its effect list,
indicates that any use of the file resource will follow the protocol
correctly (i.e. it both begins and ends with an empty resource). If we
fail to follow the protocol correctly (perhaps by forgetting to close
the file, failing to check that ``open`` succeeded, or opening the file
for writing) then we will get a compile-time error. For example,
changing ``open name Read`` to ``open name Write`` yields a compile-time
error of the following form:
::
FileTest.idr:16:18:When elaborating right hand side of Main.case
block in testFile:
Can't solve goal
SubList [(FILE_IO (OpenFile Read))]
[(FILE_IO (OpenFile Write)), STDIO]
In other words: when reading a file, we need a file which is open for
reading, but the effect list contains a ``FILE_IO`` effect carrying a
file open for writing.
Pattern-matching bind
---------------------
It might seem that having to test each potentially failing operation
with a ``case`` clause could lead to ugly code, with lots of
nested case blocks. Many languages support exceptions to improve this,
but unfortunately exceptions may not allow completely clean resource
management—for example, guaranteeing that any ``open`` which did succeed
has a corresponding close.
Idris supports *pattern-matching* bindings, such as the following:
.. code-block:: idris
dumpFile : String -> { [FILE_IO (), STDIO] } Eff ()
dumpFile name = do True <- open name Read
putStrLn (show !readFile)
close
This also has a problem: we are no longer dealing with the case where
opening a file failed! The solution is to extend the pattern-matching
binding syntax to give brief clauses for failing matches. Here, for
example, we could write:
.. code-block:: idris
dumpFile : String -> { [FILE_IO (), STDIO] } Eff ()
dumpFile name = do True <- open name Read | False => putStrLn "Error"
putStrLn (show !readFile)
close
This is exactly equivalent to the definition with the explicit ``case``.
In general, in a ``do``-block, the syntax:
.. code-block:: idris
do pat <- val | <alternatives>
p
is desugared to
.. code-block:: idris
do x <- val
case x of
pat => p
<alternatives>
There can be several ``alternatives``, separated by a vertical bar
``|``. For example, there is a ``SYSTEM`` effect which supports
reading command line arguments, among other things (see Appendix
:ref:`sect-appendix`). To read command line arguments, we can use
``getArgs``:
.. code-block:: idris
getArgs : { [SYSTEM] } Eff (List String)
A main program can read command line arguments as follows, where in the
list which is returned, the first element ``prog`` is the executable
name and the second is an expected argument:
.. code-block:: idris
emain : { [SYSTEM, STDIO] } Eff ()
emain = do [prog, arg] <- getArgs
putStrLn $ "Argument is " ++ arg
{- ... rest of function ... -}
Unfortunately, this will not fail gracefully if no argument is given, or
if too many arguments are given. We can use pattern matching bind
alternatives to give a better (more informative) error:
.. code-block:: idris
emain : { [SYSTEM, STDIO] } Eff ()
emain = do [prog, arg] <- getArgs | [] => putStrLn "Can't happen!"
| [prog] => putStrLn "No arguments!"
| _ => putStrLn "Too many arguments!"
putStrLn $ "Argument is " ++ arg
{- ... rest of function ... -}
If ``getArgs`` does not return something of the form ``[prog, arg]`` the
alternative which does match is executed instead, and that value
returned.

333
docs/effects/hangman.rst Normal file
View File

@ -0,0 +1,333 @@
.. _sect-hangman:
=======================================
Example: A “Mystery Word” Guessing Game
=======================================
In this section, we will use the techniques and specific effects
discussed in the tutorial so far to implement a larger example, a simple
text-based word-guessing game. In the game, the computer chooses a word,
which the player must guess letter by letter. After a limited number of
wrong guesses, the player loses [1]_.
We will implement the game by following these steps:
#. Define the game state, in enough detail to express the rules
#. Define the rules of the game (i.e. what actions the player may take,
and how these actions affect the game state)
#. Implement the rules of the game (i.e. implement state updates for
each action)
#. Implement a user interface which allows a player to direct actions
Step 2 may be achieved by defining an effect which depends on the state
defined in step 1. Then step 3 involves implementing a ``Handler`` for
this effect. Finally, step 4 involves implementing a program in ``Eff``
using the newly defined effect (and any others required to implement the
interface).
Step 1: Game State
------------------
First, we categorise the game states as running games (where there are a
number of guesses available, and a number of letters still to guess), or
non-running games (i.e. games which have not been started, or games
which have been won or lost).
.. code-block:: idris
data GState = Running Nat Nat | NotRunning
Notice that at this stage, we say nothing about what it means to make a
guess, what the word to be guessed is, how to guess letters, or any
other implementation detail. We are only interested in what is necessary
to describe the game rules.
We will, however, parameterise a concrete game state ``Mystery`` over
this data:
.. code-block:: idris
data Mystery : GState -> Type
Step 2: Game Rules
------------------
We describe the game rules as a dependent effect, where each action has
a *precondition* (i.e. what the game state must be before carrying out
the action) and a *postcondition* (i.e. how the action affects the game
state). Informally, these actions with the pre- and postconditions are:
Guess
Guess a letter in the word.
- Precondition: The game must be running, and there must be both
guesses still available, and letters still to be guessed.
- Postcondition: If the guessed letter is in the word and not yet
guessed, reduce the number of letters, otherwise reduce the
number of guesses.
Won
Declare victory
- Precondition: The game must be running, and there must be no
letters still to be guessed.
- Postcondition: The game is no longer running.
Lost
Accept defeat
- Precondition: The game must be running, and there must be no
guesses left.
- Postcondition: The game is no longer running.
NewWord
Set a new word to be guessed
- Precondition: The game must not be running.
- Postcondition: The game is running, with 6 guesses available (the
choice of 6 is somewhat arbitrary here) and the number of unique
letters in the word still to be guessed.
StrState
Get a string representation of the game state. This is for display
purposes; there are no pre- or postconditions.
We can make these rules precise by declaring them more formally in an
effect signature:
.. code-block:: idris
data MysteryRules : Effect where
Guess : (x : Char) ->
{ Mystery (Running (S g) (S w)) ==>
{inword} if inword then Mystery (Running (S g) w)
else Mystery (Running g (S w)) }
MysteryRules Bool
Won : { Mystery (Running g 0) ==> Mystery NotRunning } MysteryRules ()
Lost : { Mystery (Running 0 g) ==> Mystery NotRunning } MysteryRules ()
NewWord : (w : String) ->
{ Mystery NotRunning ==>
Mystery (Running 6 (length (letters w))) } MysteryRules ()
StrState : { Mystery h } MysteryRules String
This description says nothing about how the rules are implemented. In
particular, it does not specify *how* to tell whether a guessed letter
was in a word, just that the result of ``Guess`` depends on it.
Nevertheless, we can still create an ``EFFECT`` from this, and use it in
an ``Eff`` program. Implementing a ``Handler`` for ``MysteryRules`` will
then allow us to play the game.
.. code-block:: idris
MYSTERY : GState -> EFFECT
MYSTERY h = MkEff (Mystery h) MysteryRules
Step 3: Implement Rules
-----------------------
To *implement* the rules, we begin by giving a concrete definition of
game state:
.. code-block:: idris
data Mystery : GState -> Type where
Init : Mystery NotRunning
GameWon : (word : String) -> Mystery NotRunning
GameLost : (word : String) -> Mystery NotRunning
MkG : (word : String) ->
(guesses : Nat) ->
(got : List Char) ->
(missing : Vect m Char) ->
Mystery (Running guesses m)
If a game is ``NotRunning``, that is either because it has not yet
started (``Init``) or because it is won or lost (``GameWon`` and
``GameLost``, each of which carry the word so that showing the game
state will reveal the word to the player). Finally, ``MkG`` captures a
running games state, including the target word, the letters
successfully guessed, and the missing letters. Using a ``Vect`` for the
missing letters is convenient since its length is used in the type.
To initialise the state, we implement the following functions:
``letters``, which returns a list of unique letters in a ``String``
(ignoring spaces) and ``initState`` which sets up an initial state
considered valid as a postcondition for ``NewWord``.
.. code-block:: idris
letters : String -> List Char
initState : (x : String) -> Mystery (Running 6 (length (letters x)))
When checking if a guess is in the vector of missing letters, it is
convenient to return a *proof* that the guess is in the vector, using
``isElem`` below, rather than merely a ``Bool``:
.. code-block:: idris
data IsElem : a -> Vect n a -> Type where
First : IsElem x (x :: xs)
Later : IsElem x xs -> IsElem x (y :: xs)
isElem : DecEq a => (x : a) -> (xs : Vect n a) -> Maybe (IsElem x xs)
The reason for returning a proof is that we can use it to remove an
element from the correct position in a vector:
.. code-block:: idris
shrink : (xs : Vect (S n) a) -> IsElem x xs -> Vect n a
We leave the definitions of ``letters``, ``init``, ``isElem`` and
``shrink`` as exercises. Having implemented these, the ``Handler``
implementation for ``MysteryRules`` is surprisingly straightforward:
.. code-block:: idris
instance Handler MysteryRules m where
handle (MkG w g got []) Won k = k () (GameWon w)
handle (MkG w Z got m) Lost k = k () (GameLost w)
handle st StrState k = k (show st) st
handle st (NewWord w) k = k () (initState w)
handle (MkG w (S g) got m) (Guess x) k =
case isElem x m of
Nothing => k False (MkG w _ got m)
(Just p) => k True (MkG w _ (x :: got) (shrink m p))
Each case simply involves directly updating the game state in a way
which is consistent with the declared rules. In particular, in
``Guess``, if the handler claims that the guessed letter is in the word
(by passing ``True`` to ``k``), there is no way to update the state in
such a way that the number of missing letters or number of guesses does
not follow the rules.
Step 4: Implement Interface
---------------------------
Having described the rules, and implemented state transitions which
follow those rules as an effect handler, we can now write an interface
for the game which uses the ``MYSTERY`` effect:
.. code-block:: idris
game : { [MYSTERY (Running (S g) w), STDIO] ==>
[MYSTERY NotRunning, STDIO] } Eff ()
The type indicates that the game must start in a running state, with
some guesses available, and eventually reach a not-running state (i.e.
won or lost). The only way to achieve this is by correctly following the
stated rules.
Note that the type of ``game`` makes no assumption that there are
letters to be guessed in the given word (i.e. it is ``w`` rather than
``S w``). This is because we will be choosing a word at random from a
vector of ``String``, and at no point have we made it explicit that
those ``String`` are non-empty.
Finally, we need to initialise the game by picking a word at random from
a list of candidates, setting it as the target using ``NewWord``, then
running ``game``:
.. code-block:: idris
runGame : { [MYSTERY NotRunning, RND, SYSTEM, STDIO] } Eff ()
runGame = do srand (cast !time)
let w = index !(rndFin WEWEWE) words
NewWord w
game
putStrLn !StrState
We use the system time (``time`` from the ``SYSTEM`` effect; see
Appendix :ref:`sect-appendix`) to initialise the random number
generator, then pick a random ``Fin`` to index into a list of
``words``. For example, we could initialise a word list as follows:
.. code-block:: idris
words : ?wtype
words = with Vect ["idris","agda","haskell","miranda",
"java","javascript","fortran","basic",
"coffeescript","rust"]
wtype = proof search
.. note::
Rather than have to explicitly declare a type with the vectors
length, it is convenient to give a metavariable ``?wtype`` and let
Idriss proof search mechanism find the type. This is a
limited form of type inference, but very useful in practice.
A possible complete implementation of ``game`` is
presented below:
.. code-block:: idris
game : { [MYSTERY (Running (S g) w), STDIO] ==>
[MYSTERY NotRunning, STDIO] } Eff ()
game {w=Z} = Won
game {w=S _}
= do putStrLn !StrState
putStr "Enter guess: "
let guess = trim !getStr
case choose (not (guess == "")) of
(Left p) => processGuess (strHead' guess p)
(Right p) => do putStrLn "Invalid input!"
game
where
processGuess : Char -> { [MYSTERY (Running (S g) (S w)), STDIO] ==>
[MYSTERY NotRunning, STDIO] }
Eff ()
processGuess {g} {w} c
= case !(Main.Guess c) of
True => do putStrLn "Good guess!"
case w of
Z => Won
(S k) => game
False => do putStrLn "No, sorry"
case g of
Z => Lost
(S k) => game
Discussion
----------
Writing the rules separately as an effect, then an implementation
which uses that effect, ensures that the implementation must follow
the rules. This has practical applications in more serious contexts;
``MysteryRules`` for example can be though of as describing a
*protocol* that a game player most follow, or alternative a
*precisely-typed API*.
In practice, we wouldnt really expect to write rules first then
implement the game once the rules were complete. Indeed, I didnt do
so when constructing this example! Rather, I wrote down a set of
likely rules making any assumptions *explicit* in the state
transitions for ``MysteryRules``. Then, when implementing ``game`` at
first, any incorrect assumption was caught as a type error. The
following errors were caught during development:
- Not realising that allowing ``NewWord`` to be an arbitrary string
would mean that ``game`` would have to deal with a zero-length word
as a starting state.
- Forgetting to check whether a game was won before recursively
calling ``processGuess``, thus accidentally continuing a finished
game.
- Accidentally checking the number of missing letters, rather than the
number of remaining guesses, when checking if a game was lost.
These are, of course, simple errors, but were caught by the type
checker before any testing of the game.
.. [1]
Readers may recognise this game by the name “Hangman.”

323
docs/effects/impleff.rst Normal file
View File

@ -0,0 +1,323 @@
.. _sect-impleff:
====================
Creating New Effects
====================
We have now seen several side-effecting operations provided by the
``Effects`` library, and examples of their use in Section
:ref:`sect-simpleff`. We have also seen how operations may *modify*
the available effects by changing state in Section
:ref:`sect-depeff`. We have not, however, yet seen how these
operations are implemented. In this section, we describe how a
selection of the available effects are implemented, and show how new
effectful operations may be provided.
State
-----
Effects are described by *algebraic data types*, where the
constructors describe the operations provided when the effect is
available. Stateful operations are described as follows:
.. code-block:: idris
data State : Effect where
Get : { a } State a
Put : b -> { a ==> b } State ()
Each effect is associated with a *resource*, the type of which is
given with the notation ``{ x ==> x }``. This notation gives the
resource type expected by each operation, and how it updates when the
operation is run. Here, it means:
- ``Get`` takes no arguments. It has a resource of type ``a``, which
is not updated, and running the ``Get`` operation returns something
of type ``a``.
- ``Put`` takes a ``b`` as an argument. It has a resource of type
``a`` on input, which is updated to a resource of type
``b``. Running the ``Put`` operation returns the element of the
unit type.
``Effect`` itself is a type synonym, declared as follows:
.. code-block:: idris
Effect : Type
Effect = (result : Type) ->
(input_resource : Type) ->
(output_resource : result -> Type) -> Type
That is, an effectful operation returns something of type ``result``,
has an input resource of type ``input_resource``, and a function
``output_resource`` which computes the output resource type from the
result. We use the same syntactic sugar as with ``Eff`` to make effect
declarations more readable. It is defined as follows in the library:
.. code-block:: idris
syntax "{" [inst] "}" [eff] = eff inst (\result => inst)
syntax "{" [inst] "==>" "{" {b} "}" [outst] "}" [eff]
= eff inst (\b => outst)
syntax "{" [inst] "==>" [outst] "}" [eff] = eff inst (\result => outst)
In order to convert ``State`` (of type ``Effect``) into something
usable in an effects list, of type ``EFFECT``, we write the following:
.. code-block:: idris
STATE : Type -> EFFECT
STATE t = MkEff t State
``MkEff`` constructs an ``EFFECT`` by taking the resource type (here,
the ``t`` which parameterises ``STATE``) and the effect signature
(here, ``State``). For reference, ``EFFECT`` is declared as follows:
.. code-block:: idris
data EFFECT : Type where
MkEff : Type -> Effect -> EFFECT
Recall that to run an effectful program in ``Eff``, we use one of the
``run`` family of functions to run the program in a particular
computation context ``m``. For each effect, therefore, we must explain
how it is executed in a particular computation context for ``run`` to
work in that context. This is achieved with the following type class:
.. code-block:: idris
class Handler (e : Effect) (m : Type -> Type) where
handle : resource -> (eff : e t resource resource') ->
((x : t) -> resource' x -> m a) -> m a
We have already seen some instance declarations in the effect
summaries in Section :ref:`sect-simpleff`. An instance of ``Handler e
m`` means that the effect declared with signature ``e`` can be run in
computation context ``m``. The ``handle`` function takes:
- The ``resource`` on input (so, the current value of the state for
``State``)
- The effectful operation (either ``Get`` or ``Put x`` for ``State``)
- A *continuation*, which we conventionally call ``k``, and should be
passed the result value of the operation, and an updated resource.
There are two reasons for taking a continuation here: firstly, this is
neater because there are multiple return values (a new resource and
the result of the operation); secondly, and more importantly, the
continuation can be called zero or more times.
A ``Handler`` for ``State`` simply passes on the value of the state,
in the case of ``Get``, or passes on a new state, in the case of
``Put``. It is defined the same way for all computation contexts:
.. code-block:: idris
instance Handler State m where
handle st Get k = k st st
handle st (Put n) k = k () n
This gives enough information for ``Get`` and ``Put`` to be used
directly in ``Eff`` programs. It is tidy, however, to define top level
functions in ``Eff``, as follows:
.. code-block:: idris
get : { [STATE x] } Eff x
get = call Get
put : x -> { [STATE x] } Eff ()
put val = call (Put val)
putM : y -> { [STATE x] ==> [STATE y] } Eff ()
putM val = call (Put val)
**An implementation detail (aside):** The ``call`` function converts
an ``Effect`` to a function in ``Eff``, given a proof that the effect
is available. This proof can be constructed automatically by , since
it is essentially an index into a statically known list of effects:
.. code-block:: idris
call : {e : Effect} ->
(eff : e t a b) -> {auto prf : EffElem e a xs} ->
Eff t xs (\v => updateResTy v xs prf eff)
This is the reason for the ``Cant solve goal`` error when an effect
is not available: the implicit proof ``prf`` has not been solved
automatically because the required effect is not in the list of
effects ``xs``.
Such details are not important for using the library, or even writing
new effects, however.
Summary
~~~~~~~
The following listing summarises what is required to define the
``STATE`` effect:
.. code-block:: idris
data State : Effect where
Get : { a } State a
Put : b -> { a ==> b } State ()
STATE : Type -> EFFECT
STATE t = MkEff t State
instance Handler State m where
handle st Get k = k st st
handle st (Put n) k = k () n
get : { [STATE x] } Eff x
get = call Get
put : x -> { [STATE x] } Eff ()
put val = call (Put val)
putM : y -> { [STATE x] ==> [STATE y] } Eff ()
putM val = call (Put val)
Console I/O
-----------
Then listing below gives the definition of the ``STDIO``
effect, including handlers for ``IO`` and ``IOExcept``. We omit the
definition of the top level ``Eff`` functions, as this merely invoke
the effects ``PutStr``, ``GetStr``, ``PutCh`` and ``GetCh`` directly.
Note that in this case, the resource is the unit type in every case,
since the handlers merely apply the ``IO`` equivalents of the effects
directly.
.. _eff-stdiodef:
.. code-block:: idris
data StdIO : Effect where
PutStr : String -> { () } StdIO ()
GetStr : { () } StdIO String
PutCh : Char -> { () } StdIO ()
GetCh : { () } StdIO Char
instance Handler StdIO IO where
handle () (PutStr s) k = do putStr s; k () ()
handle () GetStr k = do x <- getLine; k x ()
handle () (PutCh c) k = do putChar c; k () ()
handle () GetCh k = do x <- getChar; k x ()
instance Handler StdIO (IOExcept a) where
handle () (PutStr s) k = do ioe_lift $ putStr s; k () ()
handle () GetStr k = do x <- ioe_lift $ getLine; k x ()
handle () (PutCh c) k = do ioe_lift $ putChar c; k () ()
handle () GetCh k = do x <- ioe_lift $ getChar; k x ()
STDIO : EFFECT
STDIO = MkEff () StdIO
Exceptions
----------
The listing below gives the definition of the ``Exception``
effect, including two of its handlers for ``Maybe`` and ``List``. The
only operation provided is ``Raise``. The key point to note in the
definitions of these handlers is that the continuation ``k`` is not
used. Running ``Raise`` therefore means that computation stops with an
error.
.. code-block:: idris
data Exception : Type -> Effect where
Raise : a -> { () } Exception a b
instance Handler (Exception a) Maybe where
handle _ (Raise e) k = Nothing
instance Handler (Exception a) List where
handle _ (Raise e) k = []
EXCEPTION : Type -> EFFECT
EXCEPTION t = MkEff () (Exception t)
Non-determinism
---------------
The following listing gives the definition of the ``Select``
effect for writing non-deterministic programs, including a handler for
``List`` context which returns all possible successful values, and a
handler for ``Maybe`` context which returns the first successful
value.
.. code-block:: idris
data Selection : Effect where
Select : List a -> { () } Selection a
instance Handler Selection Maybe where
handle _ (Select xs) k = tryAll xs where
tryAll [] = Nothing
tryAll (x :: xs) = case k x () of
Nothing => tryAll xs
Just v => Just v
instance Handler Selection List where
handle r (Select xs) k = concatMap (\x => k x r) xs
SELECT : EFFECT
SELECT = MkEff () Selection
Here, the continuation is called multiple times in each handler, for
each value in the list of possible values. In the ``List`` handler, we
accumulate all successful results, and in the ``Maybe`` handler we try
the first value in the last, and try later values only if that fails.
File Management
---------------
Result-dependent effects are no different from non-dependent effects
in the way they are implemented. The listing below
illustrates this for the ``FILE_IO`` effect. The syntax for state
transitions ``{ x ==> {res} x }``, where the result state ``x`` is
computed from the result of the operation ``res``, follows that for
the equivalent ``Eff`` programs.
.. code-block:: idris
data FileIO : Effect where
Open : String -> (m : Mode) ->
{() ==> {res} if res then OpenFile m else ()} FileIO Bool
Close : {OpenFile m ==> ()} FileIO ()
ReadLine : {OpenFile Read} FileIO String
WriteLine : String -> {OpenFile Write} FileIO ()
EOF : {OpenFile Read} FileIO Bool
instance Handler FileIO IO where
handle () (Open fname m) k = do h <- openFile fname m
if !(validFile h)
then k True (FH h)
else k False ()
handle (FH h) Close k = do closeFile h
k () ()
handle (FH h) ReadLine k = do str <- fread h
k str (FH h)
handle (FH h) (WriteLine str) k = do fwrite h str
k () (FH h)
handle (FH h) EOF k = do e <- feof h
k e (FH h)
FILE_IO : Type -> EFFECT
FILE_IO t = MkEff t FileIO
Note that in the handler for ``Open``, the types passed to the
continuation ``k`` are different depending on whether the result is
``True`` (opening succeeded) or ``False`` (opening failed). This uses
``validFile``, defined in the ``Prelude``, to test whether a file
handler refers to an open file or not.

28
docs/effects/index.rst Normal file
View File

@ -0,0 +1,28 @@
.. _eff-tutorial-index:
####################
The Effects Tutorial
####################
A tutorial on the `Effects` package in `Idris`.
.. note::
The documentation for Idris has been published under the Creative
Commons CC0 License. As such to the extent possible under law, *The
Idris Community* has waived all copyright and related or neighboring
rights to Documentation for Idris.
More information concerning the CC0 can be found online at: http://creativecommons.org/publicdomain/zero/1.0/
.. toctree::
:maxdepth: 1
introduction
state
simpleeff
depeff
impleff
hangman
conclusions
summary

View File

@ -0,0 +1,113 @@
============
Introduction
============
Pure functional languages with dependent types such as `Idris
<http://idris-lang.org/>`_ support reasoning about programs directly
in the type system, promising that we can *know* a program will run
correctly (i.e. according to the specification in its type) simply
because it compiles. Realistically, though, things are not so simple:
programs have to interact with the outside world, with user input,
input from a network, mutable state, and so on. In this tutorial I
will introduce the library, which is included with the distribution
and supports programming and reasoning with side-effecting programs,
supporting mutable state, interaction with the outside world,
exceptions, and verified resource management.
This tutorial assumes familiarity with pure programming in Idris,
as described in Sections 16 of the main tutorial [1]_. The examples
are presented are tested with Idris and can be found in the
examples directory of the Idris repository.
Consider, for example, the following introductory function which
illustrates the kind of properties which can be expressed in the type
system:
.. code-block:: idris
vadd : Vect n Int -> Vect n Int -> Vect n Int
vadd [] [] = []
vadd (x :: xs) (y :: ys) = x + y :: vadd xs ys
This function adds corresponding elements in a pair of vectors. The type
guarantees that the vectors will contain only elements of type ``Int``,
and that the input lengths and the output length all correspond. A
natural question to ask here, which is typically neglected by
introductory tutorials, is “How do I turn this into a program?” That is,
given some lists entered by a user, how do we get into a position to be
able to apply the ``vadd`` function? Before doing so, we will have to:
- Read user input, either from the keyboard, a file, or some other
input device.
- Check that the user inputs are valid, i.e. contain only ``Int`` s
and are the same length, and report an error if not.
- Write output
The complete program will include side-effects for I/O and error
handling, before we can get to the pure core functionality. In this
tutorial, we will see how Idris supports side-effects.
Furthermore, we will see how we can use the dependent type system to
*reason* about stateful and side-effecting programs. We will return to
this specific example later.
Hello world
===========
To give an idea of how programs with effects look in , here is the
ubiquitous “Hello world” program, written using the ``Effects``
library:
.. code-block:: idris
module Main
import Effects
import Effect.StdIO
hello : [STDIO] Eff ()
hello = putStrLn “Hello world!”
main : IO ()
main = run hello
As usual, the entry point is ``main``. All ``main`` has to do is invoke the
``hello`` function which supports the ``STDIO`` effect for console I/O, and
returns the unit value. All programs using the ``Effects`` library must
``import Effects``. The details of the ``Eff`` type will be presented in the
remainder of this tutorial.
To compile and run this program, Idris needs to be told to include
the ``Effects`` package, using the ``-p effects`` flag (this flag is
required for all examples in this tutorial):
.. code-block:: sh
idris hello.idr -o hello -p effects
./hello Hello world!
Outline
=======
The tutorial is structured as follows: first, in Section
:ref:`sect-state`, we will discuss state management, describing why it
is important and introducing the ``effects`` library to show how it
can be used to manage state. This section also gives an overview of
the syntax of effectful programs. Section :ref:`sect-simpleff` then
introduces a number of other effects a program may have: I/O;
Exceptions; Random Numbers; and Non-determinism, giving examples for
each, and an extended example combining several effects in one
complete program. Section :ref:`sect-depeff` introduces *dependent*
effects, showing how states and resources can be managed in
types. Section :ref:`sect-impleff` shows how new effects can be
implemented. Section :ref:`sect-hangman` gives an extended example
showing how to implement a “mystery word” guessing game, using effects
to describe the rules of the game and ensure they are implemented
accurately. References to further reading are given in Section
:ref:`sect-further`.
.. [1]
You do not, however, need to know what a monad is. A correctness
property of this tutorial is that the word “monad” should appear
exactly twice, both in this footnote.

592
docs/effects/simpleeff.rst Normal file
View File

@ -0,0 +1,592 @@
.. _sect-simpleff:
==============
Simple Effects
==============
So far we have seen how to write programs with locally mutable state
using the ``STATE`` effect. To recap, we have the definitions below
in a module ``Effect.State``
.. code-block:: idris
module Effect.State
STATE : Type -> EFFECT
get : { [STATE x] } Eff x
put : x -> { [STATE x] } Eff ()
putM : y -> { [STATE x] ==> [STATE y] } Eff ()
update : (x -> x) -> { [STATE x] } Eff ()
instance Handler State m
The last line, ``instance Handler State m``, means that the ``STATE``
effect is usable in any computation context ``m``. That is, a program
which uses this effect and returns something of type ``a`` can be
evaluated to something of type ``m a`` using ``run``, for any
``m``. The lower case ``State`` is a data type describing the
operations which make up the ``STATE`` effect itself—we will go into
more detail about this in Section :ref:`sect-impleff`.
In this section, we will introduce some other supported effects,
allowing console I/O, exceptions, random number generation and
non-deterministic programming. For each effect we introduce, we will
begin with a summary of the effect, its supported operations, and the
contexts in which it may be used, like that above for ``STATE``, and
go on to present some simple examples. At the end, we will see some
examples of programs which combine multiple effects.
All of the effects in the library, including those described in this
section, are summarised in Appendix :ref:`sect-appendix`.
Console I/O
-----------
Console I/O is supported with the ``STDIO``
effect, which allows reading and writing characters and strings to and
from standard input and standard output. Notice that there is a
constraint here on the computation context ``m``, because it only
makes sense to support console I/O operations in a context where we
can perform (or at the very least simulate) console I/O:
.. code-block:: idris
module Effect.StdIO
STDIO : EFFECT
putChar : Char -> { [STDIO] } Eff ()
putStr : String -> { [STDIO] } Eff ()
putStrLn : String -> { [STDIO] } Eff ()
getStr : { [STDIO] } Eff String
getChar : { [STDIO] } Eff Char
instance Handler StdIO IO
instance Handler StdIO (IOExcept a)
Examples
~~~~~~~~
A program which reads the users name, then says hello, can be written
as follows:
.. code-block:: idris
hello : { [STDIO] } Eff ()
hello = do putStr "Name? "
x <- getStr
putStrLn ("Hello " ++ trim x ++ "!")
We use ``trim`` here to remove the trailing newline from the
input. The resource associated with ``STDIO`` is simply the empty
tuple, which has a default value ``()``, so we can run this as
follows:
.. code-block:: idris
main : IO ()
main = run hello
In ``hello`` we could also use ``!``-notation instead of ``x <-
getStr``, since we only use the string that is read once:
.. code-block:: idris
hello : { [STDIO] } Eff ()
hello = do putStr "Name? "
putStrLn ("Hello " ++ trim !getStr ++ "!")
More interestingly, we can combine multiple effects in one
program. For example, we can loop, counting the number of people weve
said hello to:
.. code-block:: idris
hello : { [STATE Int, STDIO] } Eff ()
hello = do putStr "Name? "
putStrLn ("Hello " ++ trim !getStr ++ "!")
update (+1)
putStrLn ("I've said hello to: " ++ show !get ++ " people")
hello
The list of effects given in ``hello`` means that the function can
call ``get`` and ``put`` on an integer state, and any functions which
read and write from the console. To run this, ``main`` does not need
to be changed.
Aside: Resource Types
~~~~~~~~~~~~~~~~~~~~~
To find out the resource type of an effect, if necessary (for example
if we want to initialise a resource explicitiy with ``runInit`` rather
than using a default value with ``run``) we can run the
``resourceType`` function at the REPL:
.. code-block:: idris
*ConsoleIO> resourceType STDIO
() : Type
*ConsoleIO> resourceType (STATE Int)
Int : Type
Exceptions
----------
The ``EXCEPTION``
effect is declared in module ``Effect.Exception``. This allows programs
to exit immediately with an error, or errors to be handled more
generally:
.. _eff-exception:
.. code-block:: idris
module Effect.Exception
EXCEPTION : Type -> EFFECT
raise : a -> { [EXCEPTION a ] } Eff b
instance Handler (Exception a) Maybe
instance Handler (Exception a) List
instance Handler (Exception a) (Either a)
instance Handler (Exception a) (IOExcept a)
instance Show a => Handler (Exception a) IO
Example
~~~~~~~
Suppose we have a ``String`` which is expected to represent an integer
in the range ``0`` to ``n``. We can write a function ``parseNumber``
which returns an ``Int`` if parsing the string returns a number in the
appropriate range, or throws an exception otherwise. Exceptions are
paramaterised by an error type:
.. code-block:: idris
data Err = NotANumber | OutOfRange
parseNumber : Int -> String -> { [EXCEPTION Err] } Eff Int
parseNumber num str
= if all isDigit (unpack str)
then let x = cast str in
if (x >=0 && x <= num)
then pure x
else raise OutOfRange
else raise NotANumber
Programs which support the ``EXCEPTION`` effect can be run in any
context which has some way of throwing errors, for example, we can run
``parseNumber`` in the ``Either Err`` context. It returns a value of
the form ``Right x`` if successful:
.. code-block:: idris
*Exception> the (Either Err Int) $ run (parseNumber 42 "20")
Right 20 : Either Err Int
Or ``Left e`` on failure, carrying the appropriate exception:
.. code-block:: idris
*Exception> the (Either Err Int) $ run (parseNumber 42 "50")
Left OutOfRange : Either Err Int
*Exception> the (Either Err Int) $ run (parseNumber 42 "twenty")
Left NotANumber : Either Err Int
In fact, we can do a little bit better with ``parseNumber``, and have
it return a *proof* that the integer is in the required range along
with the integer itself. One way to do this is define a type of
bounded integers, ``Bounded``:
.. code-block:: idris
Bounded : Int -> Type
Bounded x = (n : Int ** So (n >= 0 && n <= x))
Recall that ``So`` is parameterised by a ``Bool``, and only ``So
True`` is inhabited. We can use ``choose`` to construct such a value
from the result of a dynamic check:
.. code-block:: idris
data So : Bool -> Type = Oh : So True
choose : (b : Bool) -> Either (So b) (So (not b))
We then write ``parseNumber`` using ``choose`` rather than an
``if/then/else`` construct, passing the proof it returns on success as
the boundedness proof:
.. code-block:: idris
parseNumber : (x : Int) -> String -> { [EXCEPTION Err] } Eff (Bounded x)
parseNumber x str
= if all isDigit (unpack str)
then let num = cast str in
case choose (num >=0 && num <= x) of
Left p => pure (num ** p)
Right p => raise OutOfRange
else raise NotANumber
Random Numbers
--------------
Random number generation is also implemented by the library, in module
``Effect.Random``:
.. code-block:: idris
module Effect.Random
RND : EFFECT
srand : Integer -> { [RND] } Eff ()
rndInt : Integer -> Integer -> { [RND] } Eff Integer
rndFin : (k : Nat) -> { [RND] } Eff (Fin (S k))
instance Handler Random m
Random number generation is considered side-effecting because its
implementation generally relies on some external source of randomness.
The default implementation here relies on an integer *seed*, which can
be set with ``srand``. A specific seed will lead to a predictable,
repeatable sequence of random numbers. There are two functions which
produce a random number:
- ``rndInt``, which returns a random integer between the given lower
and upper bounds.
- ``rndFin``, which returns a random element of a finite set
(essentially a number with an upper bound given in its type).
Example
~~~~~~~
We can use the ``RND`` effect to implement a simple guessing game. The
``guess`` function, given a target number, will repeatedly ask the
user for a guess, and state whether the guess is too high, too low, or
correct:
.. code-block:: idris
guess : Int -> { [STDIO] } Eff ()
For reference, the code for ``guess`` is given below:
.. _eff-game:
.. code-block:: idris
guess : Int -> { [STDIO] } Eff ()
guess target
= do putStr "Guess: "
case run {m=Maybe} (parseNumber 100 (trim !getStr)) of
Nothing => do putStrLn "Invalid input"
guess target
Just (v ** _) =>
case compare v target of
LT => do putStrLn "Too low"
guess target
EQ => putStrLn "You win!"
GT => do putStrLn "Too high"
guess target
Note that we use ``parseNumber`` as defined previously to read user input, but
we dont need to list the ``EXCEPTION`` effect because we use a nested ``run``
to invoke ``parseNumber``, independently of the calling effectful program.
To invoke this, we pick a random number within the range 0100,
having set up the random number generator with a seed, then run
``guess``:
.. code-block:: idris
game : { [RND, STDIO] } Eff ()
game = do srand 123456789
guess (fromInteger !(rndInt 0 100))
main : IO ()
main = run game
If no seed is given, it is set to the ``default`` value. For a less
predictable game, some better source of randomness would be required,
for example taking an initial seed from the system time. To see how to
do this, see the ``SYSTEM`` effect described in :ref:`sect-appendix`.
Non-determinism
---------------
The listing below gives the definition of the non-determinism
effect, which allows a program to choose a value non-deterministically
from a list of possibilities in such a way that the entire computation
succeeds:
.. code-block:: idris
import Effects
import Effect.Select
SELECT : EFFECT
select : List a -> { [SELECT] } Eff a
instance Handler Selection Maybe
instance Handler Selection List
Example
~~~~~~~
The ``SELECT`` effect can be used to solve constraint problems, such
as finding Pythagorean triples. The idea is to use ``select`` to give
a set of candidate values, then throw an exception for any combination
of values which does not satisfy the constraint:
.. code-block:: idris
triple : Int -> { [SELECT, EXCEPTION String] } Eff (Int, Int, Int)
triple max = do z <- select [1..max]
y <- select [1..z]
x <- select [1..y]
if (x * x + y * y == z * z)
then pure (x, y, z)
else raise "No triple"
This program chooses a value for ``z`` between ``1`` and ``max``, then
values for ``y`` and ``x``. In operation, after a ``select``, the
program executes the rest of the ``do``-block for every possible
assignment, effectively searching depth-first. If the list is empty
(or an exception is thrown) execution fails.
There are handlers defined for ``Maybe`` and ``List`` contexts, i.e.
contexts which can capture failure. Depending on the context ``m``,
``triple`` will either return the first triple it finds (if in
``Maybe`` context) or all triples in the range (if in ``List``
context). We can try this as follows:
.. code-block:: idris
main : IO ()
main = do print $ the (Maybe _) $ run (triple 100)
print $ the (List _) $ run (triple 100)
``vadd`` revisited
------------------
We now return to the ``vadd`` program from the introduction. Recall the
definition:
.. code-block:: idris
vadd : Vect n Int -> Vect n Int -> Vect n Int
vadd [] [] = []
vadd (x :: idris xs) (y :: ys) = x + y :: vadd xs ys
Using , we can set up a program so that it reads input from a user,
checks that the input is valid (i.e both vectors contain integers, and
are the same length) and if so, pass it on to ``vadd``. First, we
write a wrapper for ``vadd`` which checks the lengths and throw an
exception if they are not equal. We can do this for input vectors of
length ``n`` and ``m`` by matching on the implicit arguments ``n`` and
``m`` and using ``decEq`` to produce a proof of their equality, if
they are equal:
.. code-block:: idris
vadd_check : Vect n Int -> Vect m Int ->
{ [EXCEPTION String] } Eff (Vect m Int)
vadd_check {n} {m} xs ys with (decEq n m)
vadd_check {n} {m=n} xs ys | (Yes Refl) = pure (vadd xs ys)
vadd_check {n} {m} xs ys | (No contra) = raise "Length mismatch"
To read a vector from the console, we implement a function of the
following type:
.. code-block:: idris
read_vec : { [STDIO] } Eff (p ** Vect p Int)
This returns a dependent pair of a length, and a vector of that
length, because we cannot know in advance how many integers the user
is going to input. One way to implement this function, using ``-1`` to
indicate the end of input, is shown in Listing [readvec]. This uses a
variation on ``parseNumber`` which does not require a number to be
within range.
Finally, we write a program which reads two vectors and prints the
result of pairwise addition of them, throwing an exception if the
inputs are of differing lengths:
.. code-block:: idris
do_vadd : { [STDIO, EXCEPTION String] } Eff ()
do_vadd = do putStrLn "Vector 1"
(_ ** xs) <- read_vec
putStrLn "Vector 2"
(_ ** ys) <- read_vec
putStrLn (show !(vadd_check xs ys))
By having explicit lengths in the type, we can be sure that ``vadd``
is only being used where the lengths of inputs are guaranteed to be
equal. This does not stop us reading vectors from user input, but it
does require that the lengths are checked and any discrepancy is dealt
with gracefully.
.. code-block:: idris
read_vec : { [STDIO] } Eff (p ** Vect p Int)
read_vec = do putStr "Number (-1 when done): "
case run (parseNumber (trim !getStr)) of
Nothing => do putStrLn "Input error"
read_vec
Just v => if (v /= -1)
then do (_ ** xs) <- read_vec
pure (_ ** v :: xs)
else pure (_ ** [])
where
parseNumber : String -> { [EXCEPTION String] } Eff Int
parseNumber str
= if all (\x => isDigit x || x == '-') (unpack str)
then pure (cast str)
else raise "Not a number"
Example: An Expression Calculator
---------------------------------
To show how these effects can fit together, let us consider an
evaluator for a simple expression language, with addition and integer
values.
.. code-block:: idris
data Expr = Val Integer
| Add Expr Expr
An evaluator for this language always returns an ``Integer``, and
there are no situations in which it can fail!
.. code-block:: idris
eval : Expr -> Integer
eval (Val x) = x
eval (Add l r) = eval l + eval r
If we add variables, however, things get more interesting. The
evaluator will need to be able to access the values stored in
variables, and variables may be undefined.
.. code-block:: idris
data Expr = Val Integer
| Var String
| Add Expr Expr
To start, we will change the type of ``eval`` so that it is effectful,
and supports an exception effect for throwing errors, and a state
containing a mapping from variable names (as ``String``) to their
values:
.. code-block:: idris
Env : Type
Env = List (String, Integer)
eval : Expr -> { [EXCEPTION String, STATE Env] } Eff Integer
eval (Val x) = return x
eval (Add l r) = return $ !(eval l) + !(eval r)
Note that we are using ``!``-notation to avoid having to bind
subexpressions in a ``do`` block. Next, we add a case for evaluating
``Var``:
.. code-block:: idris
eval (Var x) = case lookup x !get of
Nothing => raise $ "No such variable " ++ x
Just val => return val
This retrieves the state (with ``get``, supported by the ``STATE Env``
effect) and raises an exception if the variable is not in the
environment (with ``raise``, supported by the ``EXCEPTION String``
effect).
To run the evaluator on a particular expression in a particular
environment of names and their values, we can write a function which
sets the state then invokes ``eval``:
.. code-block:: idris
runEval : List (String, Integer) -> Expr -> Maybe Integer
runEval args expr = run (eval' expr)
where eval' : Expr -> { [EXCEPTION String, STATE Env] } Eff Integer
eval' e = do put args
eval e
We have picked ``Maybe`` as a computation context here; it needs to be
a context which is available for every effect supported by
``eval``. In particular, because we have exceptions, it needs to be a
context which supports exceptions. Alternatively, ``Either String`` or
``IO`` would be fine, for example.
What if we want to extend the evaluator further, with random number
generation? To achieve this, we add a new constructor to ``Expr``,
which gives a random number up to a maximum value:
.. code-block:: idris
data Expr = Val Integer
| Var String
| Add Expr Expr
| Random Integer
Then, we need to deal with the new case, making sure that we extend
the list of events to include ``RND``. It doent matter where ``RND``
appears in the list, as long as it is present:
.. code-block:: idris
eval : Expr -> { [EXCEPTION String, RND, STATE Env] } Eff Integer
eval (Random upper) = rndInt 0 upper
For test purposes, we might also want to print the random number which
has been generated:
.. code-block:: idris
eval (Random upper) = do val <- rndInt 0 upper
putStrLn (show val)
return val
If we try this without extending the effects list, we would see an
error something like the following:
::
Expr.idr:28:6:When elaborating right hand side of eval:
Can't solve goal
SubList [STDIO]
[(EXCEPTION String), RND, (STATE (List (String, Integer)))]
In other words, the ``STDIO`` effect is not available. We can correct
this simply by updating the type of ``eval`` to include ``STDIO``.
.. code-block:: idris
eval : Expr -> { [STDIO, EXCEPTION String, RND, STATE Env] } Eff Integer
Note that using ``STDIO`` will restrict the number of contexts in
which ``eval`` can be ``run`` to those which support ``STDIO``, such
as ``IO``. Once effect lists get longer, it can be a good idea instead
to encapsulate sets of effects in a type synonym. This is achieved as
follows, simply by defining a function which computes a type, since
types are first class in Idris:
.. code-block:: idris
EvalEff : Type -> Type
EvalEff t = { [STDIO, EXCEPTION String, RND, STATE Env] } Eff t
eval : Expr -> EvalEff Integer

519
docs/effects/state.rst Normal file
View File

@ -0,0 +1,519 @@
.. _sect-state:
=====
State
=====
Many programs, even pure programs, can benefit from locally mutable
state. For example, consider a program which tags binary tree nodes
with a counter, by an inorder traversal (i.e. counting depth first,
left to right). This would perform something like the following:
|image|
We can describe binary trees with the following data type ``BTree``
and ``testTree`` to represent the example input above:
.. code-block:: idris
data BTree a = Leaf
| Node (BTree a) a (BTree a)
testTree : BTree String
testTree = Node (Node Leaf "Jim" Leaf)
"Fred"
(Node (Node Leaf "Alice" Leaf)
"Sheila"
(Node Leaf "Bob" Leaf))
Then our function to implement tagging, beginning to tag with a
specific value ``i``, has the following type:
.. code-block:: idris
treeTag : (i : Int) -> BTree a -> BTree (Int, a)
First attempt
-------------
Naïvely, we can implement ``treeTag`` by implementing a helper
function which propagates a counter, returning the result of the count
for each subtree:
.. code-block:: idris
treeTagAux : (i : Int) -> BTree a -> (Int, BTree (Int, a))
treeTagAux i Leaf = (i, Leaf)
treeTagAux i (Node l x r)
= let (i', l') = treeTagAux i l in
let x' = (i', x) in
let (i'', r') = treeTagAux (i' + 1) r in
(i'', Node l' x' r')
treeTag : (i : Int) -> BTree a -> BTree (Int, a)
treeTag i x = snd (treeTagAux i x)
This gives the expected result when run at the REPL prompt:
.. code-block:: idris
*TreeTag> treeTag 1 testTree
Node (Node Leaf (1, "Jim") Leaf)
(2, "Fred")
(Node (Node Leaf (3, "Alice") Leaf)
(4, "Sheila")
(Node Leaf (5, "Bob") Leaf)) : BTree (Int, String)
This works as required, but there are several problems when we try to
scale this to larger programs. It is error prone, because we need to
ensure that state is propagated correctly to the recursive calls (i.e.
passing the appropriate ``i`` or ``i``). It is hard to read, because
the functional details are obscured by the state propagation. Perhaps
most importantly, there is a common programming pattern here which
should be abstracted but instead has been implemented by hand. There
is local mutable state (the counter) which we have had to make
explicit.
Introducing ``Effects``
-----------------------
Idris provides a library, ``Effects`` [3]_, which captures this
pattern and many others involving effectful computation [1]_. An
effectful program ``f`` has a type of the following form:
.. code-block:: idris
f : (x1 : a1) -> (x2 : a2) -> ... -> { effs } Eff t
That is, the return type gives the effects that ``f`` supports
(``effs``, of type ``List EFFECT``) and the type the computation
returns ``t``. So, our ``treeTagAux`` helper could be written with the
following type:
.. code-block:: idris
treeTagAux : BTree a -> { [STATE Int] } Eff (BTree (Int, a))
That is, ``treeTagAux`` has access to an integer state, because the
list of available effects includes ``STATE Int``. ``STATE`` is
declared as follows in the module ``Effect.State`` (that is, we must
``import Effect.State`` to be able to use it):
.. code-block:: idris
STATE : Type -> EFFECT
It is an effect parameterised by a type (by convention, we write
effects in all capitals). The ``treeTagAux`` function is an effectful
program which builds a new tree tagged with ``Ints``, and is
implemented as follows:
.. code-block:: idris
treeTagAux Leaf = pure Leaf
treeTagAux (Node l x r)
= do l' <- treeTagAux l
i <- get
put (i + 1)
r' <- treeTagAux r
pure (Node l' (i, x) r')
There are several remarks to be made about this implementation.
Essentially, it hides the state, which can be accessed using ``get``
and updated using ``put``, but it introduces several new features.
Specifically, it uses ``do``-notation, binding variables with ``<-``,
and a ``pure`` function. There is much to be said about these
features, but for our purposes, it suffices to know the following:
- ``do`` blocks allow effectful operations to be sequenced.
- ``x <- e`` binds the result of an effectful operation ``e`` to a
variable ``x``. For example, in the above code, ``treeTagAux l`` is
an effectful operation returning ``BTree (Int, a)``, so ``l`` has
type ``BTree (Int, a)``.
- ``pure e`` turns a pure value ``e`` into the result of an effectful
operation.
The ``get`` and ``put`` functions read and write a state ``t``,
assuming that the ``STATE t`` effect is available. They have the
following types, polymorphic in the state ``t`` they manage:
.. code-block:: idris
get : { [STATE t] } Eff t
put : t -> { [STATE t] } Eff ()
A program in ``Eff`` can call any other function in ``Eff`` provided
that the calling function supports at least the effects required by
the called function. In this case, it is valid for ``treeTagAux`` to
call both ``get`` and ``put`` because all three functions support the
``STATE Int`` effect.
Programs in ``Eff`` are run in some underlying *computation context*,
using the ``run`` or ``runPure`` function. Using ``runPure``, which
runs an effectful program in the identity context, we can write the
``treeTag`` function as follows, using ``put`` to initialise the
state:
.. code-block:: idris
treeTag : (i : Int) -> BTree a -> BTree (Int, a)
treeTag i x = runPure (do put i
treeTagAux x)
We could also run the program in an impure context such as ``IO``,
without changing the definition of ``treeTagAux``, by using ``run``
instead of ``runPure``:
.. code-block:: idris
treeTagAux : BTree a -> { [STATE Int] } Eff (BTree (Int, a))
...
treeTag : (i : Int) -> BTree a -> IO (BTree (Int, a))
treeTag i x = run (do put i
treeTagAux x)
Note that the definition of ``treeTagAux`` is exactly as before. For
reference, this complete program (including a ``main`` to run it) is
shown in Listing [introprog].
.. code-block:: idris
module Main
import Effects
import Effect.State
data BTree a = Leaf
| Node (BTree a) a (BTree a)
instance Show a => Show (BTree a) where
show Leaf = "[]"
show (Node l x r) = "[" ++ show l ++ " "
++ show x ++ " "
++ show r ++ "]"
testTree : BTree String
testTree = Node (Node Leaf "Jim" Leaf)
"Fred"
(Node (Node Leaf "Alice" Leaf)
"Sheila"
(Node Leaf "Bob" Leaf))
treeTagAux : BTree a -> { [STATE Int] } Eff (BTree (Int, a))
treeTagAux Leaf = pure Leaf
treeTagAux (Node l x r) = do l' <- treeTagAux l
i <- get
put (i + 1)
r' <- treeTagAux r
pure (Node l' (i, x) r')
treeTag : (i : Int) -> BTree a -> BTree (Int, a)
treeTag i x = runPure (do put i; treeTagAux x)
main : IO ()
main = print (treeTag 1 testTree)
Effects and Resources
---------------------
Each effect is associated with a *resource*, which is initialised
before an effectful program can be run. For example, in the case of
``STATE Int`` the corresponding resource is the integer state itself.
The types of ``runPure`` and ``run`` show this (slightly simplified
here for illustrative purposes):
.. code-block:: idris
runPure : {env : Env id xs} -> { xs } Eff a -> a
run : Applicative m => {env : Env m xs} -> { xs } Eff a -> m a
The ``env`` argument is implicit, and initialised automatically where
possible using default values given by instances of the following type
class:
.. code-block:: idris
class Default a where
default : a
Instances of ``Default`` are defined for all primitive types, and many
library types such as ``List``, ``Vect``, ``Maybe``, pairs, etc.
However, where no default value exists for a resource type (for
example, you may want a ``STATE`` type for which there is no
``Default`` instance) the resource environment can be given explicitly
using one of the following functions:
.. code-block:: idris
runPureInit : Env id xs -> { xs } Eff a -> a
runInit : Applicative m => Env m xs -> { xs } Eff a -> m a
To be well-typed, the environment must contain resources corresponding
exactly to the effects in ``xs``. For example, we could also have
implemented ``treeTag`` by initialising the state as follows:
.. code-block:: idris
treeTag : (i : Int) -> BTree a -> BTree (Int, a)
treeTag i x = runPureInit [i] (treeTagAux x)
Labelled Effects
----------------
What if we have more than one state, especially more than one state of
the same type? How would ``get`` and ``put`` know which state they
should be referring to? For example, how could we extend the tree
tagging example such that it additionally counts the number of leaves
in the tree? One possibility would be to change the state so that it
captured both of these values, e.g.:
.. code-block:: idris
treeTagAux : BTree a
-> { [STATE (Int, Int)] } Eff (BTree (Int, a))
Doing this, however, ties the two states together throughout (as well
as not indicating which integer is which). It would be nice to be able
to call effectful programs which guaranteed only to access one of the
states, for example. In a larger application, this becomes
particularly important.
The library therefore allows effects in general to be *labelled* so
that they can be referred to explicitly by a particular name. This
allows multiple effects of the same type to be included. We can count
leaves and update the tag separately, by labelling them as follows:
.. code-block:: idris
treeTagAux : BTree a
-> {['Tag ::: STATE Int,
'Leaves ::: STATE Int]} Eff (BTree (Int, a))
The ``:::`` operator allows an arbitrary label to be given to an
effect. This label can be any type—it is simply used to identify an
effect uniquely. Here, we have used a symbol type. In general
``name`` introduces a new symbol, the only purpose of which is to
disambiguate values [2]_.
When an effect is labelled, its operations are also labelled using the
``:-`` operator. In this way, we can say explicitly which state we
mean when using ``get`` and ``put``. The tree tagging program which
also counts leaves can be written as follows:
.. code-block:: idris
treeTagAux Leaf = do
'Leaves :- update (+1)
pure Leaf
treeTagAux (Node l x r) = do
l' <- treeTagAux l
i <- 'Tag :- get
'Tag :- put (i + 1)
r' <- treeTagAux r
pure (Node l' (i, x) r')
The ``update`` function here is a combination of ``get`` and ``put``,
applying a function to the current state.
.. code-block:: idris
update : (x -> x) -> { [STATE x] } Eff ()
Finally, our top level ``treeTag`` function now returns a pair of the
number of leaves, and the new tree. Resources for labelled effects are
intialised using the ``:=`` operator (reminisicent of assignment in an
imperative language):
.. code-block:: idris
treeTag : (i : Int) -> BTree a -> (Int, BTree (Int, a))
treeTag i x = runPureInit ['Tag := i, 'Leaves := 0]
(do x' <- treeTagAux x
leaves <- 'Leaves :- get
pure (leaves, x'))
To summarise, we have:
- ``:::`` to convert an effect to a labelled effect.
- ``:-`` to convert an effectful operation to a labelled effectful
operation.
- ``:=`` to initialise a resource for a labelled effect.
Or, more formally with their types (slightly simplified to account
only for the situation where available effects are not updated):
.. code-block:: idris
(:::) : lbl -> EFFECT -> EFFECT
(:-) : (l : lbl) -> { [x] } Eff a -> { [l ::: x] } Eff a
(:=) : (l : lbl) -> res -> LRes l res
Here, ``LRes`` is simply the resource type associated with a labelled
effect. Note that labels are polymorphic in the label type ``lbl``.
Hence, a label can be anything—a string, an integer, a type, etc.
``!``-notation
--------------
In many cases, using ``do``-notation can make programs unnecessarily
verbose, particularly in cases where the value bound is used once,
immediately. The following program returns the length of the
``String`` stored in the state, for example:
.. code-block:: idris
stateLength : { [STATE String] } Eff Nat
stateLength = do x <- get
pure (length x)
This seems unnecessarily verbose, and it would be nice to program in a
more direct style in these cases. provides ``!``-notation to help with
this. The above program can be written instead as:
.. code-block:: idris
stateLength : { [STATE String] } Eff Nat
stateLength = pure (length !get)
The notation ``!expr`` means that the expression ``expr`` should be
evaluated and then implicitly bound. Conceptually, we can think of
``!`` as being a prefix function with the following type:
.. code-block:: idris
(!) : { xs } Eff a -> a
Note, however, that it is not really a function, merely syntax! In
practice, a subexpression ``!expr`` will lift ``expr`` as high as
possible within its current scope, bind it to a fresh name ``x``, and
replace ``!expr`` with ``x``. Expressions are lifted depth first, left
to right. In practice, ``!``-notation allows us to program in a more
direct style, while still giving a notational clue as to which
expressions are effectful.
For example, the expression:
.. code-block:: idris
let y = 42 in f !(g !(print y) !x)
is lifted to:
.. code-block:: idris
let y = 42 in do y' <- print y
x' <- x
g' <- g y' x'
f g'
Syntactic Sugar and ``Eff``
---------------------------
By now, you may be wondering about the syntax we are using for
``Eff``, because it doesnt look like a normal type! (If not, you may
safely skip this section and return to it later.) In fact, the type of
``Eff`` is the following:
.. code-block:: idris
Eff : (x : Type) ->
List EFFECT -> (x -> List EFFECT) -> Type
This is more general than the types we have been writing so far. It is
parameterised over a result type ``x``, as we have already seen, but
also a ``List EFFECT`` and a function type ``x -> List EFFECT``.
These additional parameters are the list of *input* effects, and a
list of *output* effects, computed from the result of an effectful
operation. That is: running an effectful program can change the set
of effects available! This is a particularly powerful idea, and we
will see its consequences in more detail later. Some examples of
operations which can change the set of available effects are:
- Updating a state containing a dependent type (for example adding an
element to a vector).
- Opening a file for reading is an effect, but whether the file really
*is* open afterwards depends on whether the file was successfully
opened.
- Closing a file means that reading from the file should no longer be
possible.
While powerful, this can make uses of the ``Eff`` type hard to read.
Therefore, the library provides syntactic sugar which is translated
such that:
.. code-block:: idris
{ xs } Eff a
is expanded to
.. code-block:: idris
Eff a xs (\_ => xs)
i.e. the set of effects remains the same on output. This suffices for
the ``STATE`` example we have seen so far, and for many useful
side-effecting programs. We could also have written ``treeTagAux``
with the expanded type:
.. code-block:: idris
treeTagAux : BTree a ->
Eff (BTree (Int, a)) [STATE Int] (\x => [STATE Int])
Later, we will see programs which update effects:
.. code-block:: idris
{ xs ==> xs' } Eff a
which is expanded to
.. code-block:: idris
Eff a xs (\_ => xs')
i.e. the set of effects is updated to ``xs`` (think of a transition
in a state machine). There is, for example, a version of ``put`` which
updates the type of the state:
.. code-block:: idris
putM : y -> { [STATE x] ==> [STATE y] } Eff ()
Also, we have:
.. code-block:: idris
{ xs ==> {res} xs' } Eff a
which is expanded to
.. code-block:: idris
Eff a xs (\res => xs')
i.e. the set of effects is updated according to the result of the
operation ``res``.
.. [1] The earlier paper [3]_ describes the essential implementation
details, although the library presented there is an earlier version
which is less powerful than that presented in this tutorial.
.. [2] In practice, ``name`` simply introduces a new empty type
.. [3] Edwin Brady. 2013. Programming and reasoning with algebraic
effects and dependent types. SIGPLAN Not. 48, 9 (September
2013), 133-144. DOI=10.1145/2544174.2500581
http://doi.acm.org/10.1145/2544174.2500581
.. |image| image:: ../image/effects-tree.png
:width: 500px

153
docs/effects/summary.rst Normal file
View File

@ -0,0 +1,153 @@
.. _sect-appendix:
===============
Effects Summary
===============
This appendix gives interfaces for the core effects provided by the
library.
EXCEPTION
---------
.. code-block:: idris
module Effect.Exception
import Effects
import System
import Control.IOExcept
EXCEPTION : Type -> EFFECT
raise : a -> { [EXCEPTION a ] } Eff m b
instance Handler (Exception a) Maybe
instance Handler (Exception a) List
instance Handler (Exception a) (Either a)
instance Handler (Exception a) (IOExcept a)
instance Show a => Handler (Exception a) IO
FILE\_IO
--------
.. code-block:: idris
module Effect.File
import Effects
import Control.IOExcept
FILE_IO : Type -> EFFECT
data OpenFile : Mode -> Type
open : Handler FileIO e => String -> (m : Mode) ->
{ [FILE_IO ()] ==>
{ok} [FILE_IO (if ok then OpenFile m else ())] } Eff e Bool
close : Handler FileIO e =>
{ [FILE_IO (OpenFile m)] ==> [FILE_IO ()] } Eff e ()
readLine : Handler FileIO e =>
{ [FILE_IO (OpenFile Read)] } Eff e String
writeLine : Handler FileIO e => String ->
{ [FILE_IO (OpenFile Write)] } Eff e ()
eof : Handler FileIO e =>
{ [FILE_IO (OpenFile Read)] } Eff e Bool
instance Handler FileIO IO
RND
---
.. code-block:: idris
module Effect.Random
import Effects
import Data.Vect
import Data.Fin
RND : EFFECT
srand : Integer -> { [RND] } Eff m ()
rndInt : Integer -> Integer -> { [RND] } Eff m Integer
rndFin : (k : Nat) -> { [RND] } Eff m (Fin (S k))
instance Handler Random m
SELECT
------
.. code-block:: idris
module Effect.Select
import Effects
SELECT : EFFECT
select : List a -> { [SELECT] } Eff m a
instance Handler Selection Maybe
instance Handler Selection List
STATE
-----
.. code-block:: idris
module Effect.State
import Effects
STATE : Type -> EFFECT
get : { [STATE x] } Eff m x
put : x -> { [STATE x] } Eff m ()
putM : y -> { [STATE x] ==> [STATE y] } Eff m ()
update : (x -> x) -> { [STATE x] } Eff m ()
instance Handler State m
STDIO
-----
.. code-block:: idris
module Effect.StdIO
import Effects
import Control.IOExcept
STDIO : EFFECT
putChar : Handler StdIO m => Char -> { [STDIO] } Eff m ()
putStr : Handler StdIO m => String -> { [STDIO] } Eff m ()
putStrLn : Handler StdIO m => String -> { [STDIO] } Eff m ()
getStr : Handler StdIO m => { [STDIO] } Eff m String
getChar : Handler StdIO m => { [STDIO] } Eff m Char
instance Handler StdIO IO
instance Handler StdIO (IOExcept a)
SYSTEM
------
.. code-block:: idris
module Effect.System
import Effects
import System
import Control.IOExcept
SYSTEM : EFFECT
getArgs : Handler System e => { [SYSTEM] } Eff e (List String)
time : Handler System e => { [SYSTEM] } Eff e Int
getEnv : Handler System e => String -> { [SYSTEM] } Eff e (Maybe String)
instance Handler System IO
instance Handler System (IOExcept a)

130
docs/faq/faq.rst Normal file
View File

@ -0,0 +1,130 @@
==========================
Frequently Asked Questions
==========================
What are the differences between Agda and Idris?
------------------------------------------------
The main difference is that Idris has been designed from the start to support
verified systems programming through easy interoperability with C and high
level language constructs to support domain specific language implementation.
Idris emphasises general-purpose programming, rather than theorem proving, and
as such includes higher level programming constructs such as type classes and
do notation. Idris also supports tactic based theorem proving, and has a
lightweight Hugs/GHCI style interface.
Is Idris production ready?
--------------------------
Idris is primarily a research tool for exploring the possibilities of software
development with dependent types, meaning that the primary goal is not (yet) to
make a system which could be used in production. As such, there are a few rough
corners, and lots of missing libraries. Nobody is working on Idris full time,
and we don't have the resources at the moment to polish the system on our own.
Therefore, we don't recommend building your business around it!
Having said that, contributions which help towards making Idris suitable
for use in production would be very welcome - this includes (but is not
limited to) extra library support, polishing the run-time system (and ensuring
it is robust), providing and maintaining a JVM back end, etc.
Why does Idris use eager evaluation rather than lazy?
-----------------------------------------------------
Idris uses eager evaluation for more predictable performance, in particular
because one of the longer term goals is to be able to write efficient and
verified low level code such as device drivers and network infrastructure.
Furthermore, the Idris type system allows us to state precisely the type
of each value, and therefore the run-time form of each value. In a lazy
language, consider a value of type ``Int``:
.. code-block:: idris
thing : Int
What is the representation of ``thing`` at run-time? Is it a bit pattern
representing an integer, or is it a pointer to some code which will compute
an integer? In Idris, we have decided that we would like to make this
distinction precise, in the type:
.. code-block:: idris
thing_val : Int
thing_comp : Lazy Int
Here, it is clear from the type that ``thing_val`` is guaranteed to be a
concrete ``Int``, whereas ``thing_comp`` is a computation which will produce an
``Int``.
How can I make lazy control structures?
---------------------------------------
You can make control structures using the special Lazy type. For example,
``if...then...else`` is defined as follows in the library:
.. code-block:: idris
boolElim : Bool -> (t : Lazy a) -> (e : Lazy a) -> a
boolElim True t e = t
boolElim False t e = e
syntax if [test] then [t] else [e] = boolElim test t e
The type ``Lazy a`` for ``t`` and ``f`` indicates that those arguments will
only be evaluated if they are used, that is, they are evaluated lazily.
Evaluation at the REPL doesn't behave as I expect. What's going on?
-------------------------------------------------------------------
Being a fully dependently typed language, Idris has two phases where it
evaluates things, compile-time and run-time. At compile-time it will only
evaluate things which it knows to be total (i.e. terminating and covering all
possible inputs) in order to keep type checking decidable. The compile-time
evaluator is part of the Idris kernel, and is implemented in Haskell using a
HOAS (higher order abstract syntax) style representation of values. Since
everything is known to have a normal form here, the evaluation strategy doesn't
actually matter because either way it will get the same answer, and in practice
it will do whatever the Haskell run-time system chooses to do.
The REPL, for convenience, uses the compile-time notion of evaluation. As well
as being easier to implement (because we have the evaluator available) this can
be very useful to show how terms evaluate in the type checker. So you can see
the difference between:
.. code-block:: idris
Idris> \n, m => (S n) + m
\n => \m => S (plus n m) : Nat -> Nat -> Nat
Idris> \n, m => n + (S m)
\n => \m => plus n (S m) : Nat -> Nat -> Nat
When will Idris be self-hosting?
--------------------------------
Its not a priority, though not a bad idea in the long run. It would be a
worthwhile effort in the short term to implement libraries to support
self-hosting, such as a good parsing library.
Does Idris have Universe Polymorphism? What is the type of ``Type``?
--------------------------------------------------------------------
Rather than Universe polymorphism, Idris has a cumulative hierarchy of
universes; ``Type : Type 1``, ``Type 1 : Type 2``, etc.
Cumulativity means that if ``x : Type n`` then also ``x : Type m``,
provided that ``n <= m``.
What does the name Idris mean?
--------------------------------
British people of a certain age may be familiar with this
`singing dragon <https://www.youtube.com/watch?v=G5ZMNyscPcg>`_. If
that doesnt help, maybe you can invent a suitable acronym :-) .
Where can I find more answers?
------------------------------
There is an `Unofficial FAQ
<https://github.com/idris-lang/Idris-dev/wiki/Unofficial-FAQ>`_ on the wiki on
github which answers more technical questions and may be updated more often.

19
docs/faq/index.rst Normal file
View File

@ -0,0 +1,19 @@
.. _faq-index:
##########################
Frequently Asked Questions
##########################
.. note::
The documentation for Idris has been published under the Creative
Commons CC0 License. As such to the extent possible under law, *The
Idris Community* has waived all copyright and related or neighboring
rights to Documentation for Idris.
More information concerning the CC0 can be found online at: http://creativecommons.org/publicdomain/zero/1.0/
.. toctree::
:maxdepth: 1
faq

21
docs/guides/index.rst Normal file
View File

@ -0,0 +1,21 @@
.. _guides-index:
################################
Tutorials on the Idris Language
################################
Tutorials submitted by community members.
.. note::
The documentation for Idris has been published under the Creative
Commons CC0 License. As such to the extent possible under law, *The
Idris Community* has waived all copyright and related or neighboring
rights to Documentation for Idris.
More information concerning the CC0 can be found online at: http://creativecommons.org/publicdomain/zero/1.0/
.. toctree::
:maxdepth: 1
type-providers-ffi

View File

@ -0,0 +1,290 @@
Type Providers in Idris
=======================
`Type providers in Idris
<http://www.itu.dk/people/drc/pubs/dependent-type-providers.pdf>`__
are simple enough, but there are a few caveats to using them that it
would be worthwhile to go through the basic steps. We also go over
foreign functions, because these will often be used with type
providers.
The use case
------------
First, let's talk about *why* we might want type providers. There are
a number of reasons to use them and there are other examples available
around the net, but in this tutorial we'll be using them to port C's
``struct stat`` to Idris.
Why do we need type providers? Well, Idris's FFI needs to know the
types of the things it passes to and from C, but the fields of a
``struct stat`` are implementation-dependent types that cannot be
relied upon. We don't just want to hard-code these types into our
program... so we'll use a type provider to find them at compile time!
A simple example
----------------
First, let's go over a basic usage of type providers, because foreign
functions can be confusing but it's important to remember that
providers themselves are simple.
A type provider is simply an IO action that returns a value of this
type:
.. code-block:: idris
data Provider a = Provide a | Error String
Looks familiar? ``Provider`` is just ``Either a String``, given a
slightly more descriptive name.
Remember though, type providers we use in our program must be IO
actions. Let's write a simple one now:
.. code-block:: idris
module Provider
-- Asks nicely for the user to supply the size of C's size_t type on this
-- machine
getSizeT : IO (Provider Int)
getSizeT = do
putStrLn "I'm sorry, I don't know how big size_t is. Can you tell me, in bytes?"
resp <- getLine
case readInt resp of
Just sizeTSize => return (Provide sizeTSize)
Nothing => return (Error "I'm sorry, I don't understand.")
-- the readInt function is left as an exercise
We assume that whoever's compiling the library knows the size of
``size_t``, so we'll just ask them! (Don't worry, we'll get it
ourselves later.) Then, if their response can be converted to an
integer, we present ``Provide sizeTSize`` as the result of our IO
action; or if it can't, we signal a failure. (This will then become a
compile-time error.)
Now we can use this IO action as a type provider:
.. code-block:: idris
module Main
-- to gain access to the IO action we're using as a provider
import Provider
-- TypeProviders is an extension, so we'll enable it
%language TypeProviders
-- And finally, use the provider!
-- Note that the parentheses are mandatory.
%provide (sizeTSize : Int) with getSizeT
-- From now on it's just a normal program where `sizeTSize` is available
-- as a top-level constant
main : IO ()
main = do
putStr "Look! I figured out how big size_t is! It's "
putStr (show sizeTSize)
putStr " bytes!"
Yay! We... asked the user something at compile time? That's not very
good, actually. Our library is going to be difficult to compile! This
is hardly a step up from having them edit in the size of ``size_t``
themselves!
Don't worry, there's a better way.
Foreign Functions
-----------------
It's actually pretty easy to write a C function that figures out the
size of ``size_t``:
.. code:: c
int sizeof_size_t() { return sizeof(size_t); }
(Why an int and not a ``size_t``? The FFI needs to know how to receive
the return value of this function and translate it into an Idris
value. If we knew how to do this for values of C type ``size_t``, we
wouldn't need to write this function at all! If we really wanted to be
safe from overflow, we could use an array of multiple integers, but
the SIZE of ``size_t`` is never going to be a 65535 byte integer.)
So now we can get the size of ``size_t`` as long as we're in C code.
We'd like to be able to use this from Idris. Can we do this? It turns
out we can.
mkForeign
~~~~~~~~~
With mkForeign, we can turn a C function into an IO action. It works
like this:
.. code-block:: idris
getSizeT : IO Int
getSizeT = mkForeign (FFun "sizeof_size_t" [] FInt)
Pretty simple. ``mkForeign`` takes a specification of what function it
needs to call, and we construct this specification with ``FFun``. And
``FFun`` just takes a name, a list of argument types (we have none),
and a return type.
One thing you might want to note: the return type we've specified is
``FInt``, not ``Int``. That's because ``Int`` is an idris type and C
functions don't return idris types. ``FInt`` is not an idris type, but
is just the representation of the type of a C int. It tells the
compiler "Treat the return value of this C function like it's a C int,
and when you pass it back into Idris, convert it to an Idris int."
Caveats of mkForeign
^^^^^^^^^^^^^^^^^^^^
First and foremost: ``mkForeign`` is not actually a function. It is
treated specially by the compiler, and there are certain rules you
need to follow when using it.
- Rule 1: the name string must be a literal or constant
This does not work:
.. code-block:: idris
intIntToInt : String -> Int -> Int -> IO Int
intIntToInt name = mkForeign (FFun name [FInt, FInt] FInt)
You'll just have to bite the bullet and write out the whole
``mkForeign`` and ``FFun`` expression each time.
- Rule 2: the "call" to ``mkForeign`` must be fully applied
This just means that every argument appearing in the list of argument
types must be applied wherever you write ``mkForeign``. The arguments
don't have to be literals or even known at compile time; they just
have to be there. For example, if we have ``strlen : String -> IO
Int``, then this is fine:
.. code-block:: idris
strlen str = mkForeign (FFun "strlen" [FString] FInt) str
but this is not fine:
.. code-block:: idris
strlen = mkForeign (FFun "strlen" [FString] FInt)
Note that this only applies to places where you literally typed
``mkForeign``. Once you've defined it, ``strlen`` is just a normal
function returning an IO action, and it doesn't need to be fully
applied. This is okay:
.. code-block:: idris
lengths : IO [Int]
lengths = mapM strlen listOfStrings
Running foreign functions
~~~~~~~~~~~~~~~~~~~~~~~~~
This is all well and good for writing code that will typecheck. To
actually run the code, we'll need to do just a bit more work. Exactly
what we need to do depends on whether we want to interpret or compile
our code.
In the interpreter
^^^^^^^^^^^^^^^^^^
If we want to call our foreign functions from interpreted code (such
as the REPL or a type provider), we need to dynamically link a library
containing the symbols we need. This is pretty easy to do with the
``%dynamic`` directive:
.. code-block:: idris
%dynamic "./filename.so"
Note that the leading "./" is important: currently, the string you
provide is interpreted as by ``dlopen()``, which on Unix does not search
in the current directory by default. If you use the "./", the library
will be searched for in the directory from which you run idris (*not*
the location of the file you're running!). Of course, if you're using
functions from an installed library rather than something you wrote
yourself, the "./" is not necessary.
In an executable
^^^^^^^^^^^^^^^^
If we want to run our code from an executable, we can statically link
instead. We'll use the ``%include`` and ``%link`` directives:
.. code-block:: idris
%include C "filename.h"
%link C "filename.o"
Note the extra argument to the directive! We specify that we're
linking a C header and library. Also, unlike ``%dynamic``, these
directives search in the current directory by default. (That is, the
directory from which we run idris.)
Putting it all together
-----------------------
So, at the beginning of this article I said we'd use type providers to
port ``struct stat`` to Idris. The relevant part is just translating
all the mysterious typedef'd C types into Idris types, and that's what
we'll do here.
First, let's write a C file containing functions that we'll bind to.
.. code-block:: c
/* stattypes.c */
int sizeof_dev_t() { return sizeof(dev_t); }
int sizeof_ino_t() { return sizeof(ino_t); }
/* lots more functions like this */
Next, an Idris file to define our providers:
.. code-block:: idris
-- Providers.idr
module Providers
%dynamic "./stattypes.so"
sizeOfDevT : IO Int
sizeOfDevT = mkForeign (FFun "sizeof_dev_t" [] FInt)
{- lots of similar functions -}
-- now we have an integer, but we want a Provider FTy
-- since our sizeOf* functions are ordinary IO actions, we
-- can just map over them.
bytesToType : Int -> Provider FTy
bytesToType 1 = Provide (FIntT IT8) -- "8 bit foreign integer"
bytesToType 2 = Provide (FIntT IT16)
bytesToType 4 = Provide (FIntT IT32)
bytesToType 8 = Provide (FIntT IT64)
bytesToType _ = Error "Unrecognized integral type."
getDevT : IO (Provider FTy)
getDevT = map bytesToType sizeOfDevT
{- lots of similar functions -}
Finally, we'll write one more idris file where we use the type
providers:
.. code-block:: idris
-- Main.idr
module Main
import Providers
%language TypeProviders
%provide (FDevT : FTy) with getDevT
-- interpFTy translates a foreign type to the corresponding idris type
DevT : Type
DevT = interpFTy FDevT -- on most systems, DevT = Bits64
-- We can now use DevT in our program and FDevT in our FFun expressions!

BIN
docs/image/effects-tree.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

115
docs/index.rst Normal file
View File

@ -0,0 +1,115 @@
.. Idris Manual documentation master file, created by
sphinx-quickstart on Sat Feb 28 20:41:47 2015.
You can adapt this file completely to your liking, but it should at least
contain the root `toctree` directive.
Documentation for the Idris Language
====================================
.. note::
The documentation for Idris has been published under the Creative
Commons CC0 License. As such to the extent possible under law, *The
Idris Community* has waived all copyright and related or neighboring
rights to Documentation for Idris.
More information concerning the CC0 can be found online at: http://creativecommons.org/publicdomain/zero/1.0/
* :ref:`tutorial-index`
* :ref:`eff-tutorial-index`
* :ref:`faq-index`
* :ref:`proofs-index`
* :ref:`reference-index`
* :ref:`guides-index`
.. _tutorial:
###################
The Idris Tutorial
###################
.. toctree::
:maxdepth: 1
tutorial/introduction
tutorial/starting
tutorial/typesfuns
tutorial/classes
tutorial/modules
tutorial/packages
tutorial/interp
tutorial/views
tutorial/theorems
tutorial/provisional
tutorial/interactive
tutorial/syntax
tutorial/miscellany
tutorial/conclusions
.. _effects:
##########################
Frequently Asked Questions
##########################
.. toctree::
:maxdepth: 1
faq/faq
.. _effects:
################
Learning Effects
################
.. toctree::
:maxdepth: 1
effects/introduction
effects/state
effects/simpleeff
effects/depeff
effects/impleff
effects/hangman
effects/conclusions
effects/summary
.. _proofs:
###############
Theorem Proving
###############
.. toctree::
:maxdepth: 1
proofs/pluscomm
proofs/inductive
proofs/patterns
.. _reference:
##################
Language Reference
##################
.. toctree::
:maxdepth: 1
reference/documenting
reference/uniqueness-types
reference/ffi
reference/erasure
.. _guides:
############
Short Guides
############
.. toctree::
:maxdepth: 1
guides/type-providers-ffi

View File

@ -0,0 +1,15 @@
$ idris hello.idr
____ __ _
/ _/___/ /____(_)____
/ // __ / ___/ / ___/ Version 0.9.17
_/ // /_/ / / / (__ ) http://www.idris-lang.org/
/___/\__,_/_/ /_/____/ Type :? for help
Type checking ./hello.idr
*hello> :t main
Main.main : IO ()
*hello> :c hello
*hello> :q
Bye bye
$ ./hello
Hello world

View File

@ -0,0 +1,12 @@
$ idris interp.idr
____ __ _
/ _/___/ /____(_)____
/ // __ / ___/ / ___/ Version 0.9.17
_/ // /_/ / / / (__ ) http://www.idris-lang.org/
/___/\__,_/_/ /_/____/ Type :? for help
Type checking ./interp.idr
*interp> :exec
Enter a number: 6
720
*interp>

View File

@ -0,0 +1,8 @@
$ idris
____ __ _
/ _/___/ /____(_)____
/ // __ / ___/ / ___/ Version 0.9.17
_/ // /_/ / / / (__ ) http://www.idris-lang.org/
/___/\__,_/_/ /_/____/ Type :? for help
Idris>

263
docs/make.bat Normal file
View File

@ -0,0 +1,263 @@
@ECHO OFF
REM Command file for Sphinx documentation
if "%SPHINXBUILD%" == "" (
set SPHINXBUILD=sphinx-build
)
set BUILDDIR=_build
set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% .
set I18NSPHINXOPTS=%SPHINXOPTS% .
if NOT "%PAPER%" == "" (
set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS%
set I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS%
)
if "%1" == "" goto help
if "%1" == "help" (
:help
echo.Please use `make ^<target^>` where ^<target^> is one of
echo. html to make standalone HTML files
echo. dirhtml to make HTML files named index.html in directories
echo. singlehtml to make a single large HTML file
echo. pickle to make pickle files
echo. json to make JSON files
echo. htmlhelp to make HTML files and a HTML help project
echo. qthelp to make HTML files and a qthelp project
echo. devhelp to make HTML files and a Devhelp project
echo. epub to make an epub
echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter
echo. text to make text files
echo. man to make manual pages
echo. texinfo to make Texinfo files
echo. gettext to make PO message catalogs
echo. changes to make an overview over all changed/added/deprecated items
echo. xml to make Docutils-native XML files
echo. pseudoxml to make pseudoxml-XML files for display purposes
echo. linkcheck to check all external links for integrity
echo. doctest to run all doctests embedded in the documentation if enabled
echo. coverage to run coverage check of the documentation if enabled
goto end
)
if "%1" == "clean" (
for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i
del /q /s %BUILDDIR%\*
goto end
)
REM Check if sphinx-build is available and fallback to Python version if any
%SPHINXBUILD% 2> nul
if errorlevel 9009 goto sphinx_python
goto sphinx_ok
:sphinx_python
set SPHINXBUILD=python -m sphinx.__init__
%SPHINXBUILD% 2> nul
if errorlevel 9009 (
echo.
echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
echo.installed, then set the SPHINXBUILD environment variable to point
echo.to the full path of the 'sphinx-build' executable. Alternatively you
echo.may add the Sphinx directory to PATH.
echo.
echo.If you don't have Sphinx installed, grab it from
echo.http://sphinx-doc.org/
exit /b 1
)
:sphinx_ok
if "%1" == "html" (
%SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html
if errorlevel 1 exit /b 1
echo.
echo.Build finished. The HTML pages are in %BUILDDIR%/html.
goto end
)
if "%1" == "dirhtml" (
%SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml
if errorlevel 1 exit /b 1
echo.
echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml.
goto end
)
if "%1" == "singlehtml" (
%SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml
if errorlevel 1 exit /b 1
echo.
echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml.
goto end
)
if "%1" == "pickle" (
%SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle
if errorlevel 1 exit /b 1
echo.
echo.Build finished; now you can process the pickle files.
goto end
)
if "%1" == "json" (
%SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json
if errorlevel 1 exit /b 1
echo.
echo.Build finished; now you can process the JSON files.
goto end
)
if "%1" == "htmlhelp" (
%SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp
if errorlevel 1 exit /b 1
echo.
echo.Build finished; now you can run HTML Help Workshop with the ^
.hhp project file in %BUILDDIR%/htmlhelp.
goto end
)
if "%1" == "qthelp" (
%SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp
if errorlevel 1 exit /b 1
echo.
echo.Build finished; now you can run "qcollectiongenerator" with the ^
.qhcp project file in %BUILDDIR%/qthelp, like this:
echo.^> qcollectiongenerator %BUILDDIR%\qthelp\IdrisManual.qhcp
echo.To view the help file:
echo.^> assistant -collectionFile %BUILDDIR%\qthelp\IdrisManual.ghc
goto end
)
if "%1" == "devhelp" (
%SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp
if errorlevel 1 exit /b 1
echo.
echo.Build finished.
goto end
)
if "%1" == "epub" (
%SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub
if errorlevel 1 exit /b 1
echo.
echo.Build finished. The epub file is in %BUILDDIR%/epub.
goto end
)
if "%1" == "latex" (
%SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex
if errorlevel 1 exit /b 1
echo.
echo.Build finished; the LaTeX files are in %BUILDDIR%/latex.
goto end
)
if "%1" == "latexpdf" (
%SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex
cd %BUILDDIR%/latex
make all-pdf
cd %~dp0
echo.
echo.Build finished; the PDF files are in %BUILDDIR%/latex.
goto end
)
if "%1" == "latexpdfja" (
%SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex
cd %BUILDDIR%/latex
make all-pdf-ja
cd %~dp0
echo.
echo.Build finished; the PDF files are in %BUILDDIR%/latex.
goto end
)
if "%1" == "text" (
%SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text
if errorlevel 1 exit /b 1
echo.
echo.Build finished. The text files are in %BUILDDIR%/text.
goto end
)
if "%1" == "man" (
%SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man
if errorlevel 1 exit /b 1
echo.
echo.Build finished. The manual pages are in %BUILDDIR%/man.
goto end
)
if "%1" == "texinfo" (
%SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo
if errorlevel 1 exit /b 1
echo.
echo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo.
goto end
)
if "%1" == "gettext" (
%SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale
if errorlevel 1 exit /b 1
echo.
echo.Build finished. The message catalogs are in %BUILDDIR%/locale.
goto end
)
if "%1" == "changes" (
%SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes
if errorlevel 1 exit /b 1
echo.
echo.The overview file is in %BUILDDIR%/changes.
goto end
)
if "%1" == "linkcheck" (
%SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck
if errorlevel 1 exit /b 1
echo.
echo.Link check complete; look for any errors in the above output ^
or in %BUILDDIR%/linkcheck/output.txt.
goto end
)
if "%1" == "doctest" (
%SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest
if errorlevel 1 exit /b 1
echo.
echo.Testing of doctests in the sources finished, look at the ^
results in %BUILDDIR%/doctest/output.txt.
goto end
)
if "%1" == "coverage" (
%SPHINXBUILD% -b coverage %ALLSPHINXOPTS% %BUILDDIR%/coverage
if errorlevel 1 exit /b 1
echo.
echo.Testing of coverage in the sources finished, look at the ^
results in %BUILDDIR%/coverage/python.txt.
goto end
)
if "%1" == "xml" (
%SPHINXBUILD% -b xml %ALLSPHINXOPTS% %BUILDDIR%/xml
if errorlevel 1 exit /b 1
echo.
echo.Build finished. The XML files are in %BUILDDIR%/xml.
goto end
)
if "%1" == "pseudoxml" (
%SPHINXBUILD% -b pseudoxml %ALLSPHINXOPTS% %BUILDDIR%/pseudoxml
if errorlevel 1 exit /b 1
echo.
echo.Build finished. The pseudo-XML files are in %BUILDDIR%/pseudoxml.
goto end
)
:end

24
docs/proofs/index.rst Normal file
View File

@ -0,0 +1,24 @@
.. _proofs-index:
###############
Theorem Proving
###############
A tutorial on theorem proving in Idris.
.. note::
The documentation for Idris has been published under the Creative
Commons CC0 License. As such to the extent possible under law, *The
Idris Community* has waived all copyright and related or neighboring
rights to Documentation for Idris.
More information concerning the CC0 can be found online at: http://creativecommons.org/publicdomain/zero/1.0/
.. toctree::
:maxdepth: 1
pluscomm
inductive
patterns

98
docs/proofs/inductive.rst Normal file
View File

@ -0,0 +1,98 @@
Inductive Proofs
================
Before embarking on proving ``plus_commutes`` in Idris itself, let us
consider the overall structure of a proof of some property of natural
numbers. Recall that they are defined recursively, as follows:
.. code-block:: idris
data Nat : Type where
Z : Nat
S : Nat -> Nat
A *total* function over natural numbers must both terminate, and cover
all possible inputs. Idris checks functions for totality by cheking that
all inputs are covered, and that all recursive calls are on
*structurally smaller* values (so recursion will always reach a base
case). Recalling ``plus``:
.. code-block:: idris
plus : Nat -> Nat -> Nat
plus Z m = m
plus (S k) m = S (plus k m)
This is total because it covers all possible inputs (the first argument
can only be ``Z`` or ``S k`` for some ``k``, and the second argument
``m`` covers all possible ``Nat``\ s) and in the recursive call, ``k``
is structurally smaller than ``S k`` so the first argument will always
reach the base case ``Z`` in any sequence of recursive calls.
In some sense, this resembles a mathematical proof by induction (and
this is no coincidence!). For some property ``P`` of a natural number
``x``, we can show that ``P`` holds for all ``x`` if:
- ``P`` holds for zero (the base case).
- Assuming that ``P`` holds for ``k``, we can show ``P`` also holds for
``S k`` (the inductive step).
In ``plus``, the property we are trying to show is somewhat trivial (for
all natural numbers ``x``, there is a ``Nat`` which need not have any
relation to ``x``). However, it still takes the form of a base case and
an inductive step. In the base case, we show that there is a ``Nat``
arising from ``plus n m`` when ``n = Z``, and in the inductive step we
show that there is a ``Nat`` arising when ``n = S k`` and we know we can
get a ``Nat`` inductively from ``plus k m``. We could even write a
function capturing all such inductive definitions:
.. code-block:: idris
nat_induction : (P : Nat -> Type) -> -- Property to show
(P Z) -> -- Base case
((k : Nat) -> P k -> P (S k)) -> -- Inductive step
(x : Nat) -> -- Show for all x
P x
nat_induction P p_Z p_S Z = p_Z
nat_induction P p_Z p_S (S k) = p_S k (nat_induction P p_Z p_S k)
Using ``nat_induction``, we can implement an equivalent inductive
version of ``plus``:
.. code-block:: idris
plus_ind : Nat -> Nat -> Nat
plus_ind n m
= nat_induction (\x => Nat)
m -- Base case, plus_ind Z m
(\k, k_rec => S k_rec) -- Inductive step plus_ind (S k) m
-- where k_rec = plus_ind k m
n
To prove that ``plus n m = plus m n`` for all natural numbers ``n`` and
``m``, we can also use induction. Either we can fix ``m`` and perform
induction on ``n``, or vice versa. We can sketch an outline of a proof;
performing induction on ``n``, we have:
- Property ``P`` is ``\x => plus x m = plus m x``.
- Show that ``P`` holds in the base case and inductive step:
- | Base case: ``P Z``, i.e.
| ``plus Z m = plus m Z``, which reduces to
| ``m = plus m Z`` due to the definition of ``plus``.
- | Inductive step: Inductively, we know that ``P k`` holds for a specific, fixed ``k``, i.e.
| ``plus k m = plus m k`` (the induction hypothesis). Given this, show ``P (S k)``, i.e.
| ``plus (S k) m = plus m (S k)``, which reduces to
| ``S (plus k m) = plus m (S k)``. From the induction hypothesis, we can rewrite this to
| ``S (plus m k) = plus m (S k)``.
To complete the proof we therefore need to show that ``m = plus m Z``
for all natural numbers ``m``, and that ``S (plus m k) = plus m (S k)``
for all natural numbers ``m`` and ``k``. Each of these can also be
proved by induction, this time on ``m``.
We are now ready to embark on a proof of commutativity of ``plus``
formally in Idris.

350
docs/proofs/patterns.rst Normal file
View File

@ -0,0 +1,350 @@
Pattern Matching Proofs
=======================
In this section, we will provide a proof of ``plus_commutes`` directly,
by writing a pattern matching definition. We will use interactive
editing features extensively, since it is significantly easier to
produce a proof when the machine can give the types of intermediate
values and construct components of the proof itself. The commands we
will use are summarised below. Where we refer to commands
directly, we will use the Vim version, but these commands have a direct
mapping to Emacs commands.
+---------------------+-----------------+---------------+--------------------------------------------------------------------------------------------+
|Command | Vim binding | Emacs binding | Explanation |
+---------------------+-----------------+---------------+--------------------------------------------------------------------------------------------+
| Check type | ``\t`` | ``C-c C-t`` | Show type of identifier or metavariable under the cursor. |
+---------------------+-----------------+---------------+--------------------------------------------------------------------------------------------+
| Proof search | ``\o`` | ``C-c C-a`` | Attempt to solve metavariable under the cursor by applying simple proof search. |
+---------------------+-----------------+---------------+--------------------------------------------------------------------------------------------+
| Make new definition | ``\d`` | ``C-c C-s`` | Add a template definition for the type defined under the cursor. |
+---------------------+-----------------+---------------+--------------------------------------------------------------------------------------------+
| Make lemma | ``\l`` | ``C-c C-e`` | Add a top level function with a type which solves the metavariable under the cursor. |
+---------------------+-----------------+---------------+--------------------------------------------------------------------------------------------+
| Split cases | ``\c`` | ``C-c C-c`` | Create new constructor patterns for each possible case of the variable under the cursor. |
+---------------------+-----------------+---------------+--------------------------------------------------------------------------------------------+
Creating a Definition
---------------------
To begin, create a file ``pluscomm.idr`` containing the following type
declaration:
.. code-block:: idris
plus_commutes : (n : Nat) -> (m : Nat) -> n + m = m + n
To create a template definition for the proof, press ``\d`` (or the
equivalent in your editor of choice) on the line with the type
declaration. You should see:
.. code-block:: idris
plus_commutes : (n : Nat) -> (m : Nat) -> n + m = m + n
plus_commutes n m = ?plus_commutes_rhs
To prove this by induction on ``n``, as we sketched in Section
[sect:induction], we begin with a case split on ``n`` (press
``\c`` with the cursor over the ``n`` in the definition.) You
should see:
.. code-block:: idris
plus_commutes : (n : Nat) -> (m : Nat) -> n + m = m + n
plus_commutes Z m = ?plus_commutes_rhs_1
plus_commutes (S k) m = ?plus_commutes_rhs_2
If we inspect the types of the newly created metavariables,
``plus_commutes_rhs_1`` and ``plus_commutes_rhs_2``, we see that the
type of each reflects that ``n`` has been refined to ``Z`` and ``S k``
in each respective case. Pressing ``\t`` over
``plus_commutes_rhs_1`` shows:
.. code-block:: idris
m : Nat
--------------------------------------
plus_commutes_rhs_1 : m = plus m 0
Note that ``Z`` renders as ``0`` because the pretty printer renders
natural numbers as integer literals for readability. Similarly, for
``plus_commutes_rhs_2``:
.. code-block:: idris
k : Nat
m : Nat
--------------------------------------
plus_commutes_rhs_2 : S (plus k m) = plus m (S k)
It is a good idea to give these slightly more meaningful names:
.. code-block:: idris
plus_commutes : (n : Nat) -> (m : Nat) -> n + m = m + n
plus_commutes Z m = ?plus_commutes_Z
plus_commutes (S k) m = ?plus_commutes_S
Base Case
---------
We can create a separate lemma for the base case interactively, by
pressing ``\l`` with the cursor over ``plus_commutes_Z``. This
yields:
.. code-block:: idris
plus_commutes_Z : m = plus m 0
plus_commutes : (n : Nat) -> (m : Nat) -> n + m = m + n
plus_commutes Z m = plus_commutes_Z
plus_commutes (S k) m = ?plus_commutes_S
That is, the metavariable has been filled with a call to a top level
function ``plus_commutes_Z``. The argument ``m`` has been made implicit
because it can be inferred from context when it is applied.
Unfortunately, we cannot prove this lemma directly, since ``plus`` is
defined by matching on its *first* argument, and here ``plus m 0`` has a
specific value for its *second argument* (in fact, the left hand side of
the equality has been reduced from ``plus 0 m``.) Again, we can prove
this by induction, this time on ``m``.
First, create a template definition with ``\d``:
.. code-block:: idris
plus_commutes_Z : m = plus m 0
plus_commutes_Z = ?plus_commutes_Z_rhs
Since we are going to write this by induction on ``m``, which is
implciit, we will need to bring ``m`` into scope manually:
.. code-block:: idris
plus_commutes_Z : m = plus m 0
plus_commutes_Z {m} = ?plus_commutes_Z_rhs
Now, case split on ``m`` with ``\c``:
.. code-block:: idris
plus_commutes_Z : m = plus m 0
plus_commutes_Z {m = Z} = ?plus_commutes_Z_rhs_1
plus_commutes_Z {m = (S k)} = ?plus_commutes_Z_rhs_2
Checking the type of ``plus_commutes_Z_rhs_1`` shows the following,
which is easily proved by reflection:
.. code-block:: idris
--------------------------------------
plus_commutes_Z_rhs_1 : 0 = 0
For such trivial proofs, we can let write the proof automatically by
pressing ``\o`` with the cursor over ``plus_commutes_Z_rhs_1``.
This yields:
.. code-block:: idris
plus_commutes_Z : m = plus m 0
plus_commutes_Z {m = Z} = Refl
plus_commutes_Z {m = (S k)} = ?plus_commutes_Z_rhs_2
For ``plus_commutes_Z_rhs_2``, we are not so lucky:
.. code-block:: idris
k : Nat
--------------------------------------
plus_commutes_Z_rhs_2 : S k = S (plus k 0)
Inductively, we should know that ``k = plus k 0``, and we can get access
to this inductive hypothesis by making a recursive call on k, as
follows:
.. code-block:: idris
plus_commutes_Z : m = plus m 0
plus_commutes_Z {m = Z} = Refl
plus_commutes_Z {m = (S k)} = let rec = plus_commutes_Z {m=k} in
?plus_commutes_Z_rhs_2
For ``plus_commutes_Z_rhs_2``, we now see:
.. code-block:: idris
k : Nat
rec : k = plus k (fromInteger 0)
--------------------------------------
plus_commutes_Z_rhs_2 : S k = S (plus k 0)
Again, the ``fromInteger 0`` is merely due to ``Nat`` being an instance
of the ``Num`` typeclass. So we know that ``k = plus k 0``, but how do
we use this to update the goal to ``S k = S k``?
To achieve this, Idris provides a ``replace`` function as part of the
prelude:
.. code-block:: idris
*pluscomm> :t replace
replace : (x = y) -> P x -> P y
Given a proof that ``x = y``, and a property ``P`` which holds for
``x``, we can get a proof of the same property for ``y``, because we
know ``x`` and ``y`` must be the same. In practice, this function can be
a little tricky to use because in general the implicit argument ``P``
can be hard to infer by unification, so Idris provides a high level
syntax which calculates the property and applies ``replace``:
.. code-block:: idris
rewrite prf in expr
If we have ``prf : x = y``, and the required type for ``expr`` is some
property of ``x``, the ``rewrite ... in`` syntax will search for ``x``
in the required type of ``expr`` and replace it with ``y``. Concretely,
in our example, we can say:
.. code-block:: idris
plus_commutes_Z {m = (S k)} = let rec = plus_commutes_Z {m=k} in
rewrite rec in ?plus_commutes_Z_rhs_2
Checking the type of ``plus_commutes_Z_rhs_2`` now gives:
.. code-block:: idris
k : Nat
rec : k = plus k (fromInteger 0)
_rewrite_rule : plus k 0 = k
--------------------------------------
plus_commutes_Z_rhs_2 : S (plus k 0) = S (plus k 0)
Using the rewrite rule ``rec`` (which we can see in the context here as
``_rewrite_rule``\ [1]_, the goal type has been updated with ``k``
replaced by ``plus k 0``.
Alternatively, we could have applied the rewrite in the other direction
using the ``sym`` function:
.. code-block:: idris
*pluscomm> :t sym
sym : (l = r) -> r = l
.. code-block:: idris
plus_commutes_Z {m = (S k)} = let rec = plus_commutes_Z {m=k} in
rewrite sym rec in ?plus_commutes_Z_rhs_2
In this case, inspecting the type of the hole gives:
.. code-block:: idris
k : Nat
rec : k = plus k (fromInteger 0)
_rewrite_rule : k = plus k 0
--------------------------------------
plus_commutes_Z_rhs_2 : S k = S k
Either way, we can use proof search (``\o``) to complete the
proof, giving:
.. code-block:: idris
plus_commutes_Z : m = plus m 0
plus_commutes_Z {m = Z} = Refl
plus_commutes_Z {m = (S k)} = let rec = plus_commutes_Z {m=k} in
rewrite rec in Refl
The base case is now complete.
Inductive Step
--------------
Our main theorem, ``plus_commutes`` should currently be in the following
state:
.. code-block:: idris
plus_commutes : (n : Nat) -> (m : Nat) -> n + m = m + n
plus_commutes Z m = plus_commutes_Z
plus_commutes (S k) m = ?plus_commutes_S
Looking again at the type of ``plus_commutes_S``, we have:
.. code-block:: idris
k : Nat
m : Nat
--------------------------------------
plus_commutes_S : S (plus k m) = plus m (S k)
Conveniently, by induction we can immediately tell that
``plus k m = plus m k``, so let us rewrite directly by making a
recursive call to ``plus_commutes``. We add this directly, by hand, as
follows:
.. code-block:: idris
plus_commutes : (n : Nat) -> (m : Nat) -> n + m = m + n
plus_commutes Z m = plus_commutes_Z
plus_commutes (S k) m = rewrite plus_commutes k m in ?plus_commutes_S
Checking the type of ``plus_commutes_S`` now gives:
.. code-block:: idris
k : Nat
m : Nat
_rewrite_rule : plus m k = plus k m
--------------------------------------
plus_commutes_S : S (plus m k) = plus m (S k)
The good news is that ``m`` and ``k`` now appear in the correct order.
However, we still have to show that the successor symbol ``S`` can be
moved to the front in the right hand side of this equality. This
remaining lemma takes a similar form to the ``plus_commutes_Z``; we
begin by making a new top level lemma with ``\l``. This gives:
.. code-block:: idris
plus_commutes_S : (k : Nat) -> (m : Nat) -> S (plus m k) = plus m (S k)
Unlike the previous case, ``k`` and ``m`` are not made implicit because
we cannot in general infer arguments to a function from its result.
Again, we make a template definition with ``\d``:
.. code-block:: idris
plus_commutes_S : (k : Nat) -> (m : Nat) -> S (plus m k) = plus m (S k)
plus_commutes_S k m = ?plus_commutes_S_rhs
Again, this is defined by induction over ``m``, since ``plus`` is
defined by matching on its first argument. The complete definition is:
.. code-block:: idris
total
plus_commutes_S : (k : Nat) -> (m : Nat) -> S (plus m k) = plus m (S k)
plus_commutes_S k Z = Refl
plus_commutes_S k (S j) = rewrite plus_commutes_S k j in Refl
All metavariables have now been solved,
The ``total`` annotation means that we require the final function to
pass the totality checker; i.e. it will terminate on all possible
well-typed inputs. This is important for proofs, since it provides a
guarantee that the proof is valid in *all* cases, not just those for
which it happens to be well-defined.
Now that ``plus_commutes`` has a ``total`` annotation, we have completed the
proof of commutativity of addition on natural numbers.
.. [1]
Note that the left and right hand sides of the equality have been
swapped, because ``replace`` takes a proof of ``x=y`` and the
property for ``x``, not ``y``.

188
docs/proofs/pluscomm.rst Normal file
View File

@ -0,0 +1,188 @@
Running example: Addition of Natural Numbers
============================================
Throughout this tutorial, we will be working with the following
function, defined in the Idris prelude, which defines addition on
natural numbers:
.. code-block:: idris
plus : Nat -> Nat -> Nat
plus Z m = m
plus (S k) m = S (plus k m)
It is defined by the above equations, meaning that we have for free the
properties that adding ``m`` to zero always results in ``m``, and that
adding ``m`` to any non-zero number ``S k`` always results in
``S (plus k m)``. We can see this by evaluation at the Idris REPL (i.e.
the prompt, the read-eval-print loop):
.. code-block:: idris
Idris> \m => plus Z m
\m => m : Nat -> Nat
Idris> \k,m => plus (S k) m
\k => \m => S (plus k m) : Nat -> Nat -> Nat
Note that unlike many other language REPLs, the Idris REPL performs
evaluation on *open* terms, meaning that it can reduce terms which
appear inside lambda bindings, like those above. Therefore, we can
introduce unknowns ``k`` and ``m`` as lambda bindings and see how
``plus`` reduces.
The ``plus`` function has a number of other useful properties, for
example:
- It is *commutative*, that is for all ``Nat`` inputs ``n`` and ``m``,
we know that ``plus n m = plus m n``.
- It is *associative*, that is for all ``Nat`` inputs ``n``, ``m`` and
``p``, we know that ``plus n (plus m p) = plus (plus m n) p``.
We can use these properties in an Idris program, but in order to do so
we must *prove* them.
Equality Proofs
---------------
Idris has a built-in propositional equality type, conceptually defined
as follows:
.. code-block:: idris
data (=) : a -> b -> Type where
Refl : x = x
Note that this must be built-in, rather than defined in the library,
because ``=`` is a reserved operator — you cannot define this directly
in your own code.
It is *propositional* equality, where the type states that any two
values in different types ``a`` and ``b`` may be proposed to be equal.
There is only one way to *prove* equality, however, which is by
reflexivity (``Refl``).
We have a *type* for propositional equality here, and correspondingly a
*program* inhabiting an instance of this type can be seen as a proof of
the corresponding proposition [1]_. So, trivially, we can prove that
``4`` equals ``4``:
.. code-block:: idris
four_eq : 4 = 4
four_eq = Refl
However, trying to prove that ``4 = 5`` results in failure:
.. code-block:: idris
four_eq_five : 4 = 5
four_eq_five = Refl
The type ``4 = 5`` is a perfectly valid type, but is uninhabited, so
when trying to type check this definition, Idris gives the following
error:
::
When elaborating right hand side of four_eq_five:
Can't unify
x = x (Type of Refl)
with
4 = 5 (Expected type)
Type checking equality proofs
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
An important step in type checking Idris programs is *unification*,
which attempts to resolve implicit arguments such as the implicit
argument ``x`` in ``Refl``. As far as our understanding of type checking
proofs is concerned, it suffices to know that unifying two terms
involves reducing both to normal form then trying to find an assignment
to implicit arguments which will make those normal forms equal.
When type checking ``Refl``, Idris requires that the type is of the form
``x = x``, as we see from the type of ``Refl``. In the case of
``four_eq_five``, Idris will try to unify the expected type ``4 = 5``
with the type of ``Refl``, ``x = x``, notice that a solution requires
that ``x`` be both ``4`` and ``5``, and therefore fail.
Since type checking involves reduction to normal form, we can write the
following equalities directly:
.. code-block:: idris
twoplustwo_eq_four : 2 + 2 = 4
twoplustwo_eq_four = Refl
plus_reduces_Z : (m : Nat) -> plus Z m = m
plus_reduces_Z m = Refl
plus_reduces_Sk : (k, m : Nat) -> plus (S k) m = S (plus k m)
plus_reduces_Sk k m = Refl
Heterogeneous Equality
----------------------
Equality in Idris is *heterogeneous*, meaning that we can even propose
equalities between values in different types:
.. code-block:: idris
idris_not_php : 2 = "2"
Obviously, in Idris the type ``2 = "2"`` is uninhabited, and one might
wonder why it is useful to be able to propose equalities between values
in different types. However, with dependent types, such equalities can
arise naturally. For example, if two vectors are equal, their lengths
must be equal:
.. code-block:: idris
vect_eq_length : (xs : Vect n a) -> (ys : Vect m a) ->
(xs = ys) -> n = m
In the above declaration, ``xs`` and ``ys`` have different types because
their lengths are different, but we would still like to draw a
conclusion about the lengths if they happen to be equal. We can define
``vect_eq_length`` as follows:
.. code-block:: idris
vect_eq_length xs xs Refl = Refl
By matching on ``Refl`` for the third argument, we know that the only
valid value for ``ys`` is ``xs``, because they must be equal, and
therefore their types must be equal, so the lengths must be equal.
Alternatively, we can put an underscore for the second ``xs``, since
there is only one value which will type check:
.. code-block:: idris
vect_eq_length xs _ Refl = Refl
Properties of ``plus``
----------------------
Using the ``(=)`` type, we can now state the properties of ``plus``
given above as Idris type declarations:
.. code-block:: idris
plus_commutes : (n, m : Nat) -> plus n m = plus m n
plus_assoc : (n, m, p : Nat) -> plus n (plus m p) = plus (plus n m) p
Both of these properties (and many others) are proved for natural number
addition in the Idris standard library, using ``(+)`` from the ``Num``
type class rather than using ``plus`` directly. They have the names
``plusCommutative`` and ``plusAssociative`` respectively.
In the remainder of this tutorial, we will explore several different
ways of proving ``plus_commutes`` (or, to put it another way, writing
the function.) We will also discuss how to use such equality proofs, and
see where the need for them arises in practice.
.. [1]
This is known as the Curry-Howard correspondence 

View File

@ -0,0 +1,135 @@
.. _sect-documenting:
======================
Documenting Idris Code
======================
Idris documentation comes in two major forms: comments, which exist
for a readers edification and are ignored by the compiler, and inline
API documentation, which the compiler parses and stores for future
reference. To consult the documentation for a declaration ``f``, write
``:doc f`` at the REPL or use the appropriate command in your editor
(``C-c C-d`` in Emacs, in Vim).
Comments
--------
Use comments to explain why code is written the way that it
is. Idriss comment syntax is the same as that of Haskell: lines
beginning with ``--`` are comments, and regions bracketed by ``{-``
and ``-}`` are comments even if they extend across multiple
lines. These can be used to comment out lines of code or provide
simple documentation for the readers of Idris code.
Inline Documentation
--------------------
Idris also supports a comprehensive and rich inline syntax for Idris
code to be generated. This syntax also allows for named parameters and
variables within type signatures to be individually annotated using a
syntax similar to Javadoc parameter annotations.
Documentation always comes before the declaration being documented.
Inline documentation applies to either top-level declarations or to
constructors. Documentation for specific arguments to constructors, type
constructors, or functions can be associated with these arguments using
their names.
The inline documentation for a declaration is an unbroken string of
lines, each of which begins with ``|||`` (three pipe symbols). The
first paragraph of the documentation is taken to be an overview, and
in some contexts, only this overview will be shown. After the
documentation for the declaration as a whole, it is possible to
associate documetation with specific named parameters, which can
either be explicitly name or the results of converting free variables
to implicit parameters. Annotations are the same as with Javadoc
annotations, that is for the named parameter ``(n : T)``, the
corresponding annotation is ``||| @ n Some description`` that is
placed before the declaration.
Documentation is written in Markdown, though not all contexts will
display all possible formatting (for example, images are not displayed
when viewing documentation in the REPL, and only some terminals render
italics correctly). A comprehensive set of examples is given below.
.. code-block:: idris
||| Modules can also be documented.
module Docs
||| Add some numbers.
|||
||| Addition is really great. This paragraph is not part of the overview.
||| Still the same paragraph. Lists are also nifty:
||| * Really nifty!
||| * Yep!
||| * The name `add` is a **bold** choice
||| @ n is the recursive param
||| @ m is not
add : (n, m : Nat) -> Nat
add Z m = m
add (S n) m = S (add n m)
||| Append some vectors
||| @ a the contents of the vectors
||| @ xs the first vector (recursive param)
||| @ ys the second vector (not analyzed)
appendV : (xs : Vect n a) -> (ys : Vect m a) -> Vect (add n m) a
appendV [] ys = ys
appendV (x::xs) ys = x :: appendV xs ys
||| Here's a simple datatype
data Ty =
||| Unit
UNIT |
||| Functions
ARR Ty Ty
||| Points to a place in a typing context
data Elem : Vect n Ty -> Ty -> Type where
Here : {ts : Vect n Ty} -> Elem (t::ts) t
There : {ts : Vect n Ty} -> Elem ts t -> Elem (t'::ts) t
||| A more interesting datatype
||| @ n the number of free variables
||| @ ctxt a typing context for the free variables
||| @ ty the type of the term
data Term : (ctxt : Vect n Ty) -> (ty : Ty) -> Type where
||| The consructor of the unit type
||| More comment
||| @ ctxt the typing context
UnitCon : {ctxt : Vect n Ty} -> Term ctxt UNIT
||| Function application
||| @ f the function to apply
||| @ x the argument
App : {ctxt : Vect n Ty} -> (f : Term ctxt (ARR t1 t2)) -> (x : Term ctxt t1) -> Term ctxt t2
||| Lambda
||| @ body the function body
Lam : {ctxt : Vect n Ty} -> (body : Term (t1::ctxt) t2) -> Term ctxt (ARR t1 t2)
||| Variables
||| @ i de Bruijn index
Var : {ctxt : Vect n Ty} -> (i : Elem ctxt t) -> Term ctxt t
||| A computation that may someday finish
codata Partial : Type -> Type where
||| A finished computation
||| @ value the result
Now : (value : a) -> Partial a
||| A not-yet-finished computation
||| @ rest the remaining work
Later : (rest : Partial a) -> Partial a
||| We can document records just like normal data
record Yummy : Type where
||| Make a yummy
||| @ food what to eat
MkYummy : (food : String) -> Yummy

501
docs/reference/erasure.rst Normal file
View File

@ -0,0 +1,501 @@
Erasure By Usage Analysis
=========================
This work stems from this `feature proposal
<https://github.com/idris-lang/Idris-dev/wiki/Egg-%232%3A-Erasure-annotations>`__
(obsoleted by this page). Beware that the information in the proposal
is out of date — and sometimes even in direct contradiction with the
eventual implementation.
Motivation
----------
Traditional dependently typed languages (Agda, Coq) are good at
erasing *proofs* (either via irrelevance or an extra universe).
.. code-block:: idris
half : (n : Nat) -> Even n -> Nat
half Z EZ = Z
half (S (S n)) (ES pf) = S (half n pf)
For example, in the above snippet, the second argument is a proof,
which is used only to convince the compiler that the function is
total. This proof is never inspected at runtime and thus can be
erased. In this case, the mere existence of the proof is sufficient
and we can use irrelevance-related methods to achieve erasure.
However, sometimes we want to erase *indices* and this is where the
traditional approaches stop being useful, mainly for reasons described
in the `original proposal
<https://github.com/idris-lang/Idris-dev/wiki/Egg-%232%3A-Erasure-annotations#prop-is-cumbersome-coq>`__.
.. code-block:: idris
uninterleave : {n : Nat} -> Vect (n * 2) a -> (Vect n a, Vect n a)
uninterleave [] = ([] , [])
uninterleave (x :: y :: rest) with (unzipPairs rest)
| (xs, ys) = (x :: xs, y :: ys)
Notice that in this case, the second argument is the important one and
we would like to get rid of the ``n`` instead, although the shape of
the program is generally the same as in the previous case.
There are methods described by Brady, McBride and McKinna in [BMM04]_
to remove the indices from data structures, exploiting the fact that
functions operating on them either already have a copy of the
appropriate index or the index can be quickly reconstructed if needed.
However, we often want to erase the indices altogether, from the whole
program, even in those cases where reconstruction is not possible.
The following two sections describe two cases where doing so improves
the runtime performance asymptotically.
Binary numbers
~~~~~~~~~~~~~~
- O(n) instead of O(log n)
Consider the following ``Nat``-indexed type family representing binary
numbers:
.. code-block:: idris
data Bin : Nat -> Type where
N : Bin 0
O : {n : Nat} -> Bin n -> Bin (0 + 2*n)
I : {n : Nat} -> Bin n -> Bin (1 + 2*n)
These are supposed to be (at least asymptotically) fast and
memory-efficient because their size is logarithmic compared to the
numbers they represent.
Unfortunately this is not the case. The problem is that these binary
numbers still carry the *unary* indices with them, performing
arithmetic on the indices whenever arithmetic is done on the binary
numbers themselves. Hence the real representation of the number 15
looks like this:
::
I -> I -> I -> I -> N
S S S Z
S S Z
S S
S Z
S
S
S
Z
The used memory is actually *linear*, not logarithmic and therefore we
cannot get below O(n) with time complexities.
One could argue that Idris in fact compiles ``Nat`` via GMP but
that's a moot point for two reasons:
+ First, whenever we try to index our datastructures with anything
else than ``Nat``, the compiler is not going to come to the rescue.
+ Second, even with ``Nat``, the GMP integers are *still* there and
they slow the runtime down.
This ought not to be the case since the ``Nat`` are never used at
runtime and they are only there for typechecking purposes. Hence we
should get rid of them and get runtime code similar to what a idris
programmer would write.
U-views of lists
~~~~~~~~~~~~~~~~
- O(n^2) instead of O(n)
Consider the type of U-views of lists:
.. code-block:: idris
data U : List a -> Type where
nil : U []
one : (z : a) -> U [z]
two : {xs : List a} -> (x : a) -> (u : U xs) -> (y : a) -> U (x :: xs ++ [y])
For better intuition, the shape of the U-view of
``[x0,x1,x2,z,y2,y1,y0]`` looks like this:
::
x0 y0 (two)
x1 y1 (two)
x2 y1 (two)
z (one)
When recursing over this structure, the values of ``xs`` range over
``[x0,x1,x2,z,y2,y1,y0]``, ``[x1,x2,z,y2,y1]``, ``[x2,z,y2]``,
``[z]``. No matter whether these lists are stored or built on demand,
they take up a quadratic amount of memory (because they cannot share
nodes), and hence it takes a quadratic amount of time just to build
values of this index alone.
But the reasonable expectation is that operations with U-views take
linear time — so we need to erase the index ``xs`` if we want to
achieve this goal.
Changes to Idris
----------------
Usage analysis is run at every compilation and its outputs are used
for various purposes. This is actually invisible to the user but it's
a relatively big and important change, which enables the new features.
Everything that is found to be unused is erased. No annotations are
needed, just don't use the thing and it will vanish from the generated
code. However, if you wish, you can use the dot annotations to get a
warning if the thing is accidentally used.
"Being used" in this context means that the value of the "thing" may
influence run-time behaviour of the program. (More precisely, it is
not found to be irrelevant to the run-time behaviour by the usage
analysis algorithm.)
"Things" considered for removal by erasure include:
* function arguments
* data constructor fields (including record fields and dictionary
fields of class instances)
For example, ``Either`` often compiles to the same runtime
representation as ``Bool``. Constructor field removal sometimes
combines with the newtype optimisation to have quite a strong effect.
There is a new compiler option ``--warnreach``, which will enable
warnings coming from erasure. Since we have full usage analysis, we
can compile even those programs that violate erasure annotations --
it's just that the binaries may run slower than expected. The warnings
will be enabled by default in future versions of Idris (and possibly
turned to errors). However, in this transitional period, we chose to
keep them on-demand to avoid confusion until better documentation is
written.
Case-tree elaboration tries to avoid using dotted "things" whenever
possible. (NB. This is not yet perfect and it's being worked on:
https://gist.github.com/ziman/10458331)
Postulates are no longer required to be collapsible. They are now
required to be *unused* instead.
Changes to the language
-----------------------
You can use dots to mark fields that are not intended to be used at
runtime.
.. code-block:: idris
data Bin : Nat -> Type where
N : Bin 0
O : .{n : Nat} -> Bin n -> Bin (0 + 2*n)
I : .{n : Nat} -> Bin n -> Bin (1 + 2*n)
If these fields are found to be used at runtime, the dots will trigger
a warning (with ``--warnreach``).
Note that free (unbound) implicits are dotted by default so, for
example, the constructor ``O`` can be defined as:
.. code-block:: idris
O : Bin n -> Bin (0 + 2*n)
and this is actually the preferred form.
If you have a free implicit which is meant to be used at runtime, you
have to change it into an (undotted) ``{bound : implicit}``.
You can also put dots in types of functions to get more guarantees.
.. code-block:: idris
half : (n : Nat) -> .(pf : Even n) -> Nat
and free implicits are automatically dotted here, too.
What it means
-------------
Dot annotations serve two purposes:
* influence case-tree elaboration to avoid dotted variables
* trigger warnings when a dotted variable is used
However, there's no direct connection between being dotted and being
erased. The compiler erases everything it can, dotted or not. The dots
are there mainly to help the programmer (and the compiler) refrain
from using the values they want to erase.
How to use it
-------------
Ideally, few or no extra annotations are needed -- in practice, it
turns out that having free implicits automatically dotted is enough to
get good erasure.
Therefore, just compile with ``--warnreach`` to see warnings if
erasure cannot remove parts of the program.
However, those programs that have been written without runtime
behaviour in mind, will need some help to get in the form that
compiles to a reasonable binary. Generally, it's sufficient to follow
erasure warnings (which may be sometimes unhelpful at the moment).
Benchmarks
----------
- source: https://github.com/ziman/idris-benchmarks
- results: http://ziman.functor.sk/erasure-bm/
It can be clearly seen that asymptotics are improved by erasure.
Shortcomings
------------
You can't get warnings in libraries because usage analysis starts from
``Main.main``. This will be solved by the planned ``%default_usage``
pragma.
Usage warnings are quite bad and unhelpful at the moment. We should
include more information and at least translate argument numbers to
their names.
There is no decent documentation yet. This wiki page is the first one.
There is no generally accepted terminology. We switch between
"dotted", "unused", "erased", "irrelevant", "inaccessible", while each
has a slightly different meaning. We need more consistent and
understandable naming.
If the same type is used in both erased and non-erased context, it
will retain its fields to accomodate the least common denominator --
the non-erased context. This is particularly troublesome in the case
of the type of (dependent) pairs, where it actually means that no
erasure would be performed. We should probably locate disjoint uses of
data types and split them into "sub-types". There are three different
flavours of dependent types now: ``Sigma`` (nothing erased),
``Exists`` (first component erased), ``Subset`` (second component
erased).
Case-tree building does not avoid dotted values coming from
pattern-matched constructors (https://gist.github.com/ziman/10458331).
This is to be fixed soon. (Fixed.)
Higher-order function arguments and opaque functional variables are
considered to be using all their arguments. To work around this, you
can force erasure via the type system, using the ``Erased`` wrapper:
https://github.com/idris-lang/Idris-dev/blob/master/libs/base/Data/Erased.idr
Typeclass methods are considered to be using the union of all their
implementations. In other words, an argument of a method is unused
only if it is unused in every implementation of the method that occurs
in the program.
Planned features
----------------
- Fixes to the above shortcomings in general.
- Improvements to the case-tree elaborator so that it properly avoids
dotted fields of data constructors. Done.
- Compiler pragma ``%default_usage used/unused`` and per-function
overrides ``used`` and ``unused``, which allow the programmer to
mark the return value of a function as used, even if the function
is not used in ``main`` (which is the case when writing library
code). These annotations will help library writers discover usage
violations in their code before it is actually published and used
in compiled programs.
Troubleshooting
---------------
My program is slower
~~~~~~~~~~~~~~~~~~~~
The patch introducing erasure by usage analysis also disabled some
optimisations that were in place before; these are subsumed by the new
erasure. However, in some erasure-unaware programs, where erasure by
usage analysis does not exercise its full potential (but the old
optimisations would have worked), certain slowdown may be observed (up
to ~10% according to preliminary benchmarking), due to retention and
computation of information that should not be necessary at runtime.
A simple check whether this is the case is to compile with
``--warnreach``. If you see warnings, there is some unnecessary code
getting compiled into the binary.
The solution is to change the code so that there are no warnings.
Usage warnings are unhelpful
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
This is a known issue and we are working on it. For now, see the section
`How to read and resolve erasure
warnings <#how-to-read-and-resolve-erasure-warnings>`__.
There should be no warnings in this function
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
A possible cause is non-totality of the function (more precisely,
non-coverage). If a function is non-covering, the program needs to
inspect all arguments in order to detect coverage failures at runtime.
Since the function inspects all its arguments, nothing can be erased
and this may transitively cause usage violations. The solution is to
make the function total or accept the fact that it will use its
arguments and remove some dots from the appropriate constructor fields
and function arguments. (Please note that this is not a shortcoming of
erasure and there is nothing we can do about it.)
Another possible cause is the currently imperfect case-tree
elaboration, which does not avoid dotted constructor fields (see
https://gist.github.com/ziman/10458331). You can either rephrase the
function or wait until this is fixed, hopefully soon. Fixed.
The compiler refuses to recognise this thing as erased
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
You can force anything to be erased by wrapping it in the ``Erased``
monad. While this program triggers usage warnings,
.. code-block:: idris
f : (g : Nat -> Nat) -> .(x : Nat) -> Nat
f g x = g x -- WARNING: g uses x
the following program does not:
.. code-block:: idris
f : (g : Erased Nat -> Nat) -> .(x : Nat) -> Nat
f g x = g (Erase x) -- OK
How to read and resolve erasure warnings
----------------------------------------
Example 1
~~~~~~~~~
Consider the following program:
.. code-block:: idris
vlen : Vect n a -> Nat
vlen {n = n} xs = n
sumLengths : List (Vect n a) -> Nat
sumLengths [] = 0
sumLengths (v :: vs) = vlen v + sumLengths vs
main : IO ()
main = print . sumLengths $ [[0,1],[2,3]]
When you compile it using ``--warnreach``, there is one warning:
.. code-block:: idris
Main.sumLengths: inaccessible arguments reachable:
n (no more information available)
The warning does not contain much detail at this point so we can try
compiling with ``--dumpcases cases.txt`` and look up the compiled
definition in ``cases.txt``:
.. code-block:: idris
Main.sumLengths {e0} {e1} {e2} =
case {e2} of
| Prelude.List.::({e6}) => LPlus (ATInt ITBig)({e0}, Main.sumLengths({e0}, ____, {e6}))
| Prelude.List.Nil() => 0
The reason for the warning is that ``sumLengths`` calls ``vlen``, which
gets inlined. The second clause of ``sumLengths`` then accesses the
variable ``n``, compiled as ``{e0}``. Since ``n`` is a free implicit, it
is automatically considered dotted and this triggers the warning.
A solution would be either making the argument ``n`` a bound implicit
parameter to indicate that we wish to keep it at runtime,
.. code-block:: idris
sumLengths : {n : Nat} -> List (Vect n a) -> Nat
or fixing ``vlen`` to not use the index:
.. code-block:: idris
vlen : Vect n a -> Nat
vlen [] = Z
vlen (x :: xs) = S (vlen xs)
Which solution is appropriate depends on the usecase.
Example 2
~~~~~~~~~
Consider the following program manipulating value-indexed binary
numbers.
.. code-block:: idris
data Bin : Nat -> Type where
N : Bin Z
O : Bin n -> Bin (0 + n + n)
I : Bin n -> Bin (1 + n + n)
toN : (b : Bin n) -> Nat
toN N = Z
toN (O {n} bs) = 0 + n + n
toN (I {n} bs) = 1 + n + n
main : IO ()
main = print . toN $ I (I (O (O (I N))))
In the function ``toN``, we attempted to "cheat" and instead of
traversing the whole structure, we just projected the value index ``n``
out of constructors ``I`` and ``O``. However, this index is a free
implicit, therefore it is considered dotted.
Inspecting it then produces the following warnings when compiling with
``--warnreach``:
.. code-block:: idris
Main.I: inaccessible arguments reachable:
n from Main.toN arg# 1
Main.O: inaccessible arguments reachable:
n from Main.toN arg# 1
We can see that the argument ``n`` of both ``I`` and ``O`` is used in
the function ``toN``, argument 1.
At this stage of development, warnings only contain argument numbers,
not names; this will hopefully be fixed. When numbering arguments, we
go from 0, taking free implicits first, left-to-right; then the bound
arguments. The function ``toN`` has therefore in fact two arguments:
``n`` (argument 0) and ``b`` (argument 1). And indeed, as the warning
says, we project the dotted field from ``b``.
Again, one solution is to fix the function ``toN`` to calculate its
result honestly; the other one is to accept that we carry a ``Nat``
with every constructor of ``Bin`` and make it a bound implicit:
.. code-block:: idris
O : {n : Nat} -> Bin n -> Bin (0 + n + n)
I : {n : Nat} -> bin n -> Bin (1 + n + n)
References
----------
.. [BMM04] Edwin Brady, Conor McBride, James McKinna: `Inductive
families need not store their indices
<http://citeseerx.ist.psu.edu/viewdoc/summary;jsessionid=1F796FCF0F2C4C535FC70F62BE2FB821?doi=10.1.1.62.3849>`__

277
docs/reference/ffi.rst Normal file
View File

@ -0,0 +1,277 @@
New Foreign Function Interface
==============================
.. sectionauthor:: Edwin Brady
Ever since Idris has had multiple backends compiling to different
target languages on potentially different platforms, we have had the
problem that the foreign function interface (FFI) was written under
the assumption of compiling to C. As a result, it has been hard to
write generic code for multiple targets, or even to be sure that if
code compiles that it will run on the expected target.
As of 0.9.17, Idris will have a new foreign function interface (FFI)
which is aware of multiple targets. Users who are working with the
default code generator can happily continue writing programs as before
with no changes, but if you are writing bindings for an external
library, writing a back end, or working with a non-C back end, there
are some things you will need to be aware of, which this page
describes.
The ``IO'`` monad, and ``main``
-------------------------------
The ``IO`` monad exists as before, but is now specific to the C
backend (or, more precisely, any backend whose foreign function calls
are compatible with C.) Additionally, there is now an ``IO'`` monad,
which is parameterised over a FFI descriptor:
.. code-block:: idris
data IO' : (lang : FFI) -> Type -> Type
The Prelude defines two FFI descriptors which are imported
automatically, for C and JavaScript/Node, and defines ``IO`` to use
the C FFI and ``JS_IO`` to use the JavaScript FFI:
.. code-block:: idris
FFI_C : FFI
FFI_JS : FFI
IO : Type -> Type
IO a = IO' FFI_C a
JS_IO : Type -> Type
JS_IO a = IO' FFI_JS a
As before, the entry point to an Idris program is ``main``, but the
type of ``main`` can now be any instance of ``IO'``, e.g. the
following are both valid:
.. code-block:: idris
main : IO ()
main : JS_IO ()
The FFI descriptor includes details about which types can be
marshalled between the foreign language and Idris, and the "target" of
a foreign function call (typically just a String representation of the
function's name, but potentially something more complicated such as an
external library file or even a URL).
FFI descriptors
---------------
An FFI descriptor is a record containing a predicate which holds when
a type can be marshalled, and the type of the target of a foreign
call:
.. code-block:: idris
record FFI : Type where
MkFFI : (ffi_types : Type -> Type) ->
(ffi_fn : Type) -> FFI
For C, this is:
.. code-block:: idris
-- Supported C integer types
data C_IntTypes : Type -> Type where
C_IntChar : C_IntTypes Char
C_IntNative : C_IntTypes Int
... -- more integer types
-- Supported C foreign types
data C_Types : Type -> Type where
C_Str : C_Types String
C_Float : C_Types Float
C_Ptr : C_Types Ptr
C_MPtr : C_Types ManagedPtr
C_Unit : C_Types ()
C_Any : C_Types (Raw a)
C_IntT : C_IntTypes i -> C_Types i
FFI_C : FFI
FFI_C = MkFFI C_Types
String -- the name of the C function
Foreign calls
-------------
To call a foreign function, the ``foreign`` function is used. For
example:
.. code-block:: idris
do_fopen : String -> String -> IO Ptr
do_fopen f m
= foreign FFI_C "fileOpen" (String -> String -> IO Ptr) f m
The ``foreign`` function takes an FFI description, a function name (the
type is given by the ``ffi_fn`` field of ``FFI_C`` here), and a function
type, which gives the expected types of the remaining arguments. Here,
we're calling an external function ``fileOpen`` which takes, in the C, a
``char*`` file name, a ``char*`` mode, and returns a file pointer. It is
the job of the C back end to convert Idris ``String`` to C ``char*``
and vice versa.
The argument types and return type given here must be present in the
``fn_types`` predicate of the ``FFI_C`` description for the foreign
call to be valid.
**Note** The arguments to ``foreign`` *must* be known at compile time,
because the foreign calls are generated statically. The ``%inline``
directive on a function can be used to give hints to help this, for
example a shorthand for calling external JavaScript functions:
.. code-block:: idris
%inline
jscall : (fname : String) -> (ty : Type) ->
{auto fty : FTy FFI_JS [] ty} -> ty
jscall fname ty = foreign FFI_JS fname ty
FFI implementation
~~~~~~~~~~~~~~~~~~
In order to write bindings to external libraries, the details of how
``foreign`` works are unnecessary --- you simply need to know that
``foreign`` takes an FFI descriptor, the function name, and its
type. It is instructive to look a little deeper, however:
The type of ``foreign`` is as follows:
.. code-block:: idris
foreign : (ffi : FFI)
-> (fname : ffi_fn f)
-> (ty : Type)
-> {auto fty : FTy ffi [] ty}
-> ty
The important argument here is the implicit ``fty``, which contains a
proof (``FTy``) that the given type is valid according to the FFI
description ``ffi``:
.. code-block:: idris
data FTy : FFI -> List Type -> Type -> Type where
FRet : ffi_types f t -> FTy f xs (IO' f t)
FFun : ffi_types f s -> FTy f (s :: xs) t -> FTy f xs (s -> t)
Notice that this uses the ``ffi_types`` field of the FFI descriptor
--- these arguments to ``FRet`` and ``FFun`` give explicit proofs that
the type is valid in this FFI. For example, the above ``do_fopen``
builds the following implicit proof as the ``fty`` argument to
``foreign``:
.. code-block:: idris
FFun C_Str (FFun C_Str (FRet C_Ptr))
Compiling foreign calls
-----------------------
(This section assumes some knowledge of the Idris internals.)
When writing a back end, we now need to know how to compile
``foreign``. We'll skip the details here of how a ``foreign`` call
reaches the intermediate representation (the IR), though you can look
in ``IO.idr`` in the ``prelude`` package to see a bit more detail ---
a ``foreign`` call is implemented by the primitive function
``mkForeignPrim``. The important part of the IR as defined in
``Lang.hs`` is the following constructor:
.. code-block:: idris
data LExp = ...
| LForeign FDesc -- Function descriptor
FDesc -- Return type descriptor
[(FDesc, LExp)]
So, a ``foreign`` call appears in the IR as the ``LForeign``
constructor, which takes a function descriptor (of a type given by the
``ffi_fn`` field in the FFI descriptor), a return type descriptor
(given by an application of ``FTy``), and a list of arguments with
type descriptors (also given by an application of ``FTy``).
An ``FDesc`` describes an application of a name to some arguments, and
is really just a simplified subset of an ``LExp``:
.. code-block:: idris
data FDesc = FCon Name
| FStr String
| FUnknown
| FApp Name [FDesc]
There are corresponding structures in the lower level IRs, such as the
defunctionalised, simplified and bytecode forms.
Our ``do_fopen`` example above arrives in the ``LExp`` form as:
.. code-block:: idris
LForeign (FStr "fileOpen") (FCon (sUN "C_Ptr"))
[(FCon (sUN "C_Str"), f), (FCon (sUN "C_Str"), m)]
(Assuming that ``f`` and ``m`` stand for the ``LExp`` representations
of the arguments.) This information should be enough for any back end
to marshal the arguments and return value appropriately.
.. note::
When processing ``FDesc``, be aware that there may be implicit
arguments, which have not been erased. For example, ``C_IntT`` has
an implicit argument ``i``, so will appear in an ``FDesc`` as
something of the form ``FApp (sUN "C_IntT") [i, t]`` where ``i`` is
the implicit argument (which can be ignored) and ``t`` is the
descriptor of the integer type. See ``CodegenC.hs``, specifically
the function ``toFType``, to see how this works in practice.
JavaScript FFI descriptor
~~~~~~~~~~~~~~~~~~~~~~~~~
The JavaScript FFI descriptor is a little more complex, because the
JavaScript FFI supports marshalling functions. It is defined as
follows:
.. code-block:: idris
mutual
data JsFn t = MkJsFn t
data JS_IntTypes : Type -> Type where
JS_IntChar : JS_IntTypes Char
JS_IntNative : JS_IntTypes Int
data JS_FnTypes : Type -> Type where
JS_Fn : JS_Types s -> JS_FnTypes t -> JS_FnTypes (s -> t)
JS_FnIO : JS_Types t -> JS_FnTypes (IO' l t)
JS_FnBase : JS_Types t -> JS_FnTypes t
data JS_Types : Type -> Type where
JS_Str : JS_Types String
JS_Float : JS_Types Float
JS_Ptr : JS_Types Ptr
JS_Unit : JS_Types ()
JS_FnT : JS_FnTypes a -> JS_Types (JsFn a)
JS_IntT : JS_IntTypes i -> JS_Types i
The reason for wrapping function types in a ``JsFn`` is to help the
proof search when building ``FTy``. We hope to improve proof search
eventually, but for the moment it works much more reliably if the
indices are disjoint! An example of using this appears in `IdrisScript
<https://github.com/idris-hackers/IdrisScript>`__ when setting
timeouts:
.. code-block:: idris
setTimeout : (() -> JS_IO ()) -> (millis : Int) -> JS_IO Timeout
setTimeout f millis = do
timeout <- jscall "setTimeout(%0, %1)"
(JsFn (() -> JS_IO ()) -> Int -> JS_IO Ptr)
(MkJsFn f) millis
return $ MkTimeout timeout

26
docs/reference/index.rst Normal file
View File

@ -0,0 +1,26 @@
.. _reference-index:
###################
Language Reference
###################
This is the reference guide for the Idris Language.
It documents the language specification and internals.
This will tell you how Idris works, for using it you should read the Idris Tutorial.
.. note::
The documentation for Idris has been published under the Creative
Commons CC0 License. As such to the extent possible under law, *The
Idris Community* has waived all copyright and related or neighboring
rights to Documentation for Idris.
More information concerning the CC0 can be found online at: http://creativecommons.org/publicdomain/zero/1.0/
.. toctree::
:maxdepth: 1
documenting
uniqueness-types
ffi
erasure

View File

@ -0,0 +1,251 @@
Uniqueness Types
================
Uniqueness Types are an experimental feature available from Idris
0.9.15. A value with a unique type is guaranteed to have *at most one*
reference to it at run-time, which means that it can safely be updated
in-place, reducing the need for memory allocation and garbage
collection. The motivation is that we would like to be able to write
reactive systems, programs which run in limited memory environments,
device drivers, and any other system with hard real-time requirements,
ideally while giving up as little high level conveniences as possible.
They are inspired by linear types, `Uniqueness Types
<https://en.wikipedia.org/wiki/Uniqueness_type>`__ in the `Clean
<http://wiki.clean.cs.ru.nl/Clean>`__ programming language, and
ownership types and borrowed pointers in the `Rust
<http://www.rust-lang.org/>`__ programming language.
Some things we hope to be able to do eventually with uniqueness types
include:
- Safe, pure, in-place update of arrays, lists, etc
- Provide guarantees of correct resource usage, state transitions, etc
- Provide guarantees that critical program fragments will *never*
allocate
Using Uniqueness
~~~~~~~~~~~~~~~~
If ``x : T`` and ``T : UniqueType``, then there is at most one reference
to ``x`` at any time during run-time execution. For example, we can
declare the type of unique lists as follows:
.. code-block:: idris
data UList : Type -> UniqueType
Nil : UList a
(::) : a -> UList a -> UList a
If we have a value ``xs : UList a``, then there is at most one
reference to ``xs`` at run-time. The type checker preserves this
guarantee by ensuring that there is at most one reference to any value
of a unique type in a pattern clause. For example, the following
function definition would be valid:
.. code-block:: idris
umap : (a -> b) -> UList a -> UList b
umap f [] = []
umap f (x :: xs) = f x :: umap f xs
In the second clause, ``xs`` is a value of a unique type, and only
appears once on the right hand side, so this clause is valid. Not only
that, since we know there can be no other reference to the ``UList a``
argument, we can reuse its space for building the result! The compiler
is aware of this, and compiles this definition to an in-place update
of the list.
The following function definition would not be valid (even assuming an
implementation of ``++``), however, since ``xs`` appears twice:
.. code-block:: idris
dupList : UList a -> UList a
dupList xs = xs ++ xs
This would result in a shared pointer to ``xs``, so the typechecker
reports:
.. code-block:: idris
unique.idr:12:5:Unique name xs is used more than once
If we explicitly copy, however, the typechecker is happy:
.. code-block:: idris
dup : UList a -> UList a
dup [] = []
dup (x :: xs) = x :: x :: dup xs
Note that it's fine to use ``x`` twice, because ``a`` is a ``Type``,
rather than a ``UniqueType``.
There are some other restrictions on where a ``UniqueType`` can
appear, so that the uniqueness property is preserved. In particular,
the type of the function type, ``(x : a) -> b`` depends on the type of
``a`` or ``b`` - if either is a ``UniqueType``, then the function type
is also a ``UniqueType``. Then, in a data declaration, if the type
constructor builds a ``Type``, then no constructor can have a
``UniqueType``. For example, the following definition is invalid,
since it would embed a unique value in a possible non-unique value:
.. code-block:: idris
data BadList : UniqueType -> Type
Nil : {a : UniqueType} -> BadList a
(::) : {a : UniqueType} -> a -> BadList a -> BadList a
Finally, types may be polymorphic in their uniqueness, to a limited
extent. Since ``Type`` and ``UniqueType`` are different types, we are
limited in how much we can use polymorphic functions on unique types.
For example, if we have function composition defined as follows:
.. code-block:: idris
(.) : {a, b, c : Type} -> (b -> c) -> (a -> b) -> a -> c
(.) f g x = f (g x)
And we have some functions over unique types:
.. code-block:: idris
foo : UList a -> UList b
bar : UList b -> UList c
Then we cannot compose ``foo`` and ``bar`` as ``bar . foo``, because
``UList`` does not compute a ``Type``! Instead, we can define
composition as follows:
.. code-block:: idris
(.) : {a, b, c : Type*} -> (b -> c) -> (a -> b) -> a -> c
(.) f g x = f (g x)
The ``Type*`` type stands for either unique or non-unique types. Since
such a function may be passed a ``UniqueType``, any value of type
``Type*`` must also satisfy the requirement that it appears at most
once on the right hand side.
Borrowed Types
~~~~~~~~~~~~~~
It quickly becomes obvious when working with uniqueness types that
having only one reference at a time can be painful. For example, what
if we want to display a list before updating it?
.. code-block:: idris
showU : Show a => UList a -> String
showU xs = "[" ++ showU' xs ++ "]" where
showU' : UList a -> String
showU' [] = ""
showU' [x] = show x
showU' (x :: xs) = show x ++ ", " ++ showU' xs
This is a valid definition of ``showU``, but unfortunately it consumes
the list! So the following function would be invalid:
.. code-block:: idris
printAndUpdate : UList Int -> IO ()
printAndUpdate xs = do putStrLn (showU xs)
let xs' = umap (*2) xs -- xs no longer available!
putStrLn (showU xs')
Still, one would hope to be able to display a unique list without
problem, since it merely *inspects* the list; there are no updates. We
can achieve this, using the notion of *borrowing*. A Borrowed type is
a Unique type which can be inspected at the top level (by pattern
matching, or by *lending* to another function) but no further. This
ensures that the internals (i.e. the arguments to top level patterns)
will not be passed to any function which will update them.
``Borrowed`` converts a ``UniqueType`` to a ``BorrowedType``. It is
defined as follows (along with some additional rules in the
typechecker):
.. code-block:: idris
data Borrowed : UniqueType -> BorrowedType where
Read : {a : UniqueType} -> a -> Borrowed a
implicit
lend : {a : UniqueType} -> a -> Borrowed a
lend x = Read x
A value can be "lent" to another function using ``lend``. Arguments to
``lend`` are not counted by the type checker as a reference to a unique
value, therefore a value can be lent as many times as desired. Using
this, we can write ``showU`` as follows:
.. code-block:: idris
showU : Show a => Borrowed (UList a) -> String
showU xs = "[" ++ showU' xs ++ "]" where
showU' : Borrowed (UList a) -> String
showU' [] = ""
showU' [x] = show x
showU' (Read (x :: xs)) = show x ++ ", " ++ showU' (lend xs)
Unlike a unique value, a borrowed value may be referred to as many
times as desired. However, there is a restriction on how a borrowed
value can be used. After all, much like a library book or your
neighbour's lawnmower, if a function borrows a value it is expected to
return it in exactly the condition in which it was received!
The restriction is that when a ``Borrowed`` type is matched, any
pattern variables under the ``Read`` which have a unique type may not
be referred to at all on the right hand side (unless they are
themselves ``lent`` to another function).
Uniqueness information is stored in the type, and in particular in
function types. Once we're in a unique context, any new function which
is constructed will be required to have unique type, which prevents
the following sort of bad program being implemented:
.. code-block:: idris
foo : UList Int -> IO ()
foo xs = do let f = \x : Int => showU xs
putStrLn $ free xs
putStrLn $ f 42
return ()
Since ``lend`` is implicit, in practice for functions to lend and borrow
values merely requires the argument to be marked as ``Borrowed``. We can
therefore write ``showU`` as follows:
.. code-block:: idris
showU : Show a => Borrowed (UList a) -> String
showU xs = "[" ++ showU' xs ++ "]" where
showU' : Borrowed (UList a) -> String
showU' [] = ""
showU' [x] = show x
showU' (x :: xs) = show x ++ ", " ++ showU' xs
Problems/Disadvantages/Still to do...
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
This is a work in progress, there is lots to do. The most obvious
problem is the loss of abstraction. On the one hand, we have more
precise control over memory usage with ``UniqueType`` and
``BorrowedType``, but they are not in general compatible with
functions polymorphic over ``Type``. In the short term, we can start
to write reactive and low memory systems with this, but longer term it
would be nice to support more abstraction.
We also haven't checked any of the metatheory, so this could all be
fatally flawed! The implementation is based to a large extent on
`Uniqueness Typing Simplified
<http://lambda-the-ultimate.org/node/2708>`__, by de Vries et al, so
there is reason to believe things should be fine, but we still have to
do the work.
Much as there are with linear types, there are some annoyances when
trying to prove properties of functions with unique types (for
example, what counts as a use of a value). Since we require *at most*
one use of a value, rather than *exactly* one, this seems to be less
of an issue in practice, but still needs thought.

517
docs/tutorial/classes.rst Normal file
View File

@ -0,0 +1,517 @@
.. _sect-classes:
============
Type Classes
============
We often want to define functions which work across several different
data types. For example, we would like arithmetic operators to work on
``Int``, ``Integer`` and ``Float`` at the very least. We would like
``==`` to work on the majority of data types. We would like to be able
to display different types in a uniform way.
To achieve this, we use a feature which has proved to be effective in
Haskell, namely *type classes*. To define a type class, we provide a
collection of overloaded operations which describe the interface for
*instances* of that class. A simple example is the ``Show`` type
class, which is defined in the prelude and provides an interface for
converting values to ``String``:
.. code-block:: idris
class Show a where
show : a -> String
This generates a function of the following type (which we call a
*method* of the ``Show`` class):
.. code-block:: idris
show : Show a => a -> String
We can read this as: “under the constraint that ``a`` is an instance
of ``Show``, take an input ``a`` and return a ``String``.” An instance
of a class is defined with an ``instance`` declaration, which provides
implementations of the function for a specific type. For example, the
``Show`` instance for ``Nat`` could be defined as:
.. code-block:: idris
instance Show Nat where
show Z = "Z"
show (S k) = "s" ++ show k
::
Idris> show (S (S (S Z)))
"sssZ" : String
Only one instance of a class can be given for a type — instances may
not overlap. Instance declarations can themselves have constraints.
To help with resolution, the arguments of an instance must be
constructors (either data or type constructors), variables or
constants (i.e. you cannot give an instance for a function). For
example, to define a ``Show`` instance for vectors, we need to know
that there is a ``Show`` instance for the element type, because we are
going to use it to convert each element to a ``String``:
.. code-block:: idris
instance Show a => Show (Vect n a) where
show xs = "[" ++ show' xs ++ "]" where
show' : Vect n a -> String
show' Nil = ""
show' (x :: Nil) = show x
show' (x :: xs) = show x ++ ", " ++ show' xs
Default Definitions
-------------------
The library defines an ``Eq`` class which provides an interface for
comparing values for equality or inequality, with instances for all of
the built-in types:
.. code-block:: idris
class Eq a where
(==) : a -> a -> Bool
(/=) : a -> a -> Bool
To declare an instance of a type, we have to give definitions of all
of the methods. For example, for an instance of ``Eq`` for ``Nat``:
.. code-block:: idris
instance Eq Nat where
Z == Z = True
(S x) == (S y) = x == y
Z == (S y) = False
(S x) == Z = False
x /= y = not (x == y)
It is hard to imagine many cases where the ``/=`` method will be
anything other than the negation of the result of applying the ``==``
method. It is therefore convenient to give a default definition for
each method in the class declaration, in terms of the other method:
.. code-block:: idris
class Eq a where
(==) : a -> a -> Bool
(/=) : a -> a -> Bool
x /= y = not (x == y)
x == y = not (x /= y)
A minimal complete definition of an ``Eq`` instance requires either
``==`` or ``/=`` to be defined, but does not require both. If a method
definition is missing, and there is a default definition for it, then
the default is used instead.
Extending Classes
-----------------
Classes can also be extended. A logical next step from an equality
relation ``Eq`` is to define an ordering relation ``Ord``. We can
define an ``Ord`` class which inherits methods from ``Eq`` as well as
defining some of its own:
.. code-block:: idris
data Ordering = LT | EQ | GT
.. code-block:: idris
class Eq a => Ord a where
compare : a -> a -> Ordering
(<) : a -> a -> Bool
(>) : a -> a -> Bool
(<=) : a -> a -> Bool
(>=) : a -> a -> Bool
max : a -> a -> a
min : a -> a -> a
The ``Ord`` class allows us to compare two values and determine their
ordering. Only the ``compare`` method is required; every other method
has a default definition. Using this we can write functions such as
``sort``, a function which sorts a list into increasing order,
provided that the element type of the list is in the ``Ord`` class. We
give the constraints on the type variables left of the fat arrow
``=>``, and the function type to the right of the fat arrow:
.. code-block:: idris
sort : Ord a => List a -> List a
Functions, classes and instances can have multiple
constraints. Multiple constaints are written in brackets in a comma
separated list, for example:
.. code-block:: idris
sortAndShow : (Ord a, Show a) => List a -> String
sortAndShow xs = show (sort xs)
Functors and Applicatives
-------------------------
So far, we have seen single parameter type classes, where the parameter
is of type ``Type``. In general, there can be any number (greater than
0) of parameters, and the parameters can have *any* type. If the type
of the parameter is not ``Type``, we need to give an explicit type
declaration. For example, the ``Functor`` class is defined in the
library:
.. code-block:: idris
class Functor (f : Type -> Type) where
map : (m : a -> b) -> f a -> f b
A functor allows a function to be applied across a structure, for
example to apply a function to every element in a ``List``:
.. code-block:: idris
instance Functor List where
map f [] = []
map f (x::xs) = f x :: map f xs
::
Idris> map (*2) [1..10]
[2, 4, 6, 8, 10, 12, 14, 16, 18, 20] : List Integer
Having defined ``Functor``, we can define ``Applicative`` which
abstracts the notion of function application:
.. code-block:: idris
infixl 2 <*>
class Functor f => Applicative (f : Type -> Type) where
pure : a -> f a
(<*>) : f (a -> b) -> f a -> f b
Monads and ``do``-notation
--------------------------
The ``Monad`` class allows us to encapsulate binding and computation,
and is the basis of ``do``-notation introduced in Section
:ref:`sect-do`. It extends ``Applicative`` as defined above, and is
defined as follows:
.. code-block:: idris
class Applicative m => Monad (m : Type -> Type) where
(>>=) : m a -> (a -> m b) -> m b
Inside a ``do`` block, the following syntactic transformations are
applied:
- ``x <- v; e`` becomes ``v >>= (\backslashx => e)``
- ``v; e`` becomes ``v >>= (\backslash_ => e)``
- ``let x = v; e`` becomes ``let x = v in e``
``IO`` is an instance of ``Monad``, defined using primitive functions.
We can also define an instance for ``Maybe``, as follows:
.. code-block:: idris
instance Monad Maybe where
Nothing >>= k = Nothing
(Just x) >>= k = k x
Using this we can, for example, define a function which adds two
``Maybe Int``, using the monad to encapsulate the error handling:
.. code-block:: idris
m_add : Maybe Int -> Maybe Int -> Maybe Int
m_add x y = do x' <- x -- Extract value from x
y' <- y -- Extract value from y
return (x' + y') -- Add them
This function will extract the values from ``x`` and ``y``, if they
are available, or return ``Nothing`` if they are not. Managing the
``Nothing`` cases is achieved by the ``>>=`` operator, hidden by the
``do`` notation.
::
*classes> m_add (Just 20) (Just 22)
Just 42 : Maybe Int
*classes> m_add (Just 20) Nothing
Nothing : Maybe Int
``!``-notation
~~~~~~~~~~~~~~
In many cases, using ``do``-notation can make programs unnecessarily
verbose, particularly in cases such as ``m_add`` above where the value
bound is used once, immediately. In these cases, we can use a
shorthand version, as follows:
.. code-block:: idris
m_add : Maybe Int -> Maybe Int -> Maybe Int
m_add x y = return (!x + !y)
The notation ``!expr`` means that the expression ``expr`` should be
evaluated and then implicitly bound. Conceptually, we can think of
``!`` as being a prefix function with the following type:
.. code-block:: idris
(!) : m a -> a
Note, however, that it is not really a function, merely syntax! In
practice, a subexpression ``!expr`` will lift ``expr`` as high as
possible within its current scope, bind it to a fresh name ``x``, and
replace ``!expr`` with ``x``. Expressions are lifted depth first, left
to right. In practice, ``!``-notation allows us to program in a more
direct style, while still giving a notational clue as to which
expressions are monadic.
For example, the expression:
.. code-block:: idris
let y = 42 in f !(g !(print y) !x)
is lifted to:
.. code-block:: idris
let y = 42 in do y' <- print y
x' <- x
g' <- g y' x'
f g'
Monad comprehensions
~~~~~~~~~~~~~~~~~~~~
The list comprehension notation we saw in Section
:ref:`sect-more-expr` is more general, and applies to anything which
is an instance of both ``Monad`` and ``Alternative``:
.. code-block:: idris
class Applicative f => Alternative (f : Type -> Type) where
empty : f a
(<|>) : f a -> f a -> f a
In general, a comprehension takes the form ``[ exp | qual1, qual2, …,
qualn ]`` where ``quali`` can be one of:
- A generator ``x <- e``
- A *guard*, which is an expression of type ``Bool``
- A let binding ``let x = e``
To translate a comprehension ``[exp | qual1, qual2, …, qualn]``, first
any qualifier ``qual`` which is a *guard* is translated to ``guard
qual``, using the following function:
.. code-block:: idris
guard : Alternative f => Bool -> f ()
Then the comprehension is converted to ``do`` notation:
.. code-block:: idris
do { qual1; qual2; ...; qualn; return exp; }
Using monad comprehensions, an alternative definition for ``m_add``
would be:
.. code-block:: idris
m_add : Maybe Int -> Maybe Int -> Maybe Int
m_add x y = [ x' + y' | x' <- x, y' <- y ]
Idiom brackets
--------------
While ``do`` notation gives an alternative meaning to sequencing,
idioms give an alternative meaning to *application*. The notation and
larger example in this section is inspired by Conor McBride and Ross
Patersons paper “Applicative Programming with Effects” [1]_.
First, let us revisit ``m_add`` above. All it is really doing is
applying an operator to two values extracted from ``Maybe Int``. We
could abstract out the application:
.. code-block:: idris
m_app : Maybe (a -> b) -> Maybe a -> Maybe b
m_app (Just f) (Just a) = Just (f a)
m_app _ _ = Nothing
Using this, we can write an alternative ``m_add`` which uses this
alternative notion of function application, with explicit calls to
``m_app``:
.. code-block:: idris
m_add' : Maybe Int -> Maybe Int -> Maybe Int
m_add' x y = m_app (m_app (Just (+)) x) y
Rather than having to insert ``m_app`` everywhere there is an
application, we can use to do the job for us. To do this, we can make
``Maybe`` an instance of ``Applicative`` as follows, where ``<>`` is
defined in the same way as ``m_app`` above (this is defined in the
Idris library):
.. code-block:: idris
instance Applicative Maybe where
pure = Just
(Just f) <*> (Just a) = Just (f a)
_ <*> _ = Nothing
Using we can use this instance as follows, where a function
application ``[| f a1 …an |]`` is translated into ``pure f <> a1 <>
…<> an``:
.. code-block:: idris
m_add' : Maybe Int -> Maybe Int -> Maybe Int
m_add' x y = [| x + y |]
An error-handling interpreter
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Idiom notation is commonly useful when defining evaluators. McBride
and Paterson describe such an evaluator [1]_, for a language similar
to the following:
.. code-block:: idris
data Expr = Var String -- variables
| Val Int -- values
| Add Expr Expr -- addition
Evaluation will take place relative to a context mapping variables
(represented as ``String``s) to integer values, and can possibly fail.
We define a data type ``Eval`` to wrap an evaluator:
.. code-block:: idris
data Eval : Type -> Type where
MkEval : (List (String, Int) -> Maybe a) -> Eval a
Wrapping the evaluator in a data type means we will be able to make it
an instance of a type class later. We begin by defining a function to
retrieve values from the context during evaluation:
.. code-block:: idris
fetch : String -> Eval Int
fetch x = MkEval (\e => fetchVal e) where
fetchVal : List (String, Int) -> Maybe Int
fetchVal [] = Nothing
fetchVal ((v, val) :: xs) = if (x == v)
then (Just val)
else (fetchVal xs)
When defining an evaluator for the language, we will be applying
functions in the context of an ``Eval``, so it is natural to make
``Eval`` an instance of ``Applicative``. Before ``Eval`` can be an
instance of ``Applicative`` it is necessary to make ``Eval`` an
instance of ``Functor``:
.. code-block:: idris
instance Functor Eval where
map f (MkEval g) = MkEval (\e => map f (g e))
instance Applicative Eval where
pure x = MkEval (\e => Just x)
(<*>) (MkEval f) (MkEval g) = MkEval (\x => app (f x) (g x)) where
app : Maybe (a -> b) -> Maybe a -> Maybe b
app (Just fx) (Just gx) = Just (fx gx)
app _ _ = Nothing
Evaluating an expression can now make use of the idiomatic application
to handle errors:
.. code-block:: idris
eval : Expr -> Eval Int
eval (Var x) = fetch x
eval (Val x) = [| x |]
eval (Add x y) = [| eval x + eval y |]
runEval : List (String, Int) -> Expr -> Maybe Int
runEval env e = case eval e of
MkEval envFn => envFn env
Named Instances
---------------
It can be desirable to have multiple instances of a type class, for
example to provide alternative methods for sorting or printing values.
To achieve this, instances can be *named* as follows:
.. code-block:: idris
instance [myord] Ord Nat where
compare Z (S n) = GT
compare (S n) Z = LT
compare Z Z = EQ
compare (S x) (S y) = compare @{myord} x y
This declares an instance as normal, but with an explicit name,
``myord``. The syntax ``compare @{myord}`` gives an explicit instance to
``compare``, otherwise it would use the default instance for ``Nat``. We
can use this, for example, to sort a list of ``Nat`` in reverse.
Given the following list:
.. code-block:: idris
testList : List Nat
testList = [3,4,1]
We can sort it using the default ``Ord`` instance, then the named
instance ``myord`` as follows, at the Idris prompt:
::
*named_instance> show (sort testList)
"[sO, sssO, ssssO]" : String
*named_instance> show (sort @{myord} testList)
"[ssssO, sssO, sO]" : String
Determining Parameters
----------------------
When a class has more than one parameter, it can help resolution if
the parameters used to resolve the type class are restricted. For
example:
.. code-block:: idris
class Monad m => MonadState s (m : Type -> Type) | m where
get : m s
put : s -> m ()
In this class, only ``m`` needs to be known to resolve this class, and
``s`` can then be determined from the instance. This is declared with
the ``| m`` after the class declaration. We call ``m`` a *determining
parameter* of the ``MonadState`` class, because it is the parameter
used to resolve an instance.
.. [1] Conor Mcbride and Ross Paterson. 2008. Applicative programming
with effects. J. Funct. Program. 18, 1 (January 2008),
1-13. DOI=10.1017/S0956796807006326
http://dx.doi.org/10.1017/S0956796807006326

View File

@ -0,0 +1,54 @@
.. _sect-concs:
===============
Further Reading
===============
Further information about Idris programming, and programming with
dependent types in general, can be obtained from various sources:
- The Idris web site (http://idris-lang.org/) and by asking
questions on the mailing list.
- The IRC channel ``#idris``, on
`chat.freenode.net <http://chat.freenode.net>`__.
- The wiki (https://github.com/idris-lang/Idris-dev/wiki/) has further
user provided information, in particular:
- https://github.com/idris-lang/Idris-dev/wiki/Manual
- https://github.com/idris-lang/Idris-dev/wiki/Language-Features
- Examining the prelude and exploring the ``samples`` in the
distribution. The Idris source can be found online at:
https://github.com/idris-lang/Idris-dev.
- Existing projects on the ``Idris Hackers`` web space:
http://idris-hackers.github.io.
- Various papers (e.g. [1]_, [2]_, and [3]_). Although these mostly
describe older versions of Idris.
.. [1] Edwin Brady and Kevin Hammond. 2012. Resource-Safe systems
programming with embedded domain specific languages. In
Proceedings of the 14th international conference on Practical
Aspects of Declarative Languages (PADL'12), Claudio Russo and
Neng-Fa Zhou (Eds.). Springer-Verlag, Berlin, Heidelberg,
242-257. DOI=10.1007/978-3-642-27694-1_18
http://dx.doi.org/10.1007/978-3-642-27694-1_18
.. [2] Edwin C. Brady. 2011. IDRIS ---: systems programming meets full
dependent types. In Proceedings of the 5th ACM workshop on
Programming languages meets program verification (PLPV
'11). ACM, New York, NY, USA,
43-54. DOI=10.1145/1929529.1929536
http://doi.acm.org/10.1145/1929529.1929536
.. [3] Edwin C. Brady and Kevin Hammond. 2010. Scrapping your
inefficient engine: using partial evaluation to improve
domain-specific language implementation. In Proceedings of the
15th ACM SIGPLAN international conference on Functional
programming (ICFP '10). ACM, New York, NY, USA,
297-308. DOI=10.1145/1863543.1863587
http://doi.acm.org/10.1145/1863543.1863587

34
docs/tutorial/index.rst Normal file
View File

@ -0,0 +1,34 @@
.. _tutorial-index:
####################
The Idris Tutorial
####################
The is the Idris Tutorial. It will teach you about programming in the Idris Language.
.. note::
The documentation for Idris has been published under the Creative
Commons CC0 License. As such to the extent possible under law, *The
Idris Community* has waived all copyright and related or neighboring
rights to Documentation for Idris.
More information concerning the CC0 can be found online at: http://creativecommons.org/publicdomain/zero/1.0/
.. toctree::
:maxdepth: 1
introduction
starting
typesfuns
classes
modules
packages
interp
views
theorems
provisional
interactive
syntax
miscellany
conclusions

View File

@ -0,0 +1,258 @@
.. _sect-interactive:
===================
Interactive Editing
===================
By now, we have seen several examples of how Idris dependent type
system can give extra confidence in a functions correctness by giving
a more precise description of its intended behaviour in its *type*. We
have also seen an example of how the type system can help with EDSL
development by allowing a programmer to describe the type system of an
object language. However, precise types give us more than verification
of programs — we can also exploit types to help write programs which
are *correct by construction*.
The Idris REPL provides several commands for inspecting and
modifying parts of programs, based on their types, such as case
splitting on a pattern variable, inspecting the type of a
metavariable, and even a basic proof search mechanism. In this
section, we explain how these features can be exploited by a text
editor, and specifically how to do so in `Vim
<https://github.com/idris-hackers/idris-vim>`_. An interactive mode
for `Emacs <https://github.com/idris-hackers/idris-emacs>`_ is also
available.
Editing at the REPL
-------------------
The REPL provides a number of commands, which we will describe
shortly, which generate new program fragments based on the currently
loaded module. These take the general form
::
:command [line number] [name]
That is, each command acts on a specific source line, at a specific
name, and outputs a new program fragment. Each command has an
alternative form, which *updates* the source file in-place:
::
:command! [line number] [name]
When the REPL is loaded, it also starts a background process which
accepts and responds to REPL commands, using ``idris --client``. For
example, if we have a REPL running elsewhere, we can execute commands
such as:
::
$ idris --client ':t plus'
Prelude.Nat.plus : Nat -> Nat -> Nat
$ idris --client '2+2'
4 : Integer
A text editor can take advantage of this, along with the editing
commands, in order to provide interactive editing support.
Editing Commands
----------------
:addclause
~~~~~~~~~~
The ``:addclause n f`` command (abbreviated ``:ac n f``) creates a
template definition for the function named ``f`` declared on line
``n``. For example, if the code beginning on line 94 contains:
.. code-block:: idris
vzipWith : (a -> b -> c) ->
Vect n a -> Vect n b -> Vect n c
then ``:ac 94 vzipWith`` will give:
.. code-block:: idris
vzipWith f xs ys = ?vzipWith_rhs
The names are chosen according to hints which may be given by a
programmer, and then made unique by the machine by adding a digit if
necessary. Hints can be given as follows:
.. code-block:: idris
%name Vect xs, ys, zs, ws
This declares that any names generated for types in the ``Vect`` family
should be chosen in the order ``xs``, ``ys``, ``zs``, ``ws``.
:casesplit
~~~~~~~~~~
The ``:casesplit n x`` command, abbreviated ``:cs n x``, splits the
pattern variable ``x`` on line ``n`` into the various pattern forms it
may take, removing any cases which are impossible due to unification
errors. For example, if the code beginning on line 94 is:
.. code-block:: idris
vzipWith : (a -> b -> c) ->
Vect n a -> Vect n b -> Vect n c
vzipWith f xs ys = ?vzipWith_rhs
then ``:cs 96 xs`` will give:
.. code-block:: idris
vzipWith f [] ys = ?vzipWith_rhs_1
vzipWith f (x :: xs) ys = ?vzipWith_rhs_2
That is, the pattern variable ``xs`` has been split into the two
possible cases ``[]`` and ``x :: xs``. Again, the names are chosen
according to the same heuristic. If we update the file (using
``:cs!``) then case split on ``ys`` on the same line, we get:
.. code-block:: idris
vzipWith f [] [] = ?vzipWith_rhs_3
That is, the pattern variable ``ys`` has been split into one case
``[]``, Idris having noticed that the other possible case ``y ::
ys`` would lead to a unification error.
:addmissing
~~~~~~~~~~~
The ``:addmissing n f`` command, abbreviated ``:am n f``, adds the
clauses which are required to make the function ``f`` on line ``n``
cover all inputs. For example, if the code beginning on line 94 is
.. code-block:: idris
vzipWith : (a -> b -> c) ->
Vect n a -> Vect n b -> Vect n c
vzipWith f [] [] = ?vzipWith_rhs_1
then ``:am 96 vzipWith`` gives:
.. code-block:: idris
vzipWith f (x :: xs) (y :: ys) = ?vzipWith_rhs_2
That is, it notices that there are no cases for non-empty vectors,
generates the required clauses, and eliminates the clauses which would
lead to unification errors.
:proofsearch
~~~~~~~~~~~~
The ``:proofsearch n f`` command, abbreviated ``:ps n f``, attempts to
find a value for the metavariable ``f`` on line ``n`` by proof search,
trying values of local variables, recursive calls and constructors of
the required family. Optionally, it can take a list of *hints*, which
are functions it can try applying to solve the metavariable. For
example, if the code beginning on line 94 is:
.. code-block:: idris
vzipWith : (a -> b -> c) ->
Vect n a -> Vect n b -> Vect n c
vzipWith f [] [] = ?vzipWith_rhs_1
vzipWith f (x :: xs) (y :: ys) = ?vzipWith_rhs_2
then ``:ps 96 vzipWith_rhs_1`` will give
.. code-block:: idris
[]
This works because it is searching for a ``Vect`` of length 0, of
which the empty vector is the only possibiliy. Similarly, and perhaps
surprisingly, there is only one possibility if we try to solve ``:ps
97 vzipWith_rhs_2``:
.. code-block:: idris
f x y :: (vzipWith f xs ys)
This works because ``vzipWith`` has a precise enough type: The
resulting vector has to be non-empty (a ``::``); the first element
must have type ``c`` and the only way to get this is to apply ``f`` to
``x`` and ``y``; finally, the tail of the vector can only be built
recursively.
:makewith
~~~~~~~~~
The ``:makewith n f`` command, abbreviated ``:mw n f``, adds a
``with`` to a pattern clause. For example, recall ``parity``. If line
10 is:
.. code-block:: idris
parity (S k) = ?parity_rhs
then ``:mw 10 parity`` will give:
.. code-block:: idris
parity (S k) with (_)
parity (S k) | with_pat = ?parity_rhs
If we then fill in the placeholder ``_`` with ``parity k`` and case
split on ``with_pat`` using ``:cs 11 with_pat`` we get the following
patterns:
.. code-block:: idris
parity (S (plus n n)) | even = ?parity_rhs_1
parity (S (S (plus n n))) | odd = ?parity_rhs_2
Note that case splitting has normalised the patterns here (giving
``plus`` rather than ``+``). In any case, we see that using
interactive editing significantly simplifies the implementation of
dependent pattern matching by showing a programmer exactly what the
valid patterns are.
Interactive Editing in Vim
--------------------------
The editor mode for Vim provides syntax highlighting, indentation and
interactive editing support using the commands described above.
Interactive editing is achieved using the following editor commands,
each of which update the buffer directly:
- ``\d`` adds a template definition for the name declared on the
current line (using ``:addclause``).
- ``\c`` case splits the variable at the cursor (using
``:casesplit``).
- ``\m`` adds the missing cases for the name at the cursor (using
``:addmissing``).
- ``\w`` adds a ``with`` clause (using ``:makewith``).
- ``\o`` invokes a proof search to solve the metavariable under the
cursor (using ``:proofsearch``).
- ``\p`` invokes a proof search with additional hints to solve the
metavariable under the cursor (using ``:proofsearch``).
There are also commands to invoke the type checker and evaluator:
- ``\t`` displays the type of the (globally visible) name under the
cursor. In the case of a metavariable, this displays the context
and the expected type.
- ``\e`` prompts for an expression to evaluate.
- ``\r`` reloads and type checks the buffer.
Corresponding commands are also available in the Emacs mode. Support
for other editors can be added in a relatively straighforward manner
by using ``idris client``.

288
docs/tutorial/interp.rst Normal file
View File

@ -0,0 +1,288 @@
.. _sect-interp:
===================================
Example: The Well-Typed Interpreter
===================================
In this section, well use the features weve seen so far to write a
larger example, an interpreter for a simple functional programming
language, with variables, function application, binary operators and
an ``if...then...else`` construct. We will use the dependent type
system to ensure that any programs which can be represented are
well-typed.
Representing Languages
----------------------
First, let us define the types in the language. We have integers,
booleans, and functions, represented by ``Ty``:
.. code-block:: idris
data Ty = TyInt | TyBool | TyFun Ty Ty
We can write a function to translate these representations to a concrete
Idris type — remember that types are first class, so can be
calculated just like any other value:
.. code-block:: idris
interpTy : Ty -> Type
interpTy TyInt = Int
interpTy TyBool = Bool
interpTy (TyFun A T) = interpTy A -> interpTy T
Were going to define a representation of our language in such a way
that only well-typed programs can be represented. Well index the
representations of expressions by their type and the types of local
variables (the context), which well be using regularly as an implicit
argument, so we define everything in a ``using`` block:
.. code-block:: idris
using (G:Vect n Ty)
Expressions are indexed by the types of the local variables, and the type of
the expression itself:
.. code-block:: idris
data Expr : Vect n Ty -> Ty -> Type
The full representation of expressions is:
.. code-block:: idris
data HasType : (i : Fin n) -> Vect n Ty -> Ty -> Type where
Stop : HasType FZ (t :: G) t
Pop : HasType k G t -> HasType (FS k) (u :: G) t
data Expr : Vect n Ty -> Ty -> Type where
Var : HasType i G t -> Expr G t
Val : (x : Int) -> Expr G TyInt
Lam : Expr (a :: G) t -> Expr G (TyFun a t)
App : Expr G (TyFun a t) -> Expr G a -> Expr G t
Op : (interpTy a -> interpTy b -> interpTy c) ->
Expr G a -> Expr G b -> Expr G c
If : Expr G TyBool ->
Lazy (Expr G a) ->
Lazy (Expr G a) -> Expr G a
Since expressions are indexed by their type, we can read the typing
rules of the language from the definitions of the constructors. Let us
look at each constructor in turn.
We use a nameless representation for variables — they are *de Bruijn
indexed*. Variables are represented by a proof of their membership in
the context, ``HasType i G T``, which is a proof that variable ``i``
in context ``G`` has type ``T``. This is defined as follows:
.. code-block:: idris
data HasType : (i : Fin n) -> Vect n Ty -> Ty -> Type where
Stop : HasType FZ (t :: G) t
Pop : HasType k G t -> HasType (FS k) (u :: G) t
We can treat *Stop* as a proof that the most recently defined variable
is well-typed, and *Pop n* as a proof that, if the ``n``\ th most
recently defined variable is well-typed, so is the ``n+1``\ th. In
practice, this means we use ``Stop`` to refer to the most recently
defined variable, ``Pop Stop`` to refer to the next, and so on, via
the ``Var`` constructor:
.. code-block:: idris
Var : HasType i G t -> Expr G t
So, in an expression ``\x,\y. x y``, the variable ``x`` would have a
de Bruijn index of 1, represented as ``Pop Stop``, and ``y 0``,
represented as ``Stop``. We find these by counting the number of
lambdas between the definition and the use.
A value carries a concrete representation of an integer:
.. code-block:: idris
Val : (x : Int) -> Expr G TyInt
A lambda creates a function. In the scope of a function of type ``a ->
t``, there is a new local variable of type ``a``, which is expressed
by the context index:
.. code-block:: idris
Lam : Expr (a :: G) t -> Expr G (TyFun a t)
Function application produces a value of type ``t`` given a function
from ``a`` to ``t`` and a value of type ``a``:
.. code-block:: idris
App : Expr G (TyFun a t) -> Expr G a -> Expr G t
We allow arbitrary binary operators, where the type of the operator
informs what the types of the arguments must be:
.. code-block:: idris
Op : (interpTy a -> interpTy b -> interpTy c) ->
Expr G a -> Expr G b -> Expr G c
Finally, if expressions make a choice given a boolean. Each branch
must have the same type, and we will evaluate the branches lazily so
that only the branch which is taken need be evaluated:
.. code-block:: idris
If : Expr G TyBool ->
Lazy (Expr G a) ->
Lazy (Expr G a) ->
Expr G a
Writing the Interpreter
-----------------------
When we evaluate an ``Expr``, well need to know the values in scope,
as well as their types. ``Env`` is an environment, indexed over the
types in scope. Since an environment is just another form of list,
albeit with a strongly specified connection to the vector of local
variable types, we use the usual ``::`` and ``Nil`` constructors so
that we can use the usual list syntax. Given a proof that a variable
is defined in the context, we can then produce a value from the
environment:
.. code-block:: idris
data Env : Vect n Ty -> Type where
Nil : Env Nil
(::) : interpTy a -> Env G -> Env (a :: G)
lookup : HasType i G t -> Env G -> interpTy t
lookup Stop (x :: xs) = x
lookup (Pop k) (x :: xs) = lookup k xs
Given this, an interpreter is a function which
translates an ``Expr`` into a concrete Idris value with respect to a
specific environment:
.. code-block:: idris
interp : Env G -> Expr G t -> interpTy t
The complete interpreter is defined as follows, for reference. For
each constructor, we translate it into the corresponding Idris value:
.. code-block:: idris
interp env (Var i) = lookup i env
interp env (Val x) = x
interp env (Lam sc) = \x => interp (x :: env) sc
interp env (App f s) = interp env f (interp env s)
interp env (Op op x y) = op (interp env x) (interp env y)
interp env (If x t e) = if interp env x then interp env t
else interp env e
Let us look at each case in turn. To translate a variable, we simply look it
up in the environment:
.. code-block:: idris
interp env (Var i) = lookup i env
To translate a value, we just return the concrete representation of the
value:
.. code-block:: idris
interp env (Val x) = x
Lambdas are more interesting. In this case, we construct a function
which interprets the scope of the lambda with a new value in the
environment. So, a function in the object language is translated to an
Idris function:
.. code-block:: idris
interp env (Lam sc) = \x => interp (x :: env) sc
For an application, we interpret the function and its argument and apply
it directly. We know that interpreting ``f`` must produce a function,
because of its type:
.. code-block:: idris
interp env (App f s) = interp env f (interp env s)
Operators and interpreters are, again, direct translations into the
equivalent Idris constructs. For operators, we apply the function to
its operands directly, and for ``If``, we apply the Idris
``if...then...else`` construct directly.
.. code-block:: idris
interp env (Op op x y) = op (interp env x) (interp env y)
interp env (If x t e) = if interp env x then interp env t
else interp env e
Testing
-------
We can make some simple test functions. Firstly, adding two inputs
``\x. \y. y + x`` is written as follows:
.. code-block:: idris
add : Expr G (TyFun TyInt (TyFun TyInt TyInt))
add = Lam (Lam (Op (+) (Var Stop) (Var (Pop Stop))))
More interestingly, a factorial function ``fact``
(e.g. ``\. if (x == 0) then 1 else (fact (x-1) * x)``),
can be written as:
.. code-block:: idris
fact : Expr G (TyFun TyInt TyInt)
fact = Lam (If (Op (==) (Var Stop) (Val 0))
(Val 1)
(Op (*) (App fact (Op (-) (Var Stop) (Val 1)))
(Var Stop)))
Running
-------
To finish, we write a ``main`` program which interprets the factorial
function on user input:
.. code-block:: idris
main : IO ()
main = do putStr "Enter a number: "
x <- getLine
print (interp [] fact (cast x))
Here, ``cast`` is an overloaded function which converts a value from
one type to another if possible. Here, it converts a string to an
integer, giving 0 if the input is invalid. An example run of this
program at the Idris interactive environment is:
.. _factrun:
.. literalinclude:: ../listing/idris-prompt-interp.txt
Aside: ``cast``
~~~~~~~~~~~~~~~
The prelude defines a type class ``Cast`` which allows conversion
between types:
.. code-block:: idris
class Cast from to where
cast : from -> to
It is a *multi-parameter* type class, defining the source type and
object type of the cast. It must be possible for the type checker to
infer *both* parameters at the point where the cast is applied. There
are casts defined between all of the primitive types, as far as they
make sense.

View File

@ -0,0 +1,69 @@
.. _sect-intro:
============
Introduction
============
In conventional programming languages, there is a clear distinction
between *types* and *values*. For example, in `Haskell
<http://www.haskell.org>`_, the following are types, representing
integers, characters, lists of characters, and lists of any value
respectively:
- ``Int``, ``Char``, ``[Char]``, ``[a]``
Correspondingly, the following values are examples of inhabitants of
those types:
- ``42``, ``a``, ``Hello world!``, ``[2,3,4,5,6]``
In a language with *dependent types*, however, the distinction is less
clear. Dependent types allow types to “depend” on values — in other
words, types are a *first class* language construct and can be
manipulated like any other value. The standard example is the type of
lists of a given length [1]_, ``Vect n a``, where ``a`` is the element
type and ``n`` is the length of the list and can be an arbitrary term.
When types can contain values, and where those values describe
properties (e.g. the length of a list) the type of a function can
begin to describe its own properties. For example, concatenating two
lists has the property that the resulting lists length is the sum of
the lengths of the two input lists. We can therefore give the
following type to the ``app`` function, which concatenates vectors:
.. code-block:: idris
app : Vect n a -> Vect m a -> Vect (n + m) a
This tutorial introduces Idris, a general purpose functional
programming language with dependent types. The goal of the Idris
project is to build a dependently typed language suitable for
verifiable *systems* programming. To this end, Idris is a compiled
language which aims to generate efficient executable code. It also has
a lightweight foreign function interface which allows easy interaction
with external ``C`` libraries.
Intended Audience
-----------------
This tutorial is intended as a brief introduction to the language, and
is aimed at readers already familiar with a functional language such
as `Haskell <http://www.haskell.org>`_ or `OCaml <http://ocaml.org>`_.
In particular, a certain amount of familiarity with Haskell syntax is
assumed, although most concepts will at least be explained
briefly. The reader is also assumed to have some interest in using
dependent types for writing and verifying systems software.
Example Code
------------
This tutorial includes some example code, which has been tested with
Idris version . The files are available in the Idris
distribution, and provided along side the tutorial source, so that you
can try them out easily, under ``tutorial/examples``. However, it is
strongly recommended that you can type them in yourself, rather than
simply loading and reading them.
.. [1]
Typically, and perhaps confusingly, referred to in the dependently
typed programming literature as “vectors”

View File

@ -0,0 +1,546 @@
.. _sect-misc:
==========
Miscellany
==========
In this section we discuss a variety of additional features:
+ auto, implicit, and default arguments;
+ literate programming;
+ interfacing with external libraries through the foreign function
+ interface;
+ type providers;
+ code generation; and
+ the universe hierarchy.
Auto implicit arguments
-----------------------
We have already seen implicit arguments, which allows arguments to be
omitted when they can be inferred by the type checker, e.g.
.. code-block:: idris
index : {a:Type} -> {n:Nat} -> Fin n -> Vect n a -> a
In other situations, it may be possible to infer arguments not by type
checking but by searching the context for an appropriate value, or
constructing a proof. For example, the following definition of ``head``
which requires a proof that the list is non-empty:
.. code-block:: idris
isCons : List a -> Bool
isCons [] = False
isCons (x :: xs) = True
head : (xs : List a) -> (isCons xs = True) -> a
head (x :: xs) _ = x
If the list is statically known to be non-empty, either because its
value is known or because a proof already exists in the context, the
proof can be constructed automatically. Auto implicit arguments allow
this to happen silently. We define ``head`` as follows:
.. code-block:: idris
head : (xs : List a) -> {auto p : isCons xs = True} -> a
head (x :: xs) = x
The ``auto`` annotation on the implicit argument means that Idris
will attempt to fill in the implicit argument using the ``trivial``
tactic, which searches through the context for a proof, and tries to
solve with ``refl`` if a proof is not found. Now when ``head`` is
applied, the proof can be omitted. In the case that a proof is not
found, it can be provided explicitly as normal:
.. code-block:: idris
head xs {p = ?headProof}
More generally, we can fill in implicit arguments with a default value
by annotating them with ``default``. The definition above is equivalent
to:
.. code-block:: idris
head : (xs : List a) ->
{default proof { trivial; } p : isCons xs = True} -> a
head (x :: xs) = x
Implicit conversions
--------------------
Idris supports the creation of *implicit conversions*, which allow
automatic conversion of values from one type to another when required to
make a term type correct. This is intended to increase convenience and
reduce verbosity. A contrived but simple example is the following:
.. code-block:: idris
implicit intString : Int -> String
intString = show
test : Int -> String
test x = "Number " ++ x
In general, we cannot append an ``Int`` to a ``String``, but the
implicit conversion function ``intString`` can convert ``x`` to a
``String``, so the definition of ``test`` is type correct. An implicit
conversion is implemented just like any other function, but given the
``implicit`` modifier, and restricted to one explicit argument.
Only one implicit conversion will be applied at a time. That is,
implicit conversions cannot be chained. Implicit conversions of simple
types, as above, are however discouraged! More commonly, an implicit
conversion would be used to reduce verbosity in an embedded domain
specific language, or to hide details of a proof. Such examples are
beyond the scope of this tutorial.
Literate programming
--------------------
Like Haskell, Idris supports *literate* programming. If a file has
an extension of ``.lidr`` then it is assumed to be a literate file. In
literate programs, everything is assumed to be a comment unless the line
begins with a greater than sign ``>``, for example:
.. code-block:: idris
> module literate
This is a comment. The main program is below
> main : IO ()
> main = putStrLn "Hello literate world!\n"
An additional restriction is that there must be a blank line between a
program line (beginning with ``>``) and a comment line (beginning with
any other character).
Foreign function calls
----------------------
For practical programming, it is often necessary to be able to use
external libraries, particularly for interfacing with the operating
system, file system, networking, *et cetera*. Idris provides a
lightweight foreign function interface for achieving this, as part of
the prelude. For this, we assume a certain amount of knowledge of C and
the ``gcc`` compiler. First, we define a datatype which describes the
external types we can handle:
.. code-block:: idris
data FTy = FInt | FFloat | FChar | FString | FPtr | FUnit
Each of these corresponds directly to a C type. Respectively: ``int``,
``double``, ``char``, ``char*``, ``void*`` and ``void``. There is also a
translation to a concrete Idris type, described by the following
function:
.. code-block:: idris
interpFTy : FTy -> Type
interpFTy FInt = Int
interpFTy FFloat = Float
interpFTy FChar = Char
interpFTy FString = String
interpFTy FPtr = Ptr
interpFTy FUnit = ()
A foreign function is described by a list of input types and a return
type, which can then be converted to an Idris type:
.. code-block:: idris
ForeignTy : (xs:List FTy) -> (t:FTy) -> Type
A foreign function is assumed to be impure, so ``ForeignTy`` builds an
``IO`` type, for example:
.. code-block:: idris
Idris> ForeignTy [FInt, FString] FString
Int -> String -> IO String : Type
Idris> ForeignTy [FInt, FString] FUnit
Int -> String -> IO () : Type
We build a call to a foreign function by giving the name of the
function, a list of argument types and the return type. The built in
construct ``mkForeign`` converts this description to a function callable
by Idris:
.. code-block:: idris
data Foreign : Type -> Type where
FFun : String -> (xs:List FTy) -> (t:FTy) ->
Foreign (ForeignTy xs t)
mkForeign : Foreign x -> x
Note that the compiler expects ``mkForeign`` to be fully applied to
build a complete foreign function call. For example, the ``putStr``
function is implemented as follows, as a call to an external function
``putStr`` defined in the run-time system:
.. code-block:: idris
putStr : String -> IO ()
putStr x = mkForeign (FFun "putStr" [FString] FUnit) x
Include and linker directives
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Foreign function calls are translated directly to calls to C functions,
with appropriate conversion between the Idris representation of a
value and the C representation. Often this will require extra libraries
to be linked in, or extra header and object files. This is made possible
through the following directives:
- ``%lib target x`` — include the ``libx`` library. If the target is
``C`` this is equivalent to passing the ``-lx`` option to ``gcc``. If
the target is Java the library will be interpreted as a
``groupId:artifactId:packaging:version`` dependency coordinate for
maven.
- ``%include target x`` — use the header file or import ``x`` for the
given back end target.
- ``%link target x.o`` — link with the object file ``x.o`` when using
the given back end target.
- ``%dynamic x.so`` — dynamically link the interpreter with the shared
object ``x.so``.
Testing foreign function calls
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Normally, the Idris interpreter (used for typechecking and at the REPL)
will not perform IO actions. Additionally, as it neither generates C
code nor compiles to machine code, the ``%lib``, ``%include`` and
``%link`` directives have no effect. IO actions and FFI calls can be
tested using the special REPL command ``:x EXPR``, and C libraries can
be dynamically loaded in the interpreter by using the ``:dynamic``
command or the ``%dynamic`` directive. For example:
.. code-block:: idris
Idris> :dynamic libm.so
Idris> :x unsafePerformIO ((mkForeign (FFun "sin" [FFloat] FFloat)) 1.6)
0.9995736030415051 : Float
Type Providers
--------------
Idris type providers, inspired by F#s type providers, are a means of
making our types be “about” something in the world outside of Idris. For
example, given a type that represents a database schema and a query that
is checked against it, a type provider could read the schema of a real
database during type checking.
Idris type providers use the ordinary execution semantics of Idris to
run an IO action and extract the result. This result is then saved as a
constant in the compiled code. It can be a type, in which case it is
used like any other type, or it can be a value, in which case it can be
used as any other value, including as an index in types.
Type providers are still an experimental extension. To enable the
extension, use the ``%language`` directive:
.. code-block:: idris
%language TypeProviders
A provider ``p`` for some type ``t`` is simply an expression of type
``IO (Provider t)``. The ``%provide`` directive causes the type checker
to execute the action and bind the result to a name. This is perhaps
best illustrated with a simple example. The type provider ``fromFile``
reads a text file. If the file consists of the string ``Int``, then the
type ``Int`` will be provided. Otherwise, it will provide the type
``Nat``.
.. code-block:: idris
strToType : String -> Type
strToType "Int" = Int
strToType _ = Nat
fromFile : String -> IO (Provider Type)
fromFile fname = do str <- readFile fname
return (Provide (strToType (trim str)))
We then use the ``%provide`` directive:
.. code-block:: idris
%provide (T1 : Type) with fromFile "theType"
foo : T1
foo = 2
If the file named ``theType`` consists of the word ``Int``, then ``foo``
will be an ``Int``. Otherwise, it will be a ``Nat``. When Idris
encounters the directive, it first checks that the provider expression
``fromFile theType`` has type ``IO (Provider Type)``. Next, it executes
the provider. If the result is ``Provide t``, then ``T1`` is defined as
``t``. Otherwise, the result is an error.
Our datatype ``Provider t`` has the following definition:
.. code-block:: idris
data Provider a = Error String
| Provide a
We have already seen the ``Provide`` constructor. The ``Error``
constructor allows type providers to return useful error messages. The
example in this section was purposefully simple. More complex type
provider implementations, including a statically-checked SQLite binding,
are available in an external collection [1]_.
C Target
--------
The default target of Idris is C. Compiling via :
::
$ idris hello.idr -o hello
is equivalent to :
::
$ idris --codegen C hello.idr -o hello
When the command above is used, a temporary C source is generated, which
is then compiled into an executable named ``hello``.
In order to view the generated C code, compile via :
::
$ idris hello.idr -S -o hello.c
To turn optimisations on, use the ``%flag C`` pragma within the code, as
is shown below :
.. code-block:: idris
module Main
%flag C "-O3"
factorial : Int -> Int
factorial 0 = 1
factorial n = n * (factorial (n-1))
main : IO ()
main = do
putStrLn $ show $ factorial 3
JavaScript Target
-----------------
Idris is capable of producing *JavaScript* code that can be run in a
browser as well as in the *NodeJS* environment or alike. One can use the
FFI to communicate with the *JavaScript* ecosystem.
Code Generation
~~~~~~~~~~~~~~~
Code generation is split into two separate targets. To generate code
that is tailored for running in the browser issue the following command:
::
$ idris --codegen javascript hello.idr -o hello.js
The resulting file can be embedded into your HTML just like any other
*JavaScript* code.
Generating code for *NodeJS* is slightly different. Idris outputs a
*JavaScript* file that can be directly executed via ``node``.
::
$ idris --codegen node hello.idr -o hello
$ ./hello
Hello world
Take into consideration that the *JavaScript* code generator is using
``console.log`` to write text to ``stdout``, this means that it will
automatically add a newline to the end of each string. This behaviour
does not show up in the *NodeJS* code generator.
Using the FFI
~~~~~~~~~~~~~
To write a useful application we need to communicate with the outside
world. Maybe we want to manipulate the DOM or send an Ajax request. For
this task we can use the FFI. Since most *JavaScript* APIs demand
callbacks we need to extend the FFI so we can pass functions as
arguments.
The *JavaScript* FFI works a little bit differently than the regular
FFI. It uses positional arguments to directly insert our arguments into
a piece of *JavaScript* code.
One could use the primitive addition of *JavaScript* like so:
.. code-block:: idris
module Main
primPlus : Int -> Int -> IO Int
primPlus a b = mkForeign (FFun "%0 + %1" [FInt, FInt] FInt) a b
main : IO ()
main = do
a <- primPlus 1 1
b <- primPlus 1 2
print (a, b)
Notice that the ``%n`` notation qualifies the position of the ``n``-th
argument given to our foreign function starting from 0. When you need a
percent sign rather than a position simply use ``%%`` instead.
Passing functions to a foreign function is very similar. Lets assume
that we want to call the following function from the *JavaScript* world:
.. code-block:: idris
function twice(f, x) {
return f(f(x));
}
We obviously need to pass a function ``f`` here (we can infer it from
the way we use ``f`` in ``twice``, it would be more obvious if
*JavaScript* had types).
The *JavaScript* FFI is able to understand functions as arguments when
you give it something of type ``FFunction``. The following example code
calls ``twice`` in *JavaScript* and returns the result to our Idris
program:
.. code-block:: idris
module Main
twice : (Int -> Int) -> Int -> IO Int
twice f x = mkForeign (
FFun "twice(%0,%1)" [FFunction FInt FInt, FInt] FInt
) f x
main : IO ()
main = do
a <- twice (+1) 1
print a
The program outputs ``3``, just like we expected.
Including external *JavaScript* files
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Whenever one is working with *JavaScript* one might want to include
external libraries or just some functions that she or he wants to call
via FFI which are stored in external files. The *JavaScript* and
*NodeJS* code generators understand the ``%include`` directive. Keep in
mind that *JavaScript* and *NodeJS* are handled as different code
generators, therefore you will have to state which one you want to
target. This means that you can include different files for *JavaScript*
and *NodeJS* in the same Idris source file.
So whenever you want to add an external *JavaScript* file you can do
this like so:
For *NodeJS*:
.. code-block:: idris
%include Node "path/to/external.js"
And for use in the browser:
.. code-block:: idris
%include JavaScript "path/to/external.js"
The given files will be added to the top of the generated code.
Including *NodeJS* modules
~~~~~~~~~~~~~~~~~~~~~~~~~~
The *NodeJS* code generator can also include modules with the ``%lib``
directive.
.. code-block:: idris
%lib Node "fs"
This directive compiles into the following *JavaScript*
.. code-block:: javascript
var fs = require("fs");
Shrinking down generated *JavaScript*
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Idris can produce very big chunks of *JavaScript* code. However, the
generated code can be minified using the ``closure-compiler`` from
Google. Any other minifier is also suitable but ``closure-compiler``
offers advanced compilation that does some aggressive inlining and code
elimination. Idris can take full advantage of this compilation mode
and its highly recommended to use it when shipping a *JavaScript*
application written in Idris.
Cumulativity
------------
Since values can appear in types and *vice versa*, it is natural that
types themselves have types. For example:
::
*universe> :t Nat
Nat : Type
*universe> :t Vect
Vect : Nat -> Type -> Type
But what about the type of ``Type``? If we ask Idris it reports
::
*universe> :t Type
Type : Type 1
If ``Type`` were its own type, it would lead to an inconsistency due to
`Girards paradox <http://www.cs.cmu.edu/afs/cs.cmu.edu/user/kw/www/scans/girard72thesis.pdf>`_ , so internally there is a
*hierarchy* of types (or *universes*):
.. code-block:: idris
Type : Type 1 : Type 2 : Type 3 : ...
Universes are *cumulative*, that is, if ``x : Type n`` we can also have
that ``x : Type m``, as long as ``n < m``. The typechecker generates
such universe constraints and reports an error if any inconsistencies
are found. Ordinarily, a programmer does not need to worry about this,
but it does prevent (contrived) programs such as the following:
.. code-block:: idris
myid : (a : Type) -> a -> a
myid _ x = x
idid : (a : Type) -> a -> a
idid = myid _ myid
The application of ``myid`` to itself leads to a cycle in the universe
hierarchy — ``myid``\ s first argument is a ``Type``, which cannot be
at a lower level than required if it is applied to itself.
.. [1]
https://github.com/david-christiansen/idris-type-providers

219
docs/tutorial/modules.rst Normal file
View File

@ -0,0 +1,219 @@
.. _sect-namespaces:
=======================
Modules and Namespaces
=======================
An Idris program consists of a collection of modules. Each module
includes an optional ``module`` declaration giving the name of the
module, a list of ``import`` statements giving the other modules which
are to be imported, and a collection of declarations and definitions of
types, classes and functions. For example, the listing below gives a
module which defines a binary tree type ``BTree`` (in a file
``btree.idr``):
.. code-block:: idris
module btree
data BTree a = Leaf
| Node (BTree a) a (BTree a)
insert : Ord a => a -> BTree a -> BTree a
insert x Leaf = Node Leaf x Leaf
insert x (Node l v r) = if (x < v) then (Node (insert x l) v r)
else (Node l v (insert x r))
toList : BTree a -> List a
toList Leaf = []
toList (Node l v r) = btree.toList l ++ (v :: btree.toList r)
toTree : Ord a => List a -> BTree a
toTree [] = Leaf
toTree (x :: xs) = insert x (toTree xs)
Then, this gives a main program (in a file
``bmain.idr``) which uses the ``bst`` module to sort a list:
.. code-block:: idris
module Main
import btree
main : IO ()
main = do let t = toTree [1,8,2,7,9,3]
print (btree.toList t)
The same names can be defined in multiple modules. This is possible
because in practice names are *qualified* with the name of the module.
The names defined in the ``btree`` module are, in full:
+ ``btree.BTree``
+ ``btree.Leaf``
+ ``btree.Node``
+ ``btree.insert``
+ ``btree.toList``
+ ``btree.toTree``
If names are otherwise unambiguous, there is no need to give the fully
qualified name. Names can be disambiguated either by giving an explicit
qualification, or according to their type.
There is no formal link between the module name and its filename,
although it is generally advisable to use the same name for each. An
``import`` statement refers to a filename, using dots to separate
directories. For example, ``import foo.bar`` would import the file
``foo/bar.idr``, which would conventionally have the module declaration
``module foo.bar``. The only requirement for module names is that the
main module, with the ``main`` function, must be called
``Main``—although its filename need not be ``Main.idr``.
Export Modifiers
----------------
By default, all names defined in a module are exported for use by other
modules. However, it is good practice only to export a minimal interface
and keep internal details abstract. Idris allows functions, types,
and classes to be marked as: ``public``, ``abstract`` or ``private``:
- ``public`` means that both the name and definition are exported. For
functions, this means that the implementation is exported (which
means, for example, it can be used in a dependent type). For data
types, this means that the type name and the constructors are
exported. For classes, this means that the class name and method
names are exported.
- ``abstract`` means that only the name is exported. For functions,
this means that the implementation is not exported. For data types,
this means that the type name is exported but not the constructors.
For classes, this means that the class name is exported but not the
method names.
- ``private`` means that neither the name nor the definition is
exported.
.. note::
If any definition is given an export modifier, then all names with no modifier are assumed to be ``private``.
For our ``btree`` module, it makes sense for the tree data type and the
functions to be exported as ``abstract``, as we see below:
.. code-block:: idris
module btree
abstract data BTree a = Leaf
| Node (BTree a) a (BTree a)
abstract
insert : Ord a => a -> BTree a -> BTree a
insert x Leaf = Node Leaf x Leaf
insert x (Node l v r) = if (x < v) then (Node (insert x l) v r)
else (Node l v (insert x r))
abstract
toList : BTree a -> List a
toList Leaf = []
toList (Node l v r) = btree.toList l ++ (v :: btree.toList r)
abstract
toTree : Ord a => List a -> BTree a
toTree [] = Leaf
toTree (x :: xs) = insert x (toTree xs)
Finally, the default export mode can be changed with the ``%access``
directive, for example:
In this case, any function with no access modifier will be exported as
``abstract``, rather than left ``private``.
Additionally, a module can re-export a module it has imported, by using
the ``public`` modifier on an ``import``. For example:
.. code-block:: idris
module A
import B import public C
public a : AType a = ...
The module ``A`` will export the name ``a``, as well as any public or
abstract names in module ``C``, but will not re-export anything from
module ``B``.
Explicit Namespaces
-------------------
Defining a module also defines a namespace implicitly. However,
namespaces can also be given *explicitly*. This is most useful if you
wish to overload names within the same module:
.. code-block:: idris
module foo
namespace x
test : Int -> Int
test x = x * 2
namespace y
test : String -> String
test x = x ++ x
This (admittedly contrived) module defines two functions with fully
qualified names ``foo.x.test`` and ``foo.y.test``, which can be
disambiguated by their types:
::
*foo> test 3
6 : Int
*foo> test "foo"
"foofoo" : String
Parameterised blocks
--------------------
Groups of functions can be parameterised over a number of arguments
using a ``parameters`` declaration, for example:
.. code-block:: idris
parameters (x : Nat, y : Nat)
addAll : Nat -> Nat
addAll z = x + y + z
The effect of a ``parameters`` block is to add the declared parameters
to every function, type and data constructor within the block. Outside
the block, the parameters must be given explicitly:
::
*params> :t addAll
addAll : Nat -> Nat -> Nat -> Nat
Parameters blocks can be nested, and can also include data declarations,
in which case the parameters are added explicitly to all type and data
constructors. They may also be dependent types with implicit arguments:
.. code-block:: idris
parameters (y : Nat, xs : Vect x a)
data Vects : Type -> Type where
MkVects : Vect y a -> Vects a
append : Vects a -> Vect (x + y) a
append (MkVects ys) = xs ++ ys
To use ``Vects`` or ``append`` outside the block, we must also give the
``xs`` and ``y`` arguments. Here, we can use placeholders for the values
which can be inferred by the type checker:
::
*params> show (append _ _ (MkVects _ [1,2,3] [4,5,6]))
"[1, 2, 3, 4, 5, 6]" : String

View File

@ -0,0 +1,77 @@
.. _sect-packages:
========
Packages
========
Idris includes a simple system for building packages from a
package description file. These files can be used with the Idris
compiler to manage the development process of your Idris
programmes and packages.
Package Descriptions
--------------------
A package description includes the following:
+ A header, consisting of the keyword package followed by the package name.
+ Fields describing package contents, ``<field> = <value>``
At least one field must be the modules field, where the value is a
comma separated list of modules. For example, a library test which
has two modules ``foo.idr`` and ``bar.idr`` as source files would be
written as follows::
package foo
modules = foo, bar
Other examples of package files can be found in the ``libs`` directory
of the main Idris repository, and in `third-party libraries <https://github.com/idris-lang/Idris-dev/wiki/Libraries>`_.
Other common fields which may be present in an ``ipkg`` file are:
+ ``sourcedir = <dir>``, which gives the directory (relative to the
current directory) which contains the source. Default is the current
directory.
+ ``executable = <output>``, which gives the name of the executable
file to generate.
+ ``main = <module>``, which gives the name of the main module, and
must be present if the executable field is present.
+ ``opts = "<idris options>"``, which allows options (such as other
packages) to be passed to Idris.
In more advanced cases, particularly to support creating bindings to
external ``C`` libraries, the following options are available:
+ ``makefile = <file>``, which specifies a ``Makefile``, to be built
before the Idris modules, for example to support linking with a
``C`` library.
+ ``libs = <libs>``, which gives a comma separated list of libraries
which must be present for the package to be usable.
+ ``objs = <objs>``, which gives a comma separated list of additional
object files to be installed, perhaps generated by the ``Makefile``.
Using Package files
-------------------
Given an Idris package file ``text.ipkg`` it can be used with the Idris compiler as follows:
+ ``idris --build test.ipkg`` will build all modules in the package
+ ``idris --install test.ipkg`` will install the package, making it
accessible by other Idris libraries and programs.
+ ``idris --clean test.ipkg`` will delete all intermediate code and
executable files generated when building.
Once the test package has been installed, the command line option
``--package test`` makes it accessible (abbreviated to ``-p test``).
For example::
idris -p test Main.idr

View File

@ -0,0 +1,268 @@
.. _sect-provisional:
=======================
Provisional Definitions
=======================
Sometimes when programming with dependent types, the type required by
the type checker and the type of the program we have written will be
different (in that they do not have the same normal form), but
nevertheless provably equal. For example, recall the ``parity``
function:
.. code-block:: idris
data Parity : Nat -> Type where
Even : Parity (n + n)
Odd : Parity (S (n + n))
Wed like to implement this as follows:
.. code-block:: idris
parity : (n:Nat) -> Parity n
parity Z = Even {n=Z}
parity (S Z) = Odd {n=Z}
parity (S (S k)) with (parity k)
parity (S (S (j + j))) | Even = Even {n=S j}
parity (S (S (S (j + j)))) | Odd = Odd {n=S j}
This simply states that zero is even, one is odd, and recursively, the
parity of ``k+2`` is the same as the parity of ``k``. Explicitly marking
the value of ``n`` is even and odd is necessary to help type inference.
Unfortunately, the type checker rejects this:
::
viewsbroken.idr:12:10:When elaborating right hand side of ViewsBroken.parity:
Can't unify
Parity (plus (S j) (S j))
with
Parity (S (S (plus j j)))
Specifically:
Can't unify
plus (S j) (S j)
with
S (S (plus j j))
The type checker is telling us that ``(j+1)+(j+1)`` and ``2+j+j`` do not
normalise to the same value. This is because ``plus`` is defined by
recursion on its first argument, and in the second value, there is a
successor symbol on the second argument, so this will not help with
reduction. These values are obviously equal — how can we rewrite the
program to fix this problem?
Provisional definitions
-----------------------
*Provisional definitions* help with this problem by allowing us to defer
the proof details until a later point. There are two main reasons why
they are useful.
- When *prototyping*, it is useful to be able to test programs before
finishing all the details of proofs.
- When *reading* a program, it is often much clearer to defer the proof
details so that they do not distract the reader from the underlying
algorithm.
Provisional definitions are written in the same way as ordinary
definitions, except that they introduce the right hand side with a
``?=`` rather than ``=``. We define ``parity`` as follows:
When written in this form, instead of reporting a type error, Idris
will insert a metavariable standing for a theorem which will correct the
type error. Idris tells us we have two proof obligations, with names
generated from the module and function names:
.. code-block:: idris
*views> :m
Global metavariables:
[views.parity_lemma_2,views.parity_lemma_1]
The first of these has the following type:
.. code-block:: idris
*views> :p views.parity_lemma_1
---------------------------------- (views.parity_lemma_1) --------
{hole0} : (j : Nat) -> (Parity (plus (S j) (S j))) -> Parity (S (S (plus j j)))
-views.parity_lemma_1>
The two arguments are ``j``, the variable in scope from the pattern
match, and ``value``, which is the value we gave in the right hand side
of the provisional definition. Our goal is to rewrite the type so that
we can use this value. We can achieve this using the following theorem
from the prelude:
.. code-block:: idris
plusSuccRightSucc : (left : Nat) -> (right : Nat) ->
S (left + right) = left + (S right)
We need to use ``compute`` again to unfold the definition of ``plus``:
.. code-block:: idris
-views.parity_lemma_1> compute
---------------------------------- (views.parity_lemma_1) --------
{hole0} : (j : Nat) -> (Parity (S (plus j (S j)))) -> Parity (S (S (plus j j)))
After applying ``intros`` we have:
.. code-block:: idris
-views.parity_lemma_1> intros
j : Nat
value : Parity (S (plus j (S j)))
---------------------------------- (views.parity_lemma_1) --------
{hole2} : Parity (S (S (plus j j)))
Then we apply the ``plusSuccRightSucc`` rewrite rule, symmetrically, to
``j`` and ``j``, giving:
.. code-block:: idris
-views.parity_lemma_1> rewrite sym (plusSuccRightSucc j j)
j : Nat
value : Parity (S (plus j (S j)))
---------------------------------- (views.parity_lemma_1) --------
{hole3} : Parity (S (plus j (S j)))
``sym`` is a function, defined in the library, which reverses the order
of the rewrite:
.. code-block:: idris
sym : l = r -> r = l
sym Refl = Refl
We can complete this proof using the ``trivial`` tactic, which finds
``value`` in the premises. The proof of the second lemma proceeds in
exactly the same way.
We can now test the ``natToBin`` function from Section :ref:`sect-nattobin`
at the prompt. The number 42 is 101010 in binary. The binary digits are
reversed:
.. code-block:: idris
*views> show (natToBin 42)
"[False, True, False, True, False, True]" : String
Suspension of Disbelief
-----------------------
Idris requires that proofs be complete before compiling programs
(although evaluation at the prompt is possible without proof details).
Sometimes, especially when prototyping, it is easier not to have to do
this. It might even be beneficial to test programs before attempting to
prove things about them — if testing finds an error, you know you had
better not waste your time proving something!
Therefore, Idris provides a built-in coercion function, which allows
you to use a value of the incorrect types:
.. code-block:: idris
believe_me : a -> b
Obviously, this should be used with extreme caution. It is useful when
prototyping, and can also be appropriate when asserting properties of
external code (perhaps in an external C library). The “proof” of
``views.parity_lemma_1`` using this is:
.. code-block:: idris
views.parity_lemma_2 = proof {
intro;
intro;
exact believe_me value;
}
The ``exact`` tactic allows us to provide an exact value for the proof.
In this case, we assert that the value we gave was correct.
Example: Binary numbers
-----------------------
Previously, we implemented conversion to binary numbers using the
``Parity`` view. Here, we show how to use the same view to implement a
verified conversion to binary. We begin by indexing binary numbers over
their ``Nat`` equivalent. This is a common pattern, linking a
representation (in this case ``Binary``) with a meaning (in this case
``Nat``):
.. code-block:: idris
data Binary : Nat -> Type where
bEnd : Binary Z
bO : Binary n -> Binary (n + n)
bI : Binary n -> Binary (S (n + n))
``bO`` and ``bI`` take a binary number as an argument and effectively
shift it one bit left, adding either a zero or one as the new least
significant bit. The index, ``n + n`` or ``S (n + n)`` states the result
that this left shift then add will have to the meaning of the number.
This will result in a representation with the least significant bit at
the front.
Now a function which converts a Nat to binary will state, in the type,
that the resulting binary number is a faithful representation of the
original Nat:
.. code-block:: idris
natToBin : (n:Nat) -> Binary n
The ``Parity`` view makes the definition fairly simple — halving the
number is effectively a right shift after all — although we need to use
a provisional definition in the odd case:
.. code-block:: idris
natToBin : (n:Nat) -> Binary n
natToBin Z = bEnd
natToBin (S k) with (parity k)
natToBin (S (j + j)) | even = bI (natToBin j)
natToBin (S (S (j + j))) | odd ?= bO (natToBin (S j))
The problem with the odd case is the same as in the definition of
``parity``, and the proof proceeds in the same way:
.. code-block:: idris
natToBin_lemma_1 = proof {
intro;
intro;
rewrite sym (plusSuccRightSucc j j);
trivial;
}
To finish, well implement a main program which reads an integer from
the user and outputs it in binary.
.. code-block:: idris
main : IO ()
main = do putStr "Enter a number: "
x <- getLine
print (natToBin (fromInteger (cast x)))
For this to work, of course, we need a ``Show`` instance for
``Binary n``:
.. code-block:: idris
instance Show (Binary n) where
show (bO x) = show x ++ "0"
show (bI x) = show x ++ "1"
show bEnd = ""

View File

@ -0,0 +1,90 @@
.. _sect-starting:
===============
Getting Started
===============
Prerequisites
-------------
Before installing Idris, you will need to make sure you have all
of the necessary libraries and tools. You will need:
- A fairly recent Haskell platform. Version 2013.2.0.0 should be
sufficiently recent, though it is better to be completely up to
date.
- The *GNU Multiple Precision Arithmetic Library* (GMP) is available
from MacPorts/Homebrew and all major Linux distributions.
Downloading and Installing
--------------------------
The easiest way to install Idris, if you have all of the
prerequisites, is to type:
::
cabal update; cabal install idris
This will install the latest version released on Hackage, along with
any dependencies. If, however, you would like the most up to date
development version you can find it, as well as build intructions, on
GitHub at: https://github.com/idris-lang/Idris-dev.
To check that installation has succeeded, and to write your first
Idris program, create a file called “``hello.idr``” containing the
following text:
.. code-block:: idris
module Main
main : IO ()
main = putStrLn "Hello world"
If you are familiar with Haskell, it should be fairly clear what the
program is doing and how it works, but if not, we will explain the
details later. You can compile the program to an executable by
entering ``idris hello.idr -o hello`` at the shell prompt. This will
create an executable called ``hello``, which you can run:
::
$ idris hello.idr -o hello
$ ./hello
Hello world
Note that the ``$`` indicates the shell prompt! Should the Idris
executable not be found please ensure that you have added
``~/.cabal/bin`` to your ``$PATH`` environment variable. Mac OS X
users may find they need to use ``~/Library/Haskell/bin``
instead. Some useful options to the Idris command are:
- ``-o prog`` to compile to an executable called ``prog``.
- ``--check`` type check the file and its dependencies without
starting the interactive environment.
- ``--help`` display usage summary and command line options
The Interactive Environment
---------------------------
Entering ``idris`` at the shell prompt starts up the interactive
environment. You should see something like the following:
.. literalinclude:: ../listing/idris-prompt-start.txt
This gives a ``ghci`` style interface which allows evaluation of, as
well as type checking of, expressions; theorem proving, compilation;
editing; and various other operations. The command ``:?`` gives a list
of supported commands. Below, we see an example run in
which ``hello.idr`` is loaded, the type of ``main`` is checked and
then the program is compiled to the executable ``hello``. Type
checking a file, if successful, creates a bytecode version of the file
(in this case ``hello.ibc``) to speed up loading in future. The
bytecode is regenerated if the source file changes.
.. _run1:
.. literalinclude:: ../listing/idris-prompt-helloworld.txt

208
docs/tutorial/syntax.rst Normal file
View File

@ -0,0 +1,208 @@
Syntax Extensions
=================
supports the implementation of *Embedded Domain Specific Languages*
(EDSLs) in several ways [1]_. One way, as we have already seen, is
through extending ``do`` notation. Another important way is to allow
extension of the core syntax. In this section we describe two ways of
extending the syntax: ``syntax`` rules and ``dsl`` notation.
``syntax`` rules
----------------
We have seen ``if...then...else`` expressions, but these are not built
in. Instead, we can define a function in the prelude as follows (we
have already seen this function in Section :ref:`sect-lazy`):
.. code-block:: idris
boolCase : (x:Bool) -> Lazy a -> Lazy a -> a;
boolCase True t e = t;
boolCase False t e = e;
and then extend the core syntax with a ``syntax`` declaration:
.. code-block:: idris
syntax "if" [test] "then" [t] "else" [e] = boolCase test t e;
The left hand side of a ``syntax`` declaration describes the syntax
rule, and the right hand side describes its expansion. The syntax rule
itself consists of:
- **Keywords** — here, ``if``, ``then`` and ``else``, which must be
valid identifiers
- **Non-terminals** — included in square brackets, ``[test]``, ``[t]``
and ``[e]`` here, which stand for arbitrary expressions. To avoid
parsing ambiguities, these expressions cannot use syntax extensions
at the top level (though they can be used in parentheses).
- **Names** — included in braces, which stand for names which may be
bound on the right hand side.
- **Symbols** — included in quotations marks, e.g. ``:=``. This can
also be used to include reserved words in syntax rules, such as
``let`` or ``in``.
The limitations on the form of a syntax rule are that it must include
at least one symbol or keyword, and there must be no repeated
variables standing for non-terminals. Any expression can be used, but
if there are two non-terminals in a row in a rule, only simple
expressions may be used (that is, variables, constants, or bracketed
expressions). Rules can use previously defined rules, but may not be
recursive. The following syntax extensions would therefore be valid:
.. code-block:: idris
syntax [var] ":=" [val] = Assign var val;
syntax [test] "?" [t] ":" [e] = if test then t else e;
syntax "select" [x] "from" [t] "where" [w] = SelectWhere x t w;
syntax "select" [x] "from" [t] = Select x t;
Syntax macros can be further restricted to apply only in patterns (i.e.,
only on the left hand side of a pattern match clause) or only in terms
(i.e. everywhere but the left hand side of a pattern match clause) by
being marked as ``pattern`` or ``term`` syntax rules. For example, we
might define an interval as follows, with a static check that the lower
bound is below the upper bound using ``so``:
.. code-block:: idris
data Interval : Type where
MkInterval : (lower : Float) -> (upper : Float) ->
so (lower < upper) -> Interval
We can define a syntax which, in patterns, always matches ``oh`` for
the proof argument, and in terms requires a proof term to be provided:
.. code-block:: idris
pattern syntax "[" [x] "..." [y] "]" = MkInterval x y oh
term syntax "[" [x] "..." [y] "]" = MkInterval x y ?bounds_lemma
In terms, the syntax ``[x...y]`` will generate a proof obligation
``bounds_lemma`` (possibly renamed).
Finally, syntax rules may be used to introduce alternative binding
forms. For example, a ``for`` loop binds a variable on each iteration:
.. code-block:: idris
syntax "for" {x} "in" [xs] ":" [body] = forLoop xs (\x => body)
main : IO ()
main = do for x in [1..10]:
putStrLn ("Number " ++ show x)
putStrLn "Done!"
Note that we have used the ``{x}`` form to state that ``x`` represents
a bound variable, substituted on the right hand side. We have also put
``in`` in quotation marks since it is already a reserved word.
``dsl`` notation
----------------
The well-typed interpreter in Section :ref:`sect-interp` is a simple
example of a common programming pattern with dependent types. Namely:
describe an *object language* and its type system with dependent types
to guarantee that only well-typed programs can be represented, then
program using that representation. Using this approach we can, for
example, write programs for serialising binary data [2]_ or running
concurrent processes safely [3]_.
Unfortunately, the form of object language programs makes it rather
hard to program this way in practice. Recall the factorial program in
``Expr`` for example:
.. code-block:: idris
fact : Expr G (TyFun TyInt TyInt)
fact = Lam (If (Op (==) (Var Stop) (Val 0))
(Val 1) (Op (*) (app fact (Op (-) (Var Stop) (Val 1)))
(Var Stop)))
Since this is a particularly useful pattern, Idris provides syntax
overloading [1]_ to make it easier to program in such object
languages:
.. code-block:: idris
mkLam : TTName -> Expr (t::g) t' -> Expr g (TyFun t t')
mkLam _ body = Lam body
dsl expr
variable = Var
index_first = Stop
index_next = Pop
lambda = mkLam
A ``dsl`` block describes how each syntactic construct is represented
in an object language. Here, in the ``expr`` language, any variable is
translated to the ``Var`` constructor, using ``Pop`` and ``Stop`` to
construct the de Bruijn index (i.e., to count how many bindings since
the variable itself was bound); and any lambda is translated to a
``Lam`` constructor. The ``mkLam`` function simply ignores its first
argument, which is the name that the user chose for the variable. It
is also possible to overload ``let`` and dependent function syntax
(``pi``) in this way. We can now write ``fact`` as follows:
.. code-block:: idris
fact : Expr G (TyFun TyInt TyInt)
fact = expr (\x => If (Op (==) x (Val 0))
(Val 1) (Op (*) (app fact (Op (-) x (Val 1))) x))
In this new version, ``expr`` declares that the next expression will
be overloaded. We can take this further, using idiom brackets, by
declaring:
.. code-block:: idris
(<$>) : (f : Lazy (Expr G (TyFun a t))) -> Expr G a -> Expr G t
(<$>) f a = App f a
pure : Expr G a -> Expr G a
pure = id
Note that there is no need for these to be part of an instance of
``Applicative``, since idiom bracket notation translates directly to
the names ``<*>`` and ``pure``, and ad-hoc type-directed overloading
is allowed. We can now say:
.. code-block:: idris
fact : Expr G (TyFun TyInt TyInt)
fact = expr (\x => If (Op (==) x (Val 0))
(Val 1) (Op (*) [| fact (Op (-) x (Val 1)) |] x))
With some more ad-hoc overloading and type class instances, and a new
syntax rule, we can even go as far as:
.. code-block:: idris
syntax "IF" [x] "THEN" [t] "ELSE" [e] = If x t e
fact : Expr G (TyFun TyInt TyInt)
fact = expr (\x => IF x == 0 THEN 1 ELSE [| fact (x - 1) |] * x)
.. [1] Edwin Brady and Kevin Hammond. 2012. Resource-Safe systems
programming with embedded domain specific languages. In
Proceedings of the 14th international conference on Practical
Aspects of Declarative Languages (PADL'12), Claudio Russo and
Neng-Fa Zhou (Eds.). Springer-Verlag, Berlin, Heidelberg,
242-257. DOI=10.1007/978-3-642-27694-1_18
http://dx.doi.org/10.1007/978-3-642-27694-1_18
.. [2] Edwin C. Brady. 2011. IDRIS ---: systems programming meets full
dependent types. In Proceedings of the 5th ACM workshop on
Programming languages meets program verification (PLPV
'11). ACM, New York, NY, USA,
43-54. DOI=10.1145/1929529.1929536
http://doi.acm.org/10.1145/1929529.1929536
.. [3] Edwin Brady and Kevin Hammond. 2010. Correct-by-Construction
Concurrency: Using Dependent Types to Verify Implementations of
Effectful Resource Usage Protocols. Fundam. Inf. 102, 2 (April
2010), 145-176. http://dl.acm.org/citation.cfm?id=1883636

447
docs/tutorial/theorems.rst Normal file
View File

@ -0,0 +1,447 @@
.. _sect-theorems:
===============
Theorem Proving
===============
Equality
--------
Idris allows propositional equalities to be declared, allowing theorems about
programs to be stated and proved. Equality is built in, but conceptually
has the following definition:
.. code-block:: idris
data (=) : a -> b -> Type where
Refl : x = x
Equalities can be proposed between any values of any types, but the only
way to construct a proof of equality is if values actually are equal.
For example:
.. code-block:: idris
fiveIsFive : 5 = 5
fiveIsFive = Refl
twoPlusTwo : 2 + 2 = 4
twoPlusTwo = Refl
.. _sect-empty:
The Empty Type
--------------
There is an empty type, :math:`\bot`, which has no constructors. It is
therefore impossible to construct an element of the empty type, at least
without using a partially defined or general recursive function (see
Section :ref:`sect-totality` for more details). We can therefore use the
empty type to prove that something is impossible, for example zero is
never equal to a successor:
.. code-block:: idris
disjoint : (n : Nat) -> Z = S n -> Void
disjoint n p = replace {P = disjointTy} p ()
where
disjointTy : Nat -> Type
disjointTy Z = ()
disjointTy (S k) = Void
There is no need to worry too much about how this function works —
essentially, it applies the library function ``replace``, which uses an
equality proof to transform a predicate. Here we use it to transform a
value of a type which can exist, the empty tuple, to a value of a type
which cant, by using a proof of something which cant exist.
Once we have an element of the empty type, we can prove anything.
``void`` is defined in the library, to assist with proofs by
contradiction.
.. code-block:: idris
void : Void -> a
Simple Theorems
---------------
When type checking dependent types, the type itself gets *normalised*.
So imagine we want to prove the following theorem about the reduction
behaviour of ``plus``:
.. code-block:: idris
plusReduces : (n:Nat) -> plus Z n = n
Weve written down the statement of the theorem as a type, in just the
same way as we would write the type of a program. In fact there is no
real distinction between proofs and programs. A proof, as far as we are
concerned here, is merely a program with a precise enough type to
guarantee a particular property of interest.
We wont go into details here, but the Curry-Howard correspondence [1]_
explains this relationship. The proof itself is trivial, because
``plus Z n`` normalises to ``n`` by the definition of ``plus``:
.. code-block:: idris
plusReduces n = Refl
It is slightly harder if we try the arguments the other way, because
plus is defined by recursion on its first argument. The proof also works
by recursion on the first argument to ``plus``, namely ``n``.
.. code-block:: idris
plusReducesZ : (n:Nat) -> n = plus n Z
plusReducesZ Z = Refl
plusReducesZ (S k) = cong (plusReducesZ k)
``cong`` is a function defined in the library which states that equality
respects function application:
.. code-block:: idris
cong : {f : t -> u} -> a = b -> f a = f b
We can do the same for the reduction behaviour of plus on successors:
.. code-block:: idris
plusReducesS : (n:Nat) -> (m:Nat) -> S (plus n m) = plus n (S m)
plusReducesS Z m = Refl
plusReducesS (S k) m = cong (plusReducesS k m)
Even for trival theorems like these, the proofs are a little tricky to
construct in one go. When things get even slightly more complicated, it
becomes too much to think about to construct proofs in this batch
mode.
Idris provides interactive editing capabilities, which can help with
building proofs. For more details on building proofs interactively in
an editor, see :ref:`proofs-index`. In the rest of this section, we discuss
Idris's interactive proof mode.
Interactive theorem proving
---------------------------
Instead of writing the proof in one go, we can use Idriss interactive proof
mode. To do this, we write the general *structure* of the proof, and use
the interactive mode to complete the details. Well be constructing the
proof by *induction*, so we write the cases for ``Z`` and ``S``, with a
recursive call in the ``S`` case giving the inductive hypothesis, and
insert *metavariables* for the rest of the definition:
.. code-block:: idris
plusReducesZ' : (n:Nat) -> n = plus n Z
plusReducesZ' Z = ?plusredZ_Z
plusReducesZ' (S k) = let ih = plusReducesZ' k in
?plusredZ_S
On running , two global names are created, ``plusredZ_Z`` and
``plusredZ_S``, with no definition. We can use the ``:m`` command at the
prompt to find out which metavariables are still to be solved (or, more
precisely, which functions exist but have no definitions), then the
``:t`` command to see their types:
.. code-block:: idris
*theorems> :m
Global metavariables:
[plusredZ_S,plusredZ_Z]
.. code-block:: idris
*theorems> :t plusredZ_Z
plusredZ_Z : Z = plus Z Z
*theorems> :t plusredZ_S
plusredZ_S : (k : Nat) -> (k = plus k Z) -> S k = plus (S k) Z
The ``:p`` command enters interactive proof mode, which can be used to
complete the missing definitions.
.. code-block:: idris
*theorems> :p plusredZ_Z
---------------------------------- (plusredZ_Z) --------
{hole0} : Z = plus Z Z
This gives us a list of premises (above the line; there are none here)
and the current goal (below the line; named ``{hole0}`` here). At the
prompt we can enter tactics to direct the construction of the proof. In
this case, we can normalise the goal with the ``compute`` tactic:
.. code-block:: idris
-plusredZ_Z> compute
---------------------------------- (plusredZ_Z) --------
{hole0} : Z = Z
Now we have to prove that ``Z`` equals ``Z``, which is easy to prove by
``Refl``. To apply a function, such as ``Refl``, we use ``refine`` which
introduces subgoals for each of the functions explicit arguments
(``Refl`` has none):
.. code-block:: idris
-plusredZ_Z> refine Refl
plusredZ_Z: no more goals
Here, we could also have used the ``trivial`` tactic, which tries to
refine by ``Refl``, and if that fails, tries to refine by each name in
the local context. When a proof is complete, we use the ``qed`` tactic
to add the proof to the global context, and remove the metavariable from
the unsolved metavariables list. This also outputs a trace of the proof:
.. code-block:: idris
-plusredZ_Z> qed
plusredZ_Z = proof
compute
refine Refl
.. code-block:: idris
*theorems> :m
Global metavariables:
[plusredZ_S]
The ``:addproof`` command, at the interactive prompt, will add the proof
to the source file (effectively in an appendix). Let us now prove the
other required lemma, ``plusredZ_S``:
.. code-block:: idris
*theorems> :p plusredZ_S
---------------------------------- (plusredZ_S) --------
{hole0} : (k : Nat) -> (k = plus k Z) -> S k = plus (S k) Z
In this case, the goal is a function type, using ``k`` (the argument
accessible by pattern matching) and ``ih`` — the local variable
containing the result of the recursive call. We can introduce these as
premisses using the ``intro`` tactic twice (or ``intros``, which
introduces all arguments as premisses). This gives:
.. code-block:: idris
k : Nat
ih : k = plus k Z
---------------------------------- (plusredZ_S) --------
{hole2} : S k = plus (S k) Z
Since plus is defined by recursion on its first argument, the term
``plus (S k) Z`` in the goal can be simplified, so we use ``compute``.
.. code-block:: idris
k : Nat
ih : k = plus k Z
---------------------------------- (plusredZ_S) --------
{hole2} : S k = S (plus k Z)
We know, from the type of ``ih``, that ``k = plus k Z``, so we would
like to use this knowledge to replace ``plus k Z`` in the goal with
``k``. We can achieve this with the ``rewrite`` tactic:
.. code-block:: idris
-plusredZ_S> rewrite ih
k : Nat
ih : k = plus k Z
---------------------------------- (plusredZ_S) --------
{hole3} : S k = S k
-plusredZ_S>
The ``rewrite`` tactic takes an equality proof as an argument, and tries
to rewrite the goal using that proof. Here, it results in an equality
which is trivially provable:
.. code-block:: idris
-plusredZ_S> trivial
plusredZ_S: no more goals
-plusredZ_S> qed
plusredZ_S = proof {
intros;
rewrite ih;
trivial;
}
Again, we can add this proof to the end of our source file using the
``:addproof`` command at the interactive prompt.
.. _sect-totality:
Totality Checking
-----------------
If we really want to trust our proofs, it is important that they are
defined by *total* functions — that is, a function which is defined for
all possible inputs and is guaranteed to terminate. Otherwise we could
construct an element of the empty type, from which we could prove
anything:
.. code-block:: idris
-- making use of 'hd' being partially defined
empty1 : Void
empty1 = hd [] where
hd : List a -> a
hd (x :: xs) = x
-- not terminating
empty2 : Void
empty2 = empty2
Internally, Idris checks every definition for totality, and we can check at
the prompt with the ``:total`` command. We see that neither of the above
definitions is total:
::
*theorems> :total empty1
possibly not total due to: empty1#hd
not total as there are missing cases
*theorems> :total empty2
possibly not total due to recursive path empty2
Note the use of the word “possibly” — a totality check can, of course,
never be certain due to the undecidability of the halting problem. The
check is, therefore, conservative. It is also possible (and indeed
advisable, in the case of proofs) to mark functions as total so that it
will be a compile time error for the totality check to fail:
.. code-block:: idris
total empty2 : Void
empty2 = empty2
::
Type checking ./theorems.idr
theorems.idr:25:empty2 is possibly not total due to recursive path empty2
Reassuringly, our proof in Section :ref:`sect-empty` that the zero and
successor constructors are disjoint is total:
.. code-block:: idris
*theorems> :total disjoint
Total
The totality check is, necessarily, conservative. To be recorded as
total, a function ``f`` must:
- Cover all possible inputs
- Be *well-founded* — i.e. by the time a sequence of (possibly
mutually) recursive calls reaches ``f`` again, it must be possible to
show that one of its arguments has decreased.
- Not use any data types which are not *strictly positive*
- Not call any non-total functions
Directives and Compiler Flags for Totality
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
By default, Idris allows all well-typed definitions, whether total or not.
However, it is desirable for functions to be total as far as possible, as this
provides a guarantee that they provide a result for all possible inputs, in
finite time. It is possible to make total functions a requirement, either:
- By using the ``--total`` compiler flag.
- By adding a ``%default total`` directive to a source file. All
definitions after this will be required to be total, unless
explicitly flagged as ``partial``.
All functions *after* a ``%default total`` declaration are required to
be total. Correspondingly, after a ``%default partial`` declaration, the
requirement is relaxed.
Finally, the compiler flag ``--warnpartial`` causes to print a warning
for any undeclared partial function.
Totality checking issues
~~~~~~~~~~~~~~~~~~~~~~~~
Please note that the totality checker is not perfect! Firstly, it is
necessarily conservative due to the undecidability of the halting
problem, so many programs which *are* total will not be detected as
such. Secondly, the current implementation has had limited effort put
into it so far, so there may still be cases where it believes a function
is total which is not. Do not rely on it for your proofs yet!
Hints for totality
~~~~~~~~~~~~~~~~~~
In cases where you believe a program is total, but Idris does not agree, it is
possible to give hints to the checker to give more detail for a termination
argument. The checker works by ensuring that all chains of recursive calls
eventually lead to one of the arguments decreasing towards a base case, but
sometimes this is hard to spot. For example, the following definition cannot be
checked as ``total`` because the checker cannot decide that ``filter (<= x) xs``
will always be smaller than ``(x :: xs)``:
.. code-block:: idris
qsort : Ord a => List a -> List a
qsort [] = []
qsort (x :: xs)
= qsort (filter (< x) xs) ++
(x :: qsort (filter (>= x) xs))
The function ``assert_smaller``, defined in the Prelude, is intended to
address this problem:
.. code-block:: idris
assert_smaller : a -> a -> a
assert_smaller x y = y
It simply evaluates to its second argument, but also asserts to the
totality checker that ``y`` is structurally smaller than ``x``. This can
be used to explain the reasoning for totality if the checker cannot work
it out itself. The above example can now be written as:
.. code-block:: idris
total
qsort : Ord a => List a -> List a
qsort [] = []
qsort (x :: xs)
= qsort (assert_smaller (x :: xs) (filter (< x) xs)) ++
(x :: qsort (assert_smaller (x :: xs) (filter (>= x) xs)))
The expression ``assert_smaller (x :: xs) (filter (<= x) xs)`` asserts
that the result of the filter will always be smaller than the pattern
``(x :: xs)``.
In more extreme cases, the function ``assert_total`` marks a
subexpression as always being total:
.. code-block:: idris
assert_total : a -> a
assert_total x = x
In general, this function should be avoided, but it can be very useful
when reasoning about primitives or externally defined functions (for
example from a C library) where totality can be shown by an external
argument.
.. [1] Timothy G. Griffin. 1989. A formulae-as-type notion of
control. In Proceedings of the 17th ACM SIGPLAN-SIGACT
symposium on Principles of programming languages (POPL
'90). ACM, New York, NY, USA, 47-58. DOI=10.1145/96709.96714
http://doi.acm.org/10.1145/96709.96714

1048
docs/tutorial/typesfuns.rst Normal file

File diff suppressed because it is too large Load Diff

98
docs/tutorial/views.rst Normal file
View File

@ -0,0 +1,98 @@
.. _sec-views:
=============================
Views and the “``with``” rule
=============================
Dependent pattern matching
--------------------------
Since types can depend on values, the form of some arguments can be
determined by the value of others. For example, if we were to write
down the implicit length arguments to ``(++)``, wed see that the form
of the length argument was determined by whether the vector was empty
or not:
.. code-block:: idris
(++) : Vect n a -> Vect m a -> Vect (n + m) a
(++) {n=Z} [] ys = ys
(++) {n=S k} (x :: xs) ys = x :: xs ++ ys
If ``n`` was a successor in the ``[]`` case, or zero in the ``::``
case, the definition would not be well typed.
.. _sect-nattobin:
The ``with`` rule — matching intermediate values
------------------------------------------------
Very often, we need to match on the result of an intermediate
computation. Idris provides a construct for this, the ``with``
rule, inspired by views in ``Epigram`` [1]_, which takes account of
the fact that matching on a value in a dependently typed language can
affect what we know about the forms of other values. In its simplest
form, the ``with`` rule adds another argument to the function being
defined, e.g. we have already seen a vector filter function, defined
as follows:
.. code-block:: idris
filter : (a -> Bool) -> Vect n a -> (p ** Vect p a)
filter p [] = ( _ ** [] )
filter p (x :: xs) with (filter p xs)
| ( _ ** xs' ) = if (p x) then ( _ ** x :: xs' ) else ( _ ** xs' )
Here, the ``with`` clause allows us to deconstruct the result of
``filter p xs``. Effectively, it adds this value as an extra argument,
which we place after the vertical bar.
If the intermediate computation itself has a dependent type, then the
result can affect the forms of other arguments — we can learn the form
of one value by testing another. For example, a ``Nat`` is either even
or odd. If its even it will be the sum of two equal ``Nat``.
Otherwise, it is the sum of two equal ``Nat`` plus one:
.. code-block:: idris
data Parity : Nat -> Type where
Even : Parity (n + n)
Odd : Parity (S (n + n))
We say ``Parity`` is a *view* of ``Nat``. It has a *covering function*
which tests whether it is even or odd and constructs the predicate
accordingly.
.. code-block:: idris
parity : (n:Nat) -> Parity n
Well come back to the definition of ``parity`` shortly. We can use it
to write a function which converts a natural number to a list of
binary digits (least significant first) as follows, using the ``with``
rule:
.. code-block:: idris
natToBin : Nat -> List Bool
natToBin Z = Nil
natToBin k with (parity k)
natToBin (j + j) | Even = False :: natToBin j
natToBin (S (j + j)) | Odd = True :: natToBin j
The value of the result of ``parity k`` affects the form of ``k``,
because the result of ``parity k`` depends on ``k``. So, as well as
the patterns for the result of the intermediate computation (``Even``
and ``odd``) right of the ``\mid``, we also write how the results
affect the other patterns left of the :math:`\mid`. Note that there is
a function in the patterns (``+``) and repeated occurrences of
``j``—this is allowed because another argument has determined the form
of these patterns.
We will return to this function in Section :ref:`sect-provisional` to
complete the definition of ``parity``.
.. [1] Conor McBride and James McKinna. 2004. The view from the
left. J. Funct. Program. 14, 1 (January 2004),
69-111. DOI=10.1017/S0956796803004829
http://dx.doi.org/10.1017/S0956796803004829ñ

View File

@ -1,5 +1,5 @@
Name: idris
Version: 0.9.16
Version: 0.9.17.1
License: BSD3
License-file: LICENSE
Author: Edwin Brady
@ -99,19 +99,25 @@ Extra-source-files:
libs/base/base.ipkg
libs/base/*.idr
libs/base/Control/*.idr
libs/base/Control/Isomorphism/*.idr
libs/base/Control/Monad/*.idr
libs/base/Data/*.idr
libs/base/Data/Vect/*.idr
libs/base/Debug/*.idr
libs/base/Decidable/*.idr
libs/base/Language/Reflection/*.idr
libs/base/Makefile
libs/base/Network/*.idr
libs/base/System/*.idr
libs/base/System/Concurrency/*.idr
libs/base/Syntax/*.idr
libs/contrib/contrib.ipkg
libs/contrib/Makefile
libs/contrib/Classes/*.idr
libs/contrib/Control/*.idr
libs/contrib/Control/Isomorphism/*.idr
libs/contrib/Data/*.idr
libs/contrib/Decidable/*.idr
libs/contrib/Network/*.idr
libs/contrib/System/Concurrency/*.idr
libs/effects/Makefile
libs/effects/effects.ipkg
@ -292,6 +298,12 @@ Extra-source-files:
test/reg059/run
test/reg059/*.idr
test/reg059/expected
test/reg060/run
test/reg060/*.idr
test/reg060/expected
test/reg061/run
test/reg061/*.idr
test/reg061/expected
test/basic001/run
test/basic001/*.idr
@ -330,10 +342,9 @@ Extra-source-files:
test/basic012/run
test/basic012/*.idr
test/basic012/expected
test/buffer001-disabled/*.idr
test/buffer001-disabled/run
test/buffer001-disabled/expected
test/basic013/run
test/basic013/*.idr
test/basic013/expected
test/delab001/*.idr
test/delab001/run
@ -504,9 +515,22 @@ Extra-source-files:
test/proof004/run
test/proof004/*.idr
test/proof004/expected
test/proof005/run
test/proof005/*.idr
test/proof005/expected
test/proof006/run
test/proof006/*.idr
test/proof006/expected
test/proof007/run
test/proof007/*.idr
test/proof007/expected
test/proof008/run
test/proof008/*.idr
test/proof008/expected
test/proof009/run
test/proof009/*.idr
test/proof009/expected
test/proof009/input
test/quasiquote001/run
test/quasiquote001/*.idr
@ -622,7 +646,10 @@ Extra-source-files:
test/docs002/input
test/docs002/*.idr
test/docs002/expected
test/docs003/run
test/docs003/input
test/docs003/*.idr
test/docs003/expected
source-repository head
@ -810,6 +837,7 @@ Library
, vector-binary-instances < 0.3
, xml < 1.4
, zlib < 0.6
, safe
Extensions: MultiParamTypeClasses
, DeriveFoldable
, DeriveTraversable

View File

@ -1,28 +1,33 @@
build:
$(MAKE) -C prelude build
$(MAKE) -C base build
$(MAKE) -C contrib build
$(MAKE) -C effects build
install:
$(MAKE) -C prelude install
$(MAKE) -C base install
$(MAKE) -C contrib install
$(MAKE) -C effects install
clean:
$(MAKE) -C prelude clean
$(MAKE) -C base clean
$(MAKE) -C contrib clean
$(MAKE) -C effects clean
doc:
$(MAKE) -C prelude doc
$(MAKE) -C base doc
$(MAKE) -C effects doc
$(MAKE) -C prelude doc
$(MAKE) -C base doc
$(MAKE) -C contrib doc
$(MAKE) -C effects doc
doc_clean:
$(MAKE) -C prelude doc_clean
$(MAKE) -C base doc_clean
$(MAKE) -C effects doc_clean
$(MAKE) -C base doc_clean
$(MAKE) -C contrib doc_clean
$(MAKE) -C effects doc_clean
.PHONY: build install clean doc doc_clean

View File

@ -1,261 +0,0 @@
module Data.Buffer
import Data.Fin
%default total
-- !!! TODO: Open issues:
-- 1. It may be theoretically nice to represent Buffer size as
-- Fin (2 ^ WORD_BITS) instead of Nat
-- 2. Primitives take Bits64 when really they should take the
-- equivalent of C's size_t (ideally unboxed)
-- 3. If we had access to host system information, we could reduce
-- the needed primitives by implementing the LE/BE variants on
-- top of the native variant plus a possible swab function
-- 4. Would be nice to be able to peek/append Int, Char, and Float,
-- all have fixed (though possibly implementation-dependent) widths.
-- Currently not in place due to lack of host system introspection.
-- 5. Would be nice to be able to peek/append the vector types, but
-- for now I'm only touching the C backend which AFAICT doesn't
-- support them.
-- 6. Conversion from Fin to Bits64 (which, re 2, should eventually
-- be a fixed-width implementation-dependent type) is likely
-- inefficient relative to conversion from Nat to Bits64
-- 7. We may want to have a separate type that is a product of Buffer
-- and offset rather than storing the offset in Buffer itself, which
-- would require exposing the offset argument of prim__appendBuffer
||| A contiguous chunk of n bytes
abstract
record Buffer (n : Nat) where
constructor MkBuffer
offset : Nat
realBuffer : prim__UnsafeBuffer
{-
record Buffer : Nat -> Type where
MkBuffer : ( offset : Nat ) -> ( realBuffer : prim__UnsafeBuffer ) -> Buffer n
-}
bitsFromNat : Nat -> Bits64
bitsFromNat Z = 0
bitsFromNat (S k) = 1 + bitsFromNat k
bitsFromFin : Fin n -> Bits64
bitsFromFin FZ = 0
bitsFromFin (FS k) = 1 + bitsFromFin k
||| Allocate an empty Buffer. The size hint can be used to avoid
||| unnecessary reallocations and copies under the hood if the
||| approximate ultimate size of the Buffer is known. Users can assume
||| the new Buffer is word-aligned.
public
allocate : ( hint : Nat ) -> Buffer Z
allocate = MkBuffer Z . prim__allocate . bitsFromNat
||| Append count repetitions of a Buffer to another Buffer
%assert_total
public
appendBuffer : Buffer n ->
( count : Nat ) ->
Buffer m ->
Buffer ( n + count * m )
appendBuffer { n } { m } ( MkBuffer o1 r1 ) c ( MkBuffer o2 r2 ) =
MkBuffer o1 $ prim__appendBuffer r1 size1 count size2 off r2
where
size1 : Bits64
size1 = bitsFromNat ( n + o1 )
size2 : Bits64
size2 = bitsFromNat m
count : Bits64
count = bitsFromNat c
off : Bits64
off = bitsFromNat o2
||| Copy a buffer, potentially allowing the (potentially large) space it
||| pointed to to be freed
public
copy : Buffer n -> Buffer n
copy { n } = replace ( plusZeroRightNeutral n ) . appendBuffer ( allocate n ) 1
||| Create a view over a buffer
public
peekBuffer : { n : Nat } -> { offset : Nat } -> Buffer ( n + offset ) -> ( offset : Nat ) -> Buffer n
peekBuffer ( MkBuffer o r ) off = MkBuffer ( o + off ) r
peekBits : ( prim__UnsafeBuffer -> Bits64 -> a ) ->
Buffer ( m + n ) ->
( offset : Fin ( S n ) ) ->
a
peekBits prim ( MkBuffer o r ) = prim r . bitsFromNat . plus o . finToNat
appendBits : ( prim__UnsafeBuffer ->
Bits64 ->
Bits64 ->
a ->
prim__UnsafeBuffer ) ->
Buffer n ->
( count : Nat) ->
a ->
Buffer ( n + count * size )
appendBits { n } prim ( MkBuffer o r ) count =
MkBuffer o . prim r ( bitsFromNat $ n + o ) ( bitsFromNat count )
||| Read a Bits8 from a Buffer starting at offset
%assert_total
public
peekBits8 : Buffer ( 1 + n ) ->
( offset : Fin ( S n ) ) ->
Bits8
peekBits8 = peekBits { m = 1 } prim__peekB8Native
||| Append count repetitions of a Bits8 to a Buffer
%assert_total
public
appendBits8 : Buffer n ->
( count : Nat ) ->
Bits8 ->
Buffer ( n + count * 1 )
appendBits8 = appendBits prim__appendB8Native
||| Read a Bits16 in native byte order from a Buffer starting at offset
%assert_total
public
peekBits16Native : Buffer ( 2 + n ) ->
( offset : Fin ( S n ) ) ->
Bits16
peekBits16Native = peekBits { m = 2 } prim__peekB16Native
||| Read a little-endian Bits16 from a Buffer starting at offset
%assert_total
public
peekBits16LE : Buffer ( 2 + n ) -> ( offset : Fin ( S n ) ) -> Bits16
peekBits16LE = peekBits { m = 2 } prim__peekB16LE
||| Read a big-endian Bits16 from a Buffer starting at offset
%assert_total
public
peekBits16BE : Buffer ( 2 + n ) -> ( offset : Fin ( S n ) ) -> Bits16
peekBits16BE = peekBits { m = 2 } prim__peekB16BE
||| Append count repetitions of a Bits16 in native byte order to a Buffer
%assert_total
public
appendBits16Native : Buffer n ->
( count : Nat ) ->
Bits16 ->
Buffer ( n + count * 2 )
appendBits16Native = appendBits prim__appendB16Native
||| Append count repetitions of a little-endian Bits16 to a Buffer
%assert_total
public
appendBits16LE : Buffer n ->
( count : Nat ) ->
Bits16 ->
Buffer ( n + count * 2 )
appendBits16LE = appendBits prim__appendB16LE
||| Append count repetitions of a big-endian Bits16 to a Buffer
%assert_total
public
appendBits16BE : Buffer n ->
( count : Nat ) ->
Bits16 ->
Buffer ( n + count * 2 )
appendBits16BE = appendBits prim__appendB16BE
||| Read a Bits32 in native byte order from a Buffer starting at offset
%assert_total
public
peekBits32Native : Buffer ( 4 + n ) ->
( offset : Fin ( S n ) ) ->
Bits32
peekBits32Native = peekBits { m = 4 } prim__peekB32Native
||| Read a little-endian Bits32 from a Buffer starting at offset
%assert_total
public
peekBits32LE : Buffer ( 4 + n ) -> ( offset : Fin ( S n ) ) -> Bits32
peekBits32LE = peekBits { m = 4 } prim__peekB32LE
||| Read a big-endian Bits32 from a Buffer starting at offset
%assert_total
public
peekBits32BE : Buffer ( 4 + n ) -> ( offset : Fin ( S n ) ) -> Bits32
peekBits32BE = peekBits { m = 4 } prim__peekB32BE
||| Append count repetitions of a Bits32 in native byte order to a Buffer
%assert_total
public
appendBits32Native : Buffer n ->
( count : Nat ) ->
Bits32 ->
Buffer ( n + count * 4 )
appendBits32Native = appendBits prim__appendB32Native
||| Append count repetitions of a little-endian Bits32 to a Buffer
%assert_total
public
appendBits32LE : Buffer n ->
( count : Nat ) ->
Bits32 ->
Buffer ( n + count * 4 )
appendBits32LE = appendBits prim__appendB32LE
||| Append count repetitions of a big-endian Bits32 to a Buffer
%assert_total
public
appendBits32BE : Buffer n ->
( count : Nat ) ->
Bits32 ->
Buffer ( n + count * 4 )
appendBits32BE = appendBits prim__appendB32BE
||| Read a Bits64 in native byte order from a Buffer starting at offset
%assert_total
public
peekBits64Native : Buffer ( 8 + n ) ->
( offset : Fin ( S n ) ) ->
Bits64
peekBits64Native = peekBits { m = 8 } prim__peekB64Native
||| Read a little-endian Bits64 from a Buffer starting at offset
%assert_total
public
peekBits64LE : Buffer ( 8 + n ) -> ( offset : Fin ( S n ) ) -> Bits64
peekBits64LE = peekBits { m = 8 } prim__peekB64LE
||| Read a big-endian Bits64 from a Buffer starting at offset
%assert_total
public
peekBits64BE : Buffer ( 8 + n ) -> ( offset : Fin ( S n ) ) -> Bits64
peekBits64BE = peekBits { m = 8 } prim__peekB64BE
||| Append count repetitions of a Bits64 in native byte order to a Buffer
%assert_total
public
appendBits64Native : Buffer n ->
( count : Nat ) ->
Bits64 ->
Buffer ( n + count * 8 )
appendBits64Native = appendBits prim__appendB64Native
||| Append count repetitions of a little-endian Bits64 to a Buffer
%assert_total
public
appendBits64LE : Buffer n ->
( count : Nat ) ->
Bits64 ->
Buffer ( n + count * 8 )
appendBits64LE = appendBits prim__appendB64LE
||| Append count repetitions of a big-endian Bits64 to a Buffer
%assert_total
public
appendBits64BE : Buffer n ->
( count : Nat ) ->
Bits64 ->
Buffer ( n + count * 8 )
appendBits64BE = appendBits prim__appendB64BE

View File

@ -93,15 +93,8 @@ instance Show Const where
show (B16 b) = "(B16 ...)"
show (B32 b) = "(B32 ...)"
show (B64 b) = "(B64 ...)"
show (B8V xs) = "(B8V ...)"
show (B16V xs) = "(B16V ...)"
show (B32V xs) = "(B32V ...)"
show (B64V xs) = "(B64V ...)"
show (AType x) = "(AType ...)"
show StrType = "StrType"
show PtrType = "PtrType"
show ManagedPtrType = "ManagedPtrType"
show BufferType = "BufferType"
show VoidType = "VoidType"
show Forgot = "Forgot"
@ -117,7 +110,6 @@ instance Eq Reflection.IntTy where
ITNative == ITNative = True
ITBig == ITBig = True
ITChar == ITChar = True
(ITVec x i) == (ITVec y j) = x == y && i == j
_ == _ = False
instance Eq ArithTy where
@ -135,15 +127,8 @@ instance Eq Const where
(B16 x) == (B16 y) = x == y
(B32 x) == (B32 y) = x == y
(B64 x) == (B64 y) = x == y
(B8V xs) == (B8V ys) = False -- TODO
(B16V xs) == (B16V ys) = False -- TODO
(B32V xs) == (B32V ys) = False -- TODO
(B64V xs) == (B64V ys) = False -- TODO
(AType x) == (AType y) = x == y
StrType == StrType = True
PtrType == PtrType = True
ManagedPtrType == ManagedPtrType = True
BufferType == BufferType = True
VoidType == VoidType = True
Forgot == Forgot = True
_ == _ = False

View File

@ -3,15 +3,9 @@ package base
opts = "--nobasepkgs --total -i ../prelude"
modules = System,
Network.Cgi, Network.Socket,
Debug.Error, Debug.Trace,
System.Info,
System.Concurrency.Raw, System.Concurrency.Process,
Decidable.Decidable, Decidable.Order,
Providers,
Language.Reflection.Utils,
@ -19,20 +13,19 @@ modules = System,
Data.Morphisms,
Data.Bits, Data.Mod2,
Data.ZZ, Data.Sign,
Data.SortedMap, Data.SortedSet, Data.BoundedList,
Data.Fin, Data.Vect, Data.VectType,
Data.Fin, Data.Vect, Data.VectType,
Data.HVect, Data.Vect.Quantifiers,
Data.Floats, Data.Complex, Data.Heap, Data.Fun,
Data.Rel, Data.Buffer, Data.Erased,
Data.List, Data.Hash, Data.Matrix,
Data.Floats, Data.Complex,
Data.Erased, Data.List,
Data.So,
Control.Isomorphism, Control.Isomorphism.Primitives,
Control.Isomorphism,
Control.Monad.Identity,
Control.Monad.RWS,
Control.Monad.Trans,
Control.Monad.State, Control.Monad.Writer, Control.Monad.Reader,
Control.Category, Control.Arrow,
Control.Catchable, Control.IOExcept
Control.Catchable, Control.IOExcept,
System.Concurrency.Raw

View File

@ -0,0 +1,114 @@
module Classes.Verified
import Control.Algebra
-- Due to these being basically unused and difficult to implement,
-- they're in contrib for a bit. Once a design is found that lets them
-- be implemented for a number of instances, and we get those
-- implementations, then some of them can move back to base (or even
-- prelude, in the cases of Functor, Applicative, Monad, Semigroup,
-- and Monoid).
class Functor f => VerifiedFunctor (f : Type -> Type) where
functorIdentity : {a : Type} -> (x : f a) -> map id x = id x
functorComposition : {a : Type} -> {b : Type} -> (x : f a) ->
(g1 : a -> b) -> (g2 : b -> c) ->
map (g2 . g1) x = (map g2 . map g1) x
class (Applicative f, VerifiedFunctor f) => VerifiedApplicative (f : Type -> Type) where
applicativeMap : (x : f a) -> (g : a -> b) ->
map g x = pure g <*> x
applicativeIdentity : (x : f a) -> pure id <*> x = x
applicativeComposition : (x : f a) -> (g1 : f (a -> b)) -> (g2 : f (b -> c)) ->
((pure (.) <*> g2) <*> g1) <*> x = g2 <*> (g1 <*> x)
applicativeHomomorphism : (x : a) -> (g : a -> b) ->
(<*>) {f} (pure g) (pure x) = pure {f} (g x)
applicativeInterchange : (x : a) -> (g : f (a -> b)) ->
g <*> pure x = pure (\g' : a -> b => g' x) <*> g
class (Monad m, VerifiedApplicative m) => VerifiedMonad (m : Type -> Type) where
monadApplicative : (mf : m (a -> b)) -> (mx : m a) ->
mf <*> mx = mf >>= \f =>
mx >>= \x =>
pure (f x)
monadLeftIdentity : (x : a) -> (f : a -> m b) -> return x >>= f = f x
monadRightIdentity : (mx : m a) -> mx >>= return = mx
monadAssociativity : (mx : m a) -> (f : a -> m b) -> (g : b -> m c) ->
(mx >>= f) >>= g = mx >>= (\x => f x >>= g)
class Semigroup a => VerifiedSemigroup a where
total semigroupOpIsAssociative : (l, c, r : a) -> l <+> (c <+> r) = (l <+> c) <+> r
instance VerifiedSemigroup (List a) where
semigroupOpIsAssociative = appendAssociative
--instance VerifiedSemigroup Nat where
-- semigroupOpIsAssociative = plusAssociative
class (VerifiedSemigroup a, Monoid a) => VerifiedMonoid a where
total monoidNeutralIsNeutralL : (l : a) -> l <+> neutral = l
total monoidNeutralIsNeutralR : (r : a) -> neutral <+> r = r
-- instance VerifiedMonoid Nat where
-- monoidNeutralIsNeutralL = plusZeroRightNeutral
-- monoidNeutralIsNeutralR = plusZeroLeftNeutral
instance VerifiedMonoid (List a) where
monoidNeutralIsNeutralL = appendNilRightNeutral
monoidNeutralIsNeutralR xs = Refl
class (VerifiedMonoid a, Group a) => VerifiedGroup a where
total groupInverseIsInverseL : (l : a) -> l <+> inverse l = neutral
total groupInverseIsInverseR : (r : a) -> inverse r <+> r = neutral
class (VerifiedGroup a, AbelianGroup a) => VerifiedAbelianGroup a where
total abelianGroupOpIsCommutative : (l, r : a) -> l <+> r = r <+> l
class (VerifiedAbelianGroup a, Ring a) => VerifiedRing a where
total ringOpIsAssociative : (l, c, r : a) -> l <.> (c <.> r) = (l <.> c) <.> r
total ringOpIsDistributiveL : (l, c, r : a) -> l <.> (c <+> r) = (l <.> c) <+> (l <.> r)
total ringOpIsDistributiveR : (l, c, r : a) -> (l <+> c) <.> r = (l <.> r) <+> (c <.> r)
class (VerifiedRing a, RingWithUnity a) => VerifiedRingWithUnity a where
total ringWithUnityIsUnityL : (l : a) -> l <.> unity = l
total ringWithUnityIsUnityR : (r : a) -> unity <.> r = r
class JoinSemilattice a => VerifiedJoinSemilattice a where
total joinSemilatticeJoinIsAssociative : (l, c, r : a) -> join l (join c r) = join (join l c) r
total joinSemilatticeJoinIsCommutative : (l, r : a) -> join l r = join r l
total joinSemilatticeJoinIsIdempotent : (e : a) -> join e e = e
class MeetSemilattice a => VerifiedMeetSemilattice a where
total meetSemilatticeMeetIsAssociative : (l, c, r : a) -> meet l (meet c r) = meet (meet l c) r
total meetSemilatticeMeetIsCommutative : (l, r : a) -> meet l r = meet r l
total meetSemilatticeMeetIsIdempotent : (e : a) -> meet e e = e
class (VerifiedJoinSemilattice a, BoundedJoinSemilattice a) => VerifiedBoundedJoinSemilattice a where
total boundedJoinSemilatticeBottomIsBottom : (e : a) -> join e bottom = e
class (VerifiedMeetSemilattice a, BoundedMeetSemilattice a) => VerifiedBoundedMeetSemilattice a where
total boundedMeetSemilatticeTopIsTop : (e : a) -> meet e top = e
class (VerifiedJoinSemilattice a, VerifiedMeetSemilattice a) => VerifiedLattice a where
total latticeMeetAbsorbsJoin : (l, r : a) -> meet l (join l r) = l
total latticeJoinAbsorbsMeet : (l, r : a) -> join l (meet l r) = l
class (VerifiedBoundedJoinSemilattice a, VerifiedBoundedMeetSemilattice a, VerifiedLattice a) => VerifiedBoundedLattice a where { }
class (VerifiedRing a, Field a) => VerifiedField a where
total fieldInverseIsInverseL : (l : a) -> l <.> inverseM l = unity
total fieldInverseIsInverseR : (r : a) -> inverseM r <.> r = unity
-- class (VerifiedRingWithUnity a, VerifiedAbelianGroup b, Module a b) => VerifiedModule a b where
-- total moduleScalarMultiplyComposition : (x,y : a) -> (v : b) -> x <#> (y <#> v) = (x <.> y) <#> v
-- total moduleScalarUnityIsUnity : (v : b) -> unity <#> v = v
-- total moduleScalarMultDistributiveWRTVectorAddition : (s : a) -> (v, w : b) -> s <#> (v <+> w) = (s <#> v) <+> (s <#> w)
-- total moduleScalarMultDistributiveWRTModuleAddition : (s, t : a) -> (v : b) -> (s <+> t) <#> v = (s <#> v) <+> (t <#> v)
-- class (VerifiedField a, VerifiedModule a b) => VerifiedVectorSpace a b where {}

View File

@ -0,0 +1,274 @@
module Control.Algebra
import Data.Heap
-- XXX: change?
infixl 6 <->
infixl 6 <.>
infixl 5 <#>
||| Sets equipped with a single binary operation that is associative, along with
||| a neutral element for that binary operation and inverses for all elements.
||| Must satisfy the following laws:
||| + Associativity of `<+>`:
||| forall a b c, a <+> (b <+> c) == (a <+> b) <+> c
||| + Neutral for `<+>`:
||| forall a, a <+> neutral == a
||| forall a, neutral <+> a == a
||| + Inverse for `<+>`:
||| forall a, a <+> inverse a == neutral
||| forall a, inverse a <+> a == neutral
class Monoid a => Group a where
inverse : a -> a
(<->) : Group a => a -> a -> a
(<->) left right = left <+> (inverse right)
||| Sets equipped with a single binary operation that is associative and
||| commutative, along with a neutral element for that binary operation and
||| inverses for all elements. Must satisfy the following laws:
|||
||| + Associativity of `<+>`:
||| forall a b c, a <+> (b <+> c) == (a <+> b) <+> c
||| + Commutativity of `<+>`:
||| forall a b, a <+> b == b <+> a
||| + Neutral for `<+>`:
||| forall a, a <+> neutral == a
||| forall a, neutral <+> a == a
||| + Inverse for `<+>`:
||| forall a, a <+> inverse a == neutral
||| forall a, inverse a <+> a == neutral
class Group a => AbelianGroup a where { }
||| Sets equipped with two binary operations, one associative and commutative
||| supplied with a neutral element, and the other associative, with
||| distributivity laws relating the two operations. Must satisfy the following
||| laws:
|||
||| + Associativity of `<+>`:
||| forall a b c, a <+> (b <+> c) == (a <+> b) <+> c
||| + Commutativity of `<+>`:
||| forall a b, a <+> b == b <+> a
||| + Neutral for `<+>`:
||| forall a, a <+> neutral == a
||| forall a, neutral <+> a == a
||| + Inverse for `<+>`:
||| forall a, a <+> inverse a == neutral
||| forall a, inverse a <+> a == neutral
||| + Associativity of `<.>`:
||| forall a b c, a <.> (b <.> c) == (a <.> b) <.> c
||| + Distributivity of `<.>` and `<->`:
||| forall a b c, a <.> (b <+> c) == (a <.> b) <+> (a <.> c)
||| forall a b c, (a <+> b) <.> c == (a <.> c) <+> (b <.> c)
class AbelianGroup a => Ring a where
(<.>) : a -> a -> a
||| Sets equipped with two binary operations, one associative and commutative
||| supplied with a neutral element, and the other associative supplied with a
||| neutral element, with distributivity laws relating the two operations. Must
||| satisfy the following laws:
|||
||| + Associativity of `<+>`:
||| forall a b c, a <+> (b <+> c) == (a <+> b) <+> c
||| + Commutativity of `<+>`:
||| forall a b, a <+> b == b <+> a
||| + Neutral for `<+>`:
||| forall a, a <+> neutral == a
||| forall a, neutral <+> a == a
||| + Inverse for `<+>`:
||| forall a, a <+> inverse a == neutral
||| forall a, inverse a <+> a == neutral
||| + Associativity of `<.>`:
||| forall a b c, a <.> (b <.> c) == (a <.> b) <.> c
||| + Neutral for `<.>`:
||| forall a, a <.> unity == a
||| forall a, unity <.> a == a
||| + Distributivity of `<.>` and `<->`:
||| forall a b c, a <.> (b <+> c) == (a <.> b) <+> (a <.> c)
||| forall a b c, (a <+> b) <.> c == (a <.> c) <+> (b <.> c)
class Ring a => RingWithUnity a where
unity : a
||| Sets equipped with a binary operation that is commutative, associative and
||| idempotent. Must satisfy the following laws:
|||
||| + Associativity of join:
||| forall a b c, join a (join b c) == join (join a b) c
||| + Commutativity of join:
||| forall a b, join a b == join b a
||| + Idempotency of join:
||| forall a, join a a == a
|||
||| Join semilattices capture the notion of sets with a "least upper bound".
class JoinSemilattice a where
join : a -> a -> a
instance JoinSemilattice Nat where
join = maximum
instance Ord a => JoinSemilattice (MaxiphobicHeap a) where
join = merge
||| Sets equipped with a binary operation that is commutative, associative and
||| idempotent. Must satisfy the following laws:
|||
||| + Associativity of meet:
||| forall a b c, meet a (meet b c) == meet (meet a b) c
||| + Commutativity of meet:
||| forall a b, meet a b == meet b a
||| + Idempotency of meet:
||| forall a, meet a a == a
|||
||| Meet semilattices capture the notion of sets with a "greatest lower bound".
class MeetSemilattice a where
meet : a -> a -> a
instance MeetSemilattice Nat where
meet = minimum
||| Sets equipped with a binary operation that is commutative, associative and
||| idempotent and supplied with a unitary element. Must satisfy the following
||| laws:
|||
||| + Associativity of join:
||| forall a b c, join a (join b c) == join (join a b) c
||| + Commutativity of join:
||| forall a b, join a b == join b a
||| + Idempotency of join:
||| forall a, join a a == a
||| + Bottom (Unitary Element):
||| forall a, join a bottom == a
|||
||| Join semilattices capture the notion of sets with a "least upper bound"
||| equipped with a "bottom" element.
class JoinSemilattice a => BoundedJoinSemilattice a where
bottom : a
instance BoundedJoinSemilattice Nat where
bottom = Z
||| Sets equipped with a binary operation that is commutative, associative and
||| idempotent and supplied with a unitary element. Must satisfy the following
||| laws:
|||
||| + Associativity of meet:
||| forall a b c, meet a (meet b c) == meet (meet a b) c
||| + Commutativity of meet:
||| forall a b, meet a b == meet b a
||| + Idempotency of meet:
||| forall a, meet a a == a
||| + Top (Unitary Element):
||| forall a, meet a top == a
|||
||| Meet semilattices capture the notion of sets with a "greatest lower bound"
||| equipped with a "top" element.
class MeetSemilattice a => BoundedMeetSemilattice a where
top : a
||| Sets equipped with two binary operations that are both commutative,
||| associative and idempotent, along with absorbtion laws for relating the two
||| binary operations. Must satisfy the following:
|||
||| + Associativity of meet and join:
||| forall a b c, meet a (meet b c) == meet (meet a b) c
||| forall a b c, join a (join b c) == join (join a b) c
||| + Commutativity of meet and join:
||| forall a b, meet a b == meet b a
||| forall a b, join a b == join b a
||| + Idempotency of meet and join:
||| forall a, meet a a == a
||| forall a, join a a == a
||| + Absorbtion laws for meet and join:
||| forall a b, meet a (join a b) == a
||| forall a b, join a (meet a b) == a
class (JoinSemilattice a, MeetSemilattice a) => Lattice a where { }
instance Lattice Nat where { }
||| Sets equipped with two binary operations that are both commutative,
||| associative and idempotent and supplied with neutral elements, along with
||| absorbtion laws for relating the two binary operations. Must satisfy the
||| following:
|||
||| + Associativity of meet and join:
||| forall a b c, meet a (meet b c) == meet (meet a b) c
||| forall a b c, join a (join b c) == join (join a b) c
||| + Commutativity of meet and join:
||| forall a b, meet a b == meet b a
||| forall a b, join a b == join b a
||| + Idempotency of meet and join:
||| forall a, meet a a == a
||| forall a, join a a == a
||| + Absorbtion laws for meet and join:
||| forall a b, meet a (join a b) == a
||| forall a b, join a (meet a b) == a
||| + Neutral for meet and join:
||| forall a, meet a top == top
||| forall a, join a bottom == bottom
class (BoundedJoinSemilattice a, BoundedMeetSemilattice a) => BoundedLattice a where { }
-- Fields.
||| Sets equipped with two binary operations, both associative and commutative
||| supplied with a neutral element, with
||| distributivity laws relating the two operations. Must satisfy the following
||| laws:
|||
||| + Associativity of `<+>`:
||| forall a b c, a <+> (b <+> c) == (a <+> b) <+> c
||| + Commutativity of `<+>`:
||| forall a b, a <+> b == b <+> a
||| + Neutral for `<+>`:
||| forall a, a <+> neutral == a
||| forall a, neutral <+> a == a
||| + Inverse for `<+>`:
||| forall a, a <+> inverse a == neutral
||| forall a, inverse a <+> a == neutral
||| + Associativity of `<.>`:
||| forall a b c, a <.> (b <.> c) == (a <.> b) <.> c
||| + Unity for `<.>`:
||| forall a, a <.> unity == a
||| forall a, unity <.> a == a
||| + InverseM of `<.>`:
||| forall a, a <.> inverseM a == unity
||| forall a, inverseM a <.> a == unity
||| + Distributivity of `<.>` and `<->`:
||| forall a b c, a <.> (b <+> c) == (a <.> b) <+> (a <.> c)
||| forall a b c, (a <+> b) <.> c == (a <.> c) <+> (b <.> c)
class RingWithUnity a => Field a where
inverseM : a -> a
||| A module over a ring is an additive abelian group of 'vectors' endowed with a
||| scale operation multiplying vectors by ring elements, and distributivity laws
||| relating the scale operation to both ring addition and module addition.
||| Must satisfy the following laws:
|||
||| + Compatibility of scalar multiplication with ring multiplication:
||| forall a b v, a <#> (b <#> v) = (a <.> b) <#> v
||| + Ring unity is the identity element of scalar multiplication:
||| forall v, unity <#> v = v
||| + Distributivity of `<#>` and `<+>`:
||| forall a v w, a <#> (v <+> w) == (a <#> v) <+> (a <#> w)
||| forall a b v, (a <+> b) <#> v == (a <#> v) <+> (b <#> v)
class (RingWithUnity a, AbelianGroup b) => Module a b where
(<#>) : a -> b -> b
||| A vector space is a module over a ring that is also a field
class (Field a, Module a b) => VectorSpace a b where {}
-- XXX todo:
-- Structures where "abs" make sense.
-- Euclidean domains, etc.
-- Where to put fromInteger and fromRational?

View File

@ -0,0 +1,8 @@
module Data.Graph
import Data.Vect
data Edge e = MkEdge e e
data Graph : Nat > Nat > Type > Type > Type where
MkGraph : Vect m v > Vect n (Edge k) > Graph m n v k

View File

@ -5,6 +5,7 @@
module Data.Heap
%default total
%access public
@ -128,9 +129,6 @@ instance Ord a => Semigroup (MaxiphobicHeap a) where
instance Ord a => Monoid (MaxiphobicHeap a) where
neutral = empty
instance Ord a => JoinSemilattice (MaxiphobicHeap a) where
join = merge
--------------------------------------------------------------------------------
-- Properties
--------------------------------------------------------------------------------

View File

@ -1,5 +1,7 @@
module Data.Matrix
import Control.Algebra
import Data.Complex
import Data.ZZ
import Data.Fin
@ -212,14 +214,6 @@ instance Semigroup Nat where
instance Monoid Nat where
neutral = 0
instance VerifiedSemigroup Nat where
semigroupOpIsAssociative = plusAssociative
instance VerifiedMonoid Nat where
monoidNeutralIsNeutralL = plusZeroRightNeutral
monoidNeutralIsNeutralR = plusZeroLeftNeutral
instance Semigroup ZZ where
(<+>) = (+)

View File

@ -0,0 +1,9 @@
module Data.Polyhedra
import Data.Vect
import Data.Graph
data Face f = MkFace (Vect n f)
data Polyhedron : Nat -> Nat -> Nat -> Type -> Type where
MkPolyhedron : Vect k v -> Vect m (Edge v) -> Vect n (Face v) -> Polyhedron k m n v

View File

@ -0,0 +1,28 @@
module Data.SortedSet
import Data.SortedMap
-- TODO: add intersection, union, difference
data SortedSet k = SetWrapper (Data.SortedMap.SortedMap k ())
empty : SortedSet k
empty = SetWrapper Data.SortedMap.empty
insert : Ord k => k -> SortedSet k -> SortedSet k
insert k (SetWrapper m) = SetWrapper (Data.SortedMap.insert k () m)
delete : Ord k => k -> SortedSet k -> SortedSet k
delete k (SetWrapper m) = SetWrapper (Data.SortedMap.delete k m)
contains : Ord k => k -> SortedSet k -> Bool
contains k (SetWrapper m) = isJust (Data.SortedMap.lookup k m)
fromList : Ord k => List k -> SortedSet k
fromList l = SetWrapper (Data.SortedMap.fromList (map (\i => (i, ())) l))
toList : SortedSet k -> List k
toList (SetWrapper m) = map (\(i, _) => i) (Data.SortedMap.toList m)
instance Foldable SortedSet where
foldr f e xs = foldr f e (Data.SortedSet.toList xs)

24
libs/contrib/Makefile Normal file
View File

@ -0,0 +1,24 @@
IDRIS := idris
PKG := contrib
build:
$(IDRIS) --build ${PKG}.ipkg
install:
$(IDRIS) --install ${PKG}.ipkg
clean:
$(IDRIS) --clean ${PKG}.ipkg
rebuild: clean build
doc:
${IDRIS} --mkdoc ${PKG}.ipkg
doc_clean:
rm -rf ${PKG}_doc
linecount:
find . -name '*.idr' | xargs wc -l
.PHONY: build install clean rebuild linecount

18
libs/contrib/contrib.ipkg Normal file
View File

@ -0,0 +1,18 @@
package contrib
opts = "--nobasepkgs --total -i ../prelude -i ../base"
modules = Control.Algebra,
Control.Isomorphism.Primitives,
Classes.Verified,
Data.Fun, Data.Rel,
Data.Hash, Data.Matrix,
Data.ZZ, Data.Sign,
Data.BoundedList,
Data.Heap,
Data.SortedMap, Data.SortedSet,
Decidable.Decidable, Decidable.Order,
Network.Cgi, Network.Socket,
System.Concurrency.Process

View File

@ -117,9 +117,10 @@ FILE_IO t = MkEff t FileIO
||| @ m The file mode.
open : (fname : String)
-> (m : Mode)
-> { [FILE_IO ()] ==> [FILE_IO (if result
then OpenFile m
else ())] } Eff Bool
-> { [FILE_IO ()] ==> {result}
[FILE_IO (if result
then OpenFile m
else ())] } Eff Bool
open f m = call $ Open f m

View File

@ -149,3 +149,28 @@ believe_me x = prim__believe_me _ _ x
public %assert_total
really_believe_me : a -> b
really_believe_me x = prim__believe_me _ _ x
-- Deprecated - for backward compatibility
Float : Type
Float = Double
-- Pointers as external primitive; there's no literals for these, so no
-- need for them to be part of the compiler.
abstract data Ptr : Type
abstract data ManagedPtr : Type
%extern prim__readFile : prim__WorldType -> Ptr -> String
%extern prim__writeFile : prim__WorldType -> Ptr -> String -> Int
%extern prim__readString : prim__WorldType -> String
%extern prim__writeString : prim__WorldType -> String -> Int
%extern prim__vm : Ptr
%extern prim__stdin : Ptr
%extern prim__stdout : Ptr
%extern prim__stderr : Ptr
%extern prim__null : Ptr
%extern prim__registerPtr : Ptr -> Int -> ManagedPtr

View File

@ -181,15 +181,6 @@ instance DecEq Integer where
primitiveEq = believe_me (Refl {x})
postulate primitiveNotEq : x = y -> Void
--------------------------------------------------------------------------------
-- Float
--------------------------------------------------------------------------------
instance DecEq Float where
decEq x y = if x == y then Yes primitiveEq else No primitiveNotEq
where primitiveEq : x = y
primitiveEq = believe_me (Refl {x})
postulate primitiveNotEq : x = y -> Void
--------------------------------------------------------------------------------
-- String

View File

@ -51,8 +51,10 @@ applyEnv : (env : FEnv ffi xs) ->
applyEnv [] f = f
applyEnv (x@(_, _) :: xs) f = applyEnv xs (f x)
mkForeignPrim : ForeignPrimType xs env t
-- compiled as primitive
mkForeignPrim : {xs : _} -> {ffi : _} -> {env : FEnv ffi xs} -> {t : Type} ->
ForeignPrimType xs env t
-- compiled as primitive. Compiler assumes argument order, so make it
-- explicit here.
%inline
foreign_prim : (f : FFI) ->
@ -134,10 +136,6 @@ data C_IntTypes : Type -> Type where
C_IntBits16 : C_IntTypes Bits16
C_IntBits32 : C_IntTypes Bits32
C_IntBits64 : C_IntTypes Bits64
C_IntB8x16 : C_IntTypes Bits8x16
C_IntB16x8 : C_IntTypes Bits16x8
C_IntB32x4 : C_IntTypes Bits32x4
C_IntB64x2 : C_IntTypes Bits64x2
-- Supported C foreign types
data C_Types : Type -> Type where

View File

@ -38,17 +38,14 @@ data TTUExp =
data NativeTy = IT8 | IT16 | IT32 | IT64
data IntTy = ITFixed NativeTy | ITNative | ITBig | ITChar
| ITVec NativeTy Int
data ArithTy = ATInt Language.Reflection.IntTy | ATFloat
||| Primitive constants
data Const = I Int | BI Integer | Fl Float | Ch Char | Str String
| B8 Bits8 | B16 Bits16 | B32 Bits32 | B64 Bits64
| B8V Bits8x16 | B16V Bits16x8
| B32V Bits32x4 | B64V Bits64x2
| AType ArithTy | StrType
| PtrType | ManagedPtrType | BufferType | VoidType | Forgot
| VoidType | Forgot
| WorldType | TheWorld
%name Const c, c'
@ -361,38 +358,6 @@ instance Quotable Integer Raw where
quotedTy = `(Integer)
quote x = RConstant (BI x)
instance Quotable Bits8x16 TT where
quotedTy = `(Bits8x16)
quote x = TConst (B8V x)
instance Quotable Bits8x16 Raw where
quotedTy = `(Bits8x16)
quote x = RConstant (B8V x)
instance Quotable Bits16x8 TT where
quotedTy = `(Bits16x8)
quote x = TConst (B16V x)
instance Quotable Bits16x8 Raw where
quotedTy = `(Bits16x8)
quote x = RConstant (B16V x)
instance Quotable Bits32x4 TT where
quotedTy = `(Bits32x4)
quote x = TConst (B32V x)
instance Quotable Bits32x4 Raw where
quotedTy = `(Bits32x4)
quote x = RConstant (B32V x)
instance Quotable Bits64x2 TT where
quotedTy = `(Bits64x2)
quote x = TConst (B64V x)
instance Quotable Bits64x2 Raw where
quotedTy = `(Bits64x2)
quote x = RConstant (B64V x)
instance Quotable String TT where
quotedTy = `(String)
quote x = TConst (Str x)
@ -459,7 +424,6 @@ instance Quotable Reflection.IntTy TT where
quote ITNative = `(Reflection.ITNative)
quote ITBig = `(ITBig)
quote ITChar = `(Reflection.ITChar)
quote (ITVec x y) = `(ITVec ~(quote x) ~(quote y))
instance Quotable Reflection.IntTy Raw where
quotedTy = `(Reflection.IntTy)
@ -467,7 +431,6 @@ instance Quotable Reflection.IntTy Raw where
quote ITNative = `(Reflection.ITNative)
quote ITBig = `(ITBig)
quote ITChar = `(Reflection.ITChar)
quote (ITVec x y) = `(ITVec ~(quote {t=Raw} x) ~(quote {t=Raw} y))
instance Quotable ArithTy TT where
quotedTy = `(ArithTy)
@ -490,15 +453,8 @@ instance Quotable Const TT where
quote (B16 x) = `(B16 ~(quote x))
quote (B32 x) = `(B32 ~(quote x))
quote (B64 x) = `(B64 ~(quote x))
quote (B8V xs) = `(B8V ~(quote xs))
quote (B16V xs) = `(B16V ~(quote xs))
quote (B32V xs) = `(B32V ~(quote xs))
quote (B64V xs) = `(B64V ~(quote xs))
quote (AType x) = `(AType ~(quote x))
quote StrType = `(StrType)
quote PtrType = `(PtrType)
quote ManagedPtrType = `(ManagedPtrType)
quote BufferType = `(BufferType)
quote VoidType = `(VoidType)
quote Forgot = `(Forgot)
quote WorldType = `(WorldType)
@ -515,15 +471,8 @@ instance Quotable Const Raw where
quote (B16 x) = `(B16 ~(quote {t=Raw} x))
quote (B32 x) = `(B32 ~(quote {t=Raw} x))
quote (B64 x) = `(B64 ~(quote {t=Raw} x))
quote (B8V xs) = `(B8V ~(quote {t=Raw} xs))
quote (B16V xs) = `(B16V ~(quote {t=Raw} xs))
quote (B32V xs) = `(B32V ~(quote {t=Raw} xs))
quote (B64V xs) = `(B64V ~(quote {t=Raw} xs))
quote (AType x) = `(AType ~(quote {t=Raw} x))
quote StrType = `(StrType)
quote PtrType = `(PtrType)
quote ManagedPtrType = `(ManagedPtrType)
quote BufferType = `(BufferType)
quote VoidType = `(VoidType)
quote Forgot = `(Forgot)
quote WorldType = `(WorldType)

View File

@ -23,7 +23,7 @@ import public Prelude.Bits
import public Prelude.Uninhabited
import public Prelude.Pairs
import public Prelude.Stream
import public Prelude.Providers
import public Decidable.Equality
import public Language.Reflection
import public Language.Reflection.Errors
@ -115,106 +115,6 @@ instance Show Bits32 where
instance Show Bits64 where
show b = b64ToString b
%assert_total
viewB8x16 : Bits8x16 -> (Bits8, Bits8, Bits8, Bits8, Bits8, Bits8, Bits8, Bits8, Bits8, Bits8, Bits8, Bits8, Bits8, Bits8, Bits8, Bits8)
viewB8x16 x = ( prim__indexB8x16 x (prim__truncBigInt_B32 0)
, prim__indexB8x16 x (prim__truncBigInt_B32 1)
, prim__indexB8x16 x (prim__truncBigInt_B32 2)
, prim__indexB8x16 x (prim__truncBigInt_B32 3)
, prim__indexB8x16 x (prim__truncBigInt_B32 4)
, prim__indexB8x16 x (prim__truncBigInt_B32 5)
, prim__indexB8x16 x (prim__truncBigInt_B32 6)
, prim__indexB8x16 x (prim__truncBigInt_B32 7)
, prim__indexB8x16 x (prim__truncBigInt_B32 8)
, prim__indexB8x16 x (prim__truncBigInt_B32 9)
, prim__indexB8x16 x (prim__truncBigInt_B32 10)
, prim__indexB8x16 x (prim__truncBigInt_B32 11)
, prim__indexB8x16 x (prim__truncBigInt_B32 12)
, prim__indexB8x16 x (prim__truncBigInt_B32 13)
, prim__indexB8x16 x (prim__truncBigInt_B32 14)
, prim__indexB8x16 x (prim__truncBigInt_B32 15)
)
instance Show Bits8x16 where
show x =
case viewB8x16 x of
(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p) =>
"<" ++ prim__toStrB8 a
++ ", " ++ prim__toStrB8 b
++ ", " ++ prim__toStrB8 c
++ ", " ++ prim__toStrB8 d
++ ", " ++ prim__toStrB8 e
++ ", " ++ prim__toStrB8 f
++ ", " ++ prim__toStrB8 g
++ ", " ++ prim__toStrB8 h
++ ", " ++ prim__toStrB8 i
++ ", " ++ prim__toStrB8 j
++ ", " ++ prim__toStrB8 k
++ ", " ++ prim__toStrB8 l
++ ", " ++ prim__toStrB8 m
++ ", " ++ prim__toStrB8 n
++ ", " ++ prim__toStrB8 o
++ ", " ++ prim__toStrB8 p
++ ">"
%assert_total
viewB16x8 : Bits16x8 -> (Bits16, Bits16, Bits16, Bits16, Bits16, Bits16, Bits16, Bits16)
viewB16x8 x = ( prim__indexB16x8 x (prim__truncBigInt_B32 0)
, prim__indexB16x8 x (prim__truncBigInt_B32 1)
, prim__indexB16x8 x (prim__truncBigInt_B32 2)
, prim__indexB16x8 x (prim__truncBigInt_B32 3)
, prim__indexB16x8 x (prim__truncBigInt_B32 4)
, prim__indexB16x8 x (prim__truncBigInt_B32 5)
, prim__indexB16x8 x (prim__truncBigInt_B32 6)
, prim__indexB16x8 x (prim__truncBigInt_B32 7)
)
instance Show Bits16x8 where
show x =
case viewB16x8 x of
(a, b, c, d, e, f, g, h) =>
"<" ++ prim__toStrB16 a
++ ", " ++ prim__toStrB16 b
++ ", " ++ prim__toStrB16 c
++ ", " ++ prim__toStrB16 d
++ ", " ++ prim__toStrB16 e
++ ", " ++ prim__toStrB16 f
++ ", " ++ prim__toStrB16 g
++ ", " ++ prim__toStrB16 h
++ ">"
%assert_total
viewB32x4 : Bits32x4 -> (Bits32, Bits32, Bits32, Bits32)
viewB32x4 x = ( prim__indexB32x4 x (prim__truncBigInt_B32 0)
, prim__indexB32x4 x (prim__truncBigInt_B32 1)
, prim__indexB32x4 x (prim__truncBigInt_B32 2)
, prim__indexB32x4 x (prim__truncBigInt_B32 3)
)
instance Show Bits32x4 where
show x =
case viewB32x4 x of
(a, b, c, d) =>
"<" ++ prim__toStrB32 a
++ ", " ++ prim__toStrB32 b
++ ", " ++ prim__toStrB32 c
++ ", " ++ prim__toStrB32 d
++ ">"
%assert_total
viewB64x2 : Bits64x2 -> (Bits64, Bits64)
viewB64x2 x = ( prim__indexB64x2 x (prim__truncBigInt_B32 0)
, prim__indexB64x2 x (prim__truncBigInt_B32 1)
)
instance Show Bits64x2 where
show x =
case viewB64x2 x of
(a, b) =>
"<" ++ prim__toStrB64 a
++ ", " ++ prim__toStrB64 b
++ ">"
instance (Show a, Show b) => Show (a, b) where
show (x, y) = "(" ++ show x ++ ", " ++ show y ++ ")"
@ -419,18 +319,6 @@ curry f a b = f (a, b)
uncurry : (a -> b -> c) -> (a, b) -> c
uncurry f (a, b) = f a b
uniformB8x16 : Bits8 -> Bits8x16
uniformB8x16 x = prim__mkB8x16 x x x x x x x x x x x x x x x x
uniformB16x8 : Bits16 -> Bits16x8
uniformB16x8 x = prim__mkB16x8 x x x x x x x x
uniformB32x4 : Bits32 -> Bits32x4
uniformB32x4 x = prim__mkB32x4 x x x x
uniformB64x2 : Bits64 -> Bits64x2
uniformB64x2 x = prim__mkB64x2 x x
---- some basic io
||| Output a string to stdout without a trailing newline

View File

@ -22,8 +22,6 @@ infixl 5 <#>
class Semigroup a where
(<+>) : a -> a -> a
class Semigroup a => VerifiedSemigroup a where
total semigroupOpIsAssociative : (l, c, r : a) -> l <+> (c <+> r) = (l <+> c) <+> r
||| Sets equipped with a single binary operation that is associative, along with
||| a neutral element for that binary operation. Must satisfy the following
@ -37,295 +35,3 @@ class Semigroup a => VerifiedSemigroup a where
class Semigroup a => Monoid a where
neutral : a
class (VerifiedSemigroup a, Monoid a) => VerifiedMonoid a where
total monoidNeutralIsNeutralL : (l : a) -> l <+> neutral = l
total monoidNeutralIsNeutralR : (r : a) -> neutral <+> r = r
||| Sets equipped with a single binary operation that is associative, along with
||| a neutral element for that binary operation and inverses for all elements.
||| Must satisfy the following laws:
||| + Associativity of `<+>`:
||| forall a b c, a <+> (b <+> c) == (a <+> b) <+> c
||| + Neutral for `<+>`:
||| forall a, a <+> neutral == a
||| forall a, neutral <+> a == a
||| + Inverse for `<+>`:
||| forall a, a <+> inverse a == neutral
||| forall a, inverse a <+> a == neutral
class Monoid a => Group a where
inverse : a -> a
class (VerifiedMonoid a, Group a) => VerifiedGroup a where
total groupInverseIsInverseL : (l : a) -> l <+> inverse l = neutral
total groupInverseIsInverseR : (r : a) -> inverse r <+> r = neutral
(<->) : Group a => a -> a -> a
(<->) left right = left <+> (inverse right)
||| Sets equipped with a single binary operation that is associative and
||| commutative, along with a neutral element for that binary operation and
||| inverses for all elements. Must satisfy the following laws:
|||
||| + Associativity of `<+>`:
||| forall a b c, a <+> (b <+> c) == (a <+> b) <+> c
||| + Commutativity of `<+>`:
||| forall a b, a <+> b == b <+> a
||| + Neutral for `<+>`:
||| forall a, a <+> neutral == a
||| forall a, neutral <+> a == a
||| + Inverse for `<+>`:
||| forall a, a <+> inverse a == neutral
||| forall a, inverse a <+> a == neutral
class Group a => AbelianGroup a where { }
class (VerifiedGroup a, AbelianGroup a) => VerifiedAbelianGroup a where
total abelianGroupOpIsCommutative : (l, r : a) -> l <+> r = r <+> l
||| Sets equipped with two binary operations, one associative and commutative
||| supplied with a neutral element, and the other associative, with
||| distributivity laws relating the two operations. Must satisfy the following
||| laws:
|||
||| + Associativity of `<+>`:
||| forall a b c, a <+> (b <+> c) == (a <+> b) <+> c
||| + Commutativity of `<+>`:
||| forall a b, a <+> b == b <+> a
||| + Neutral for `<+>`:
||| forall a, a <+> neutral == a
||| forall a, neutral <+> a == a
||| + Inverse for `<+>`:
||| forall a, a <+> inverse a == neutral
||| forall a, inverse a <+> a == neutral
||| + Associativity of `<.>`:
||| forall a b c, a <.> (b <.> c) == (a <.> b) <.> c
||| + Distributivity of `<.>` and `<->`:
||| forall a b c, a <.> (b <+> c) == (a <.> b) <+> (a <.> c)
||| forall a b c, (a <+> b) <.> c == (a <.> c) <+> (b <.> c)
class AbelianGroup a => Ring a where
(<.>) : a -> a -> a
class (VerifiedAbelianGroup a, Ring a) => VerifiedRing a where
total ringOpIsAssociative : (l, c, r : a) -> l <.> (c <.> r) = (l <.> c) <.> r
total ringOpIsDistributiveL : (l, c, r : a) -> l <.> (c <+> r) = (l <.> c) <+> (l <.> r)
total ringOpIsDistributiveR : (l, c, r : a) -> (l <+> c) <.> r = (l <.> r) <+> (c <.> r)
||| Sets equipped with two binary operations, one associative and commutative
||| supplied with a neutral element, and the other associative supplied with a
||| neutral element, with distributivity laws relating the two operations. Must
||| satisfy the following laws:
|||
||| + Associativity of `<+>`:
||| forall a b c, a <+> (b <+> c) == (a <+> b) <+> c
||| + Commutativity of `<+>`:
||| forall a b, a <+> b == b <+> a
||| + Neutral for `<+>`:
||| forall a, a <+> neutral == a
||| forall a, neutral <+> a == a
||| + Inverse for `<+>`:
||| forall a, a <+> inverse a == neutral
||| forall a, inverse a <+> a == neutral
||| + Associativity of `<.>`:
||| forall a b c, a <.> (b <.> c) == (a <.> b) <.> c
||| + Neutral for `<.>`:
||| forall a, a <.> unity == a
||| forall a, unity <.> a == a
||| + Distributivity of `<.>` and `<->`:
||| forall a b c, a <.> (b <+> c) == (a <.> b) <+> (a <.> c)
||| forall a b c, (a <+> b) <.> c == (a <.> c) <+> (b <.> c)
class Ring a => RingWithUnity a where
unity : a
class (VerifiedRing a, RingWithUnity a) => VerifiedRingWithUnity a where
total ringWithUnityIsUnityL : (l : a) -> l <.> unity = l
total ringWithUnityIsUnityR : (r : a) -> unity <.> r = r
||| Sets equipped with a binary operation that is commutative, associative and
||| idempotent. Must satisfy the following laws:
|||
||| + Associativity of join:
||| forall a b c, join a (join b c) == join (join a b) c
||| + Commutativity of join:
||| forall a b, join a b == join b a
||| + Idempotency of join:
||| forall a, join a a == a
|||
||| Join semilattices capture the notion of sets with a "least upper bound".
class JoinSemilattice a where
join : a -> a -> a
class JoinSemilattice a => VerifiedJoinSemilattice a where
total joinSemilatticeJoinIsAssociative : (l, c, r : a) -> join l (join c r) = join (join l c) r
total joinSemilatticeJoinIsCommutative : (l, r : a) -> join l r = join r l
total joinSemilatticeJoinIsIdempotent : (e : a) -> join e e = e
||| Sets equipped with a binary operation that is commutative, associative and
||| idempotent. Must satisfy the following laws:
|||
||| + Associativity of meet:
||| forall a b c, meet a (meet b c) == meet (meet a b) c
||| + Commutativity of meet:
||| forall a b, meet a b == meet b a
||| + Idempotency of meet:
||| forall a, meet a a == a
|||
||| Meet semilattices capture the notion of sets with a "greatest lower bound".
class MeetSemilattice a where
meet : a -> a -> a
class MeetSemilattice a => VerifiedMeetSemilattice a where
total meetSemilatticeMeetIsAssociative : (l, c, r : a) -> meet l (meet c r) = meet (meet l c) r
total meetSemilatticeMeetIsCommutative : (l, r : a) -> meet l r = meet r l
total meetSemilatticeMeetIsIdempotent : (e : a) -> meet e e = e
||| Sets equipped with a binary operation that is commutative, associative and
||| idempotent and supplied with a unitary element. Must satisfy the following
||| laws:
|||
||| + Associativity of join:
||| forall a b c, join a (join b c) == join (join a b) c
||| + Commutativity of join:
||| forall a b, join a b == join b a
||| + Idempotency of join:
||| forall a, join a a == a
||| + Bottom (Unitary Element):
||| forall a, join a bottom == a
|||
||| Join semilattices capture the notion of sets with a "least upper bound"
||| equipped with a "bottom" element.
class JoinSemilattice a => BoundedJoinSemilattice a where
bottom : a
class (VerifiedJoinSemilattice a, BoundedJoinSemilattice a) => VerifiedBoundedJoinSemilattice a where
total boundedJoinSemilatticeBottomIsBottom : (e : a) -> join e bottom = e
||| Sets equipped with a binary operation that is commutative, associative and
||| idempotent and supplied with a unitary element. Must satisfy the following
||| laws:
|||
||| + Associativity of meet:
||| forall a b c, meet a (meet b c) == meet (meet a b) c
||| + Commutativity of meet:
||| forall a b, meet a b == meet b a
||| + Idempotency of meet:
||| forall a, meet a a == a
||| + Top (Unitary Element):
||| forall a, meet a top == a
|||
||| Meet semilattices capture the notion of sets with a "greatest lower bound"
||| equipped with a "top" element.
class MeetSemilattice a => BoundedMeetSemilattice a where
top : a
class (VerifiedMeetSemilattice a, BoundedMeetSemilattice a) => VerifiedBoundedMeetSemilattice a where
total boundedMeetSemilatticeTopIsTop : (e : a) -> meet e top = e
||| Sets equipped with two binary operations that are both commutative,
||| associative and idempotent, along with absorbtion laws for relating the two
||| binary operations. Must satisfy the following:
|||
||| + Associativity of meet and join:
||| forall a b c, meet a (meet b c) == meet (meet a b) c
||| forall a b c, join a (join b c) == join (join a b) c
||| + Commutativity of meet and join:
||| forall a b, meet a b == meet b a
||| forall a b, join a b == join b a
||| + Idempotency of meet and join:
||| forall a, meet a a == a
||| forall a, join a a == a
||| + Absorbtion laws for meet and join:
||| forall a b, meet a (join a b) == a
||| forall a b, join a (meet a b) == a
class (JoinSemilattice a, MeetSemilattice a) => Lattice a where { }
class (VerifiedJoinSemilattice a, VerifiedMeetSemilattice a) => VerifiedLattice a where
total latticeMeetAbsorbsJoin : (l, r : a) -> meet l (join l r) = l
total latticeJoinAbsorbsMeet : (l, r : a) -> join l (meet l r) = l
||| Sets equipped with two binary operations that are both commutative,
||| associative and idempotent and supplied with neutral elements, along with
||| absorbtion laws for relating the two binary operations. Must satisfy the
||| following:
|||
||| + Associativity of meet and join:
||| forall a b c, meet a (meet b c) == meet (meet a b) c
||| forall a b c, join a (join b c) == join (join a b) c
||| + Commutativity of meet and join:
||| forall a b, meet a b == meet b a
||| forall a b, join a b == join b a
||| + Idempotency of meet and join:
||| forall a, meet a a == a
||| forall a, join a a == a
||| + Absorbtion laws for meet and join:
||| forall a b, meet a (join a b) == a
||| forall a b, join a (meet a b) == a
||| + Neutral for meet and join:
||| forall a, meet a top == top
||| forall a, join a bottom == bottom
class (BoundedJoinSemilattice a, BoundedMeetSemilattice a) => BoundedLattice a where { }
class (VerifiedBoundedJoinSemilattice a, VerifiedBoundedMeetSemilattice a, VerifiedLattice a) => VerifiedBoundedLattice a where { }
-- Fields.
||| Sets equipped with two binary operations, both associative and commutative
||| supplied with a neutral element, with
||| distributivity laws relating the two operations. Must satisfy the following
||| laws:
|||
||| + Associativity of `<+>`:
||| forall a b c, a <+> (b <+> c) == (a <+> b) <+> c
||| + Commutativity of `<+>`:
||| forall a b, a <+> b == b <+> a
||| + Neutral for `<+>`:
||| forall a, a <+> neutral == a
||| forall a, neutral <+> a == a
||| + Inverse for `<+>`:
||| forall a, a <+> inverse a == neutral
||| forall a, inverse a <+> a == neutral
||| + Associativity of `<.>`:
||| forall a b c, a <.> (b <.> c) == (a <.> b) <.> c
||| + Unity for `<.>`:
||| forall a, a <.> unity == a
||| forall a, unity <.> a == a
||| + InverseM of `<.>`:
||| forall a, a <.> inverseM a == unity
||| forall a, inverseM a <.> a == unity
||| + Distributivity of `<.>` and `<->`:
||| forall a b c, a <.> (b <+> c) == (a <.> b) <+> (a <.> c)
||| forall a b c, (a <+> b) <.> c == (a <.> c) <+> (b <.> c)
class RingWithUnity a => Field a where
inverseM : a -> a
class (VerifiedRing a, Field a) => VerifiedField a where
total fieldInverseIsInverseL : (l : a) -> l <.> inverseM l = unity
total fieldInverseIsInverseR : (r : a) -> inverseM r <.> r = unity
||| A module over a ring is an additive abelian group of 'vectors' endowed with a
||| scale operation multiplying vectors by ring elements, and distributivity laws
||| relating the scale operation to both ring addition and module addition.
||| Must satisfy the following laws:
|||
||| + Compatibility of scalar multiplication with ring multiplication:
||| forall a b v, a <#> (b <#> v) = (a <.> b) <#> v
||| + Ring unity is the identity element of scalar multiplication:
||| forall v, unity <#> v = v
||| + Distributivity of `<#>` and `<+>`:
||| forall a v w, a <#> (v <+> w) == (a <#> v) <+> (a <#> w)
||| forall a b v, (a <+> b) <#> v == (a <#> v) <+> (b <#> v)
class (RingWithUnity a, AbelianGroup b) => Module a b where
(<#>) : a -> b -> b
class (VerifiedRingWithUnity a, VerifiedAbelianGroup b, Module a b) => VerifiedModule a b where
total moduleScalarMultiplyComposition : (x,y : a) -> (v : b) -> x <#> (y <#> v) = (x <.> y) <#> v
total moduleScalarUnityIsUnity : (v : b) -> unity {a} <#> v = v
total moduleScalarMultDistributiveWRTVectorAddition : (s : a) -> (v, w : b) -> s <#> (v <+> w) = (s <#> v) <+> (s <#> w)
total moduleScalarMultDistributiveWRTModuleAddition : (s, t : a) -> (v : b) -> (s <+> t) <#> v = (s <#> v) <+> (t <#> v)
||| A vector space is a module over a ring that is also a field
class (Field a, Module a b) => VectorSpace a b where {}
class (VerifiedField a, VerifiedModule a b) => VerifiedVectorSpace a b where {}
-- XXX todo:
-- Structures where "abs" make sense.
-- Euclidean domains, etc.
-- Where to put fromInteger and fromRational?

View File

@ -15,17 +15,6 @@ class Functor f => Applicative (f : Type -> Type) where
pure : a -> f a
(<*>) : f (a -> b) -> f a -> f b
class (Applicative f, VerifiedFunctor f) => VerifiedApplicative (f : Type -> Type) where
applicativeMap : (x : f a) -> (g : a -> b) ->
map g x = pure g <*> x
applicativeIdentity : (x : f a) -> pure id <*> x = x
applicativeComposition : (x : f a) -> (g1 : f (a -> b)) -> (g2 : f (b -> c)) ->
((pure (.) <*> g2) <*> g1) <*> x = g2 <*> (g1 <*> x)
applicativeHomomorphism : (x : a) -> (g : a -> b) ->
(<*>) {f} (pure g) (pure x) = pure {f} (g x)
applicativeInterchange : (x : a) -> (g : f (a -> b)) ->
g <*> pure x = pure (\g' : a -> b => g' x) <*> g
infixl 2 <*
(<*) : Applicative f => f a -> f b -> f a
a <* b = map const a <*> b

View File

@ -1,5 +1,7 @@
module Prelude.Cast
import public Builtins
||| Type class for transforming a instance of a data type to another type.
class Cast from to where
||| Perform a cast operation.

View File

@ -18,9 +18,3 @@ infixl 4 <$>
(<$>) : Functor f => (m : a -> b) -> f a -> f b
m <$> x = map m x
class Functor f => VerifiedFunctor (f : Type -> Type) where
functorIdentity : {a : Type} -> (x : f a) -> map id x = id x
functorComposition : {a : Type} -> {b : Type} -> (x : f a) ->
(g1 : a -> b) -> (g2 : b -> c) ->
map (g2 . g1) x = (map g2 . map g1) x

View File

@ -809,12 +809,6 @@ hasAnyByNilFalse p (x::xs) =
hasAnyNilFalse : Eq a => (l : List a) -> hasAny [] l = False
hasAnyNilFalse l = ?hasAnyNilFalseBody
instance VerifiedSemigroup (List a) where
semigroupOpIsAssociative = appendAssociative
instance VerifiedMonoid (List a) where
monoidNeutralIsNeutralL = appendNilRightNeutral
monoidNeutralIsNeutralR xs = Refl
--------------------------------------------------------------------------------
-- Proofs

View File

@ -14,16 +14,6 @@ infixl 5 >>=
class Applicative m => Monad (m : Type -> Type) where
(>>=) : m a -> (a -> m b) -> m b
class (Monad m, VerifiedApplicative m) => VerifiedMonad (m : Type -> Type) where
monadApplicative : (mf : m (a -> b)) -> (mx : m a) ->
mf <*> mx = mf >>= \f =>
mx >>= \x =>
pure (f x)
monadLeftIdentity : (x : a) -> (f : a -> m b) -> return x >>= f = f x
monadRightIdentity : (mx : m a) -> mx >>= return = mx
monadAssociativity : (mx : m a) -> (f : a -> m b) -> (g : b -> m c) ->
(mx >>= f) >>= g = mx >>= (\x => f x >>= g)
||| Also called `join` or mu
flatten : Monad m => m (m a) -> m a
flatten a = a >>= id

View File

@ -243,18 +243,6 @@ instance Monoid Multiplicative where
instance Monoid Additive where
neutral = getAdditive Z
instance MeetSemilattice Nat where
meet = minimum
instance JoinSemilattice Nat where
join = maximum
instance Lattice Nat where { }
instance BoundedJoinSemilattice Nat where
bottom = Z
||| Casts negative `Ints` to 0.
instance Cast Int Nat where
cast i = fromInteger (cast i)

View File

@ -1,4 +1,8 @@
module Providers
module Prelude.Providers
import Prelude.Functor
import Prelude.Applicative
import Prelude.Monad
||| Type providers must build one of these in an IO computation.
public

View File

@ -8,7 +8,7 @@ modules = Builtins, Prelude, IO,
Prelude.Maybe, Prelude.Monad, Prelude.Applicative, Prelude.Either,
Prelude.Strings, Prelude.Chars, Prelude.Functor,
Prelude.Foldable, Prelude.Traversable, Prelude.Bits, Prelude.Stream,
Prelude.Uninhabited, Prelude.Pairs,
Prelude.Uninhabited, Prelude.Pairs, Prelude.Providers,
Language.Reflection, Language.Reflection.Errors, Language.Reflection.Tactical,

View File

@ -10,6 +10,15 @@ git tag v$VERSION -a
cabal sdist
# Generate Idris library docs and put them in lib_docs.tar.gz in the root
make lib_doc
DOCDIR=`mktemp -d /tmp/docsXXXXX`
cp -r libs/base/base_doc "$DOCDIR"
cp -r libs/prelude/prelude_doc "$DOCDIR"
cp -r libs/effects/effects_doc "$DOCDIR"
cp -r libs/contrib/contrib_doc "$DOCDIR"
tar -czvf lib_docs.tar.gz -C "$DOCDIR" prelude_doc base_doc effects_doc contrib_doc
cabal configure --prefix=/usr/local
cabal build
cabal copy --destdir=/tmp/idris-pkg/

View File

@ -1,9 +1,10 @@
include ../config.mk
OBJS = idris_rts.o idris_heap.o idris_gc.o idris_gmp.o idris_bitstring.o \
idris_opts.o idris_stats.o mini-gmp.o
idris_opts.o idris_stats.o idris_utf8.o mini-gmp.o
HDRS = idris_rts.h idris_heap.h idris_gc.h idris_gmp.h idris_bitstring.h \
idris_opts.h idris_stats.h mini-gmp.h idris_stdfgn.h idris_net.h
idris_opts.h idris_stats.h mini-gmp.h idris_stdfgn.h idris_net.h \
idris_utf8.h
CFLAGS:=-fPIC $(CFLAGS)
CFLAGS += $(GMP_INCLUDE_DIR) $(GMP) -DIDRIS_TARGET_OS="\"$(OS)\""
CFLAGS += -DIDRIS_TARGET_TRIPLE="\"$(MACHINE)\""

View File

@ -822,71 +822,3 @@ VAL idris_b64T32(VM *vm, VAL a) {
return cl;
}
// SSE vectors
VAL idris_IDXB8x16(VM* vm, VAL vec, VAL idx) {
__m128i sse = *vec->info.bits128p;
uint8_t data[16];
_mm_storeu_si128((__m128i*)&data, sse);
return MKB8(vm, data[idx->info.bits32]);
}
VAL idris_IDXB16x8(VM* vm, VAL vec, VAL idx) {
__m128i sse = *vec->info.bits128p;
uint16_t data[8];
_mm_storeu_si128((__m128i*)&data, sse);
return MKB16(vm, data[idx->info.bits32]);
}
VAL idris_IDXB32x4(VM* vm, VAL vec, VAL idx) {
__m128i sse = *vec->info.bits128p;
uint32_t data[4];
_mm_storeu_si128((__m128i*)&data, sse);
return MKB32(vm, data[idx->info.bits32]);
}
VAL idris_IDXB64x2(VM* vm, VAL vec, VAL idx) {
__m128i sse = *vec->info.bits128p;
uint64_t data[2];
_mm_storeu_si128((__m128i*)&data, sse);
return MKB64(vm, data[idx->info.bits32]);
}
VAL idris_b8x16CopyForGC(VM *vm, VAL vec) {
__m128i sse = *vec->info.bits128p;
VAL cl = allocate(sizeof(Closure) + 16 + sizeof(__m128i), 1);
SETTY(cl, BITS8X16);
cl->info.bits128p = (__m128i*)ALIGN((uintptr_t)cl + sizeof(Closure), 16);
assert ((uintptr_t)cl->info.bits128p % 16 == 0);
*cl->info.bits128p = sse;
return cl;
}
VAL idris_b16x8CopyForGC(VM *vm, VAL vec) {
__m128i sse = *vec->info.bits128p;
VAL cl = allocate(sizeof(Closure) + 16 + sizeof(__m128i), 1);
SETTY(cl, BITS16X8);
cl->info.bits128p = (__m128i*)ALIGN((uintptr_t)cl + sizeof(Closure), 16);
assert ((uintptr_t)cl->info.bits128p % 16 == 0);
*cl->info.bits128p = sse;
return cl;
}
VAL idris_b32x4CopyForGC(VM *vm, VAL vec) {
__m128i sse = *vec->info.bits128p;
VAL cl = allocate(sizeof(Closure) + 16 + sizeof(__m128i), 1);
SETTY(cl, BITS32X4);
cl->info.bits128p = (__m128i*)ALIGN((uintptr_t)cl + sizeof(Closure), 16);
assert ((uintptr_t)cl->info.bits128p % 16 == 0);
*cl->info.bits128p = sse;
return cl;
}
VAL idris_b64x2CopyForGC(VM *vm, VAL vec) {
__m128i sse = *vec->info.bits128p;
VAL cl = allocate(sizeof(Closure) + 16 + sizeof(__m128i), 1);
SETTY(cl, BITS64X2);
cl->info.bits128p = (__m128i*)ALIGN((uintptr_t)cl + sizeof(Closure), 16);
assert ((uintptr_t)cl->info.bits128p % 16 == 0);
*cl->info.bits128p = sse;
return cl;
}

View File

@ -126,14 +126,4 @@ VAL idris_b64T8(VM *vm, VAL a);
VAL idris_b64T16(VM *vm, VAL a);
VAL idris_b64T32(VM *vm, VAL a);
VAL idris_IDXB8x16(VM* vm, VAL vec, VAL idx);
VAL idris_IDXB16x8(VM* vm, VAL vec, VAL idx);
VAL idris_IDXB32x4(VM* vm, VAL vec, VAL idx);
VAL idris_IDXB64x2(VM* vm, VAL vec, VAL idx);
VAL idris_b8x16CopyForGC(VM *vm, VAL a);
VAL idris_b16x8CopyForGC(VM *vm, VAL a);
VAL idris_b32x4CopyForGC(VM *vm, VAL a);
VAL idris_b64x2CopyForGC(VM *vm, VAL a);
#endif

Some files were not shown because too many files have changed in this diff Show More