Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Sum types #1602

Merged
merged 61 commits into from
Feb 2, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
61 commits
Select commit Hold shift + click to select a range
c978b91
Checkpoint: sum types
yav Jan 12, 2024
5e0594f
Make constructors work
yav Jan 15, 2024
1db3dbc
Fix docstrings on constructors, and show proper help for constructors
yav Jan 16, 2024
04d0e79
Make simple patterns, we disallow nested constructir patterns.
yav Jan 16, 2024
ad34745
Fix renaming of `CaseAlt`
yav Jan 16, 2024
81c5d12
Get started on type checking `case`
yav Jan 16, 2024
6a2b1c3
Make it build
yav Jan 16, 2024
e19a4de
Keep constructors in a separate namespace.
yav Jan 16, 2024
659030a
Typechecking for `case` expressions
yav Jan 17, 2024
a5404e6
Concrete evaluation for `case`
yav Jan 17, 2024
221bf4a
Fix exports to account for constructors
yav Jan 18, 2024
5004a3d
Add missing signature
yav Jan 22, 2024
385c328
Comments
yav Jan 22, 2024
a0179c0
Add missing cases for types that exist after `NoPat`
yav Jan 22, 2024
c7f12ad
Show help about constructors
yav Jan 22, 2024
c5b896c
Improve error message for bad self recursive things
yav Jan 22, 2024
cb6228e
Add some tests
yav Jan 22, 2024
40a37c8
Handle missing cases for nested patterns, with test
yav Jan 22, 2024
e975355
Document `enum` declarations
yav Jan 22, 2024
5236aff
Fix test
yav Jan 23, 2024
4c1b857
Fill in some of the random code for `enum`.
yav Jan 23, 2024
e6291ce
Update issue1040 golden output
RyanGlScott Jan 23, 2024
9438d83
Implement random generation of enum values
RyanGlScott Jan 23, 2024
f0706f9
Test case for :check'ing enum values
RyanGlScott Jan 23, 2024
0a3a2e1
Throw exception when symbolically evaluating enum values
RyanGlScott Jan 23, 2024
c00c9e5
Implement toExpr for enums
RyanGlScott Jan 23, 2024
77a8406
Fix precedence when pretty-printing counterexamples
RyanGlScott Jan 23, 2024
a9c7751
s/ppValue/ppValuePrec/
RyanGlScott Jan 23, 2024
8abf33f
Remove `private` annotations on constructors.
yav Jan 24, 2024
dd6313c
Implement symbolic evaluation for `enum` types.
yav Jan 25, 2024
8252656
Generalize CheckEnum test case to include :prove commands
RyanGlScott Jan 25, 2024
3e072f7
Use `Vector` and `IntMap` instead of `Map`, and a common ConInfo type
yav Jan 25, 2024
eb4a4ce
Document no-overlapping-patterns restriction
RyanGlScott Jan 26, 2024
ca85fac
Error when default case overlaps with subsequent cases
RyanGlScott Jan 26, 2024
17b1e25
RefMan: Say "enums", not "enumerations", to avoid ambiguous terminology
RyanGlScott Jan 26, 2024
1e07d0d
Rename "newtype" to "nominal type" after type checker
yav Jan 26, 2024
99b9a3c
Move constructors to the value namespace before checking for ambiguit…
yav Jan 26, 2024
d965cb4
Emit an error for non-exhaustive case expressions
RyanGlScott Jan 28, 2024
0c88cc3
Add regression test for #1606
RyanGlScott Jan 28, 2024
e47ebcb
Improvements to ":help" for nominal types/constructors
yav Jan 31, 2024
684a85e
Tweak error messages
yav Jan 31, 2024
c68ecfc
Make the `cryptol-remote-api` build again.
yav Jan 31, 2024
c06fc89
Rename some more things `newtype` -> `nominal`
yav Jan 31, 2024
e5251ee
Add nominal type constructors in scope.
yav Jan 31, 2024
3e7d786
Implement sanity checking for `case`
yav Jan 31, 2024
a980328
Make abstract type a special kind of nomainal type.
yav Feb 1, 2024
c8e4615
Apply suggestions from code review
RyanGlScott Feb 1, 2024
2f5bbd9
Clarify "Known Enum Type" restriction in RefMan
RyanGlScott Feb 1, 2024
8aaad22
Clarify two comments
RyanGlScott Feb 1, 2024
69327ba
Add freeVars case for Abstract
RyanGlScott Feb 1, 2024
642d192
Fix a -Wunused-matches warning
RyanGlScott Feb 1, 2024
8732b32
Add Option and Result types to the Cryptol prelude
RyanGlScott Feb 1, 2024
75958c1
RefMan: Fix formatting error
RyanGlScott Feb 1, 2024
76f9c3e
RefMan: Regenerate HTML
RyanGlScott Feb 1, 2024
4e6e5d8
Mention enums in CHANGES.md
RyanGlScott Feb 1, 2024
56cf1a3
Repair ProgrammingCryptol test
RyanGlScott Feb 1, 2024
01d8c1d
Repair ProgrammingCryptol test
RyanGlScott Feb 1, 2024
f825abd
Update expected stdout for test cases
RyanGlScott Feb 2, 2024
a9e35fe
Update .stdout.mingw32 and .stdout.darwin files
RyanGlScott Feb 2, 2024
ca0eb7e
Fix handling of abstract types
RyanGlScott Feb 2, 2024
9be52cc
Fix typo (parameterss) in error message
RyanGlScott Feb 2, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,24 @@

## Language changes

* Cryptol now supports *enum* declarations. An enum is a named typed which is
defined by one or more constructors. Enums correspond to the notion of
algebraic data types, which are commonly found in other programming
languages. See the [manual
section](https://galoisinc.github.io/cryptol/master/TypeDeclarations.html#enums)
for more information.

* Add two enum declarations to the Cryptol standard library:

```
enum Option a = None | Some a

enum Result t e = Ok t | Err e
```

These types are useful for representing optional values (`Option`) or values
that may fail in some way (`Result`).

* `foreign` functions can now have an optional Cryptol implementation, which by
default is used when the foreign implementation cannot be found, or if the FFI
is unavailable. The `:set evalForeign` REPL option controls this behavior.
Expand Down
3 changes: 1 addition & 2 deletions cryptol-remote-api/src/CryptolServer/Data/Expression.hs
Original file line number Diff line number Diff line change
Expand Up @@ -617,8 +617,7 @@ readBack ty val =
TVTuple{} -> "tuple"
TVRec{} -> "record"
TVFun{} -> "fun"
TVNewtype nt _ _ -> identText $ nameIdent $ TC.ntName nt
TVAbstract{} -> "abstract"
TVNominal nt _ _ -> identText $ nameIdent $ TC.ntName nt


-- | Given a suggested `name` and a type and value, attempt to bind the value
Expand Down
15 changes: 12 additions & 3 deletions cryptol-remote-api/src/CryptolServer/Data/Type.hs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,10 @@ import Cryptol.Parser.Position (emptyRange)
import Cryptol.Parser.Selector (ppSelector)
import Cryptol.Utils.RecordMap (recordFromFields)
import Cryptol.TypeCheck.PP (NameMap, emptyNameMap, ppWithNames)
import Cryptol.TypeCheck.Type (Kind(..), PC(..), TC(..), TCon(..), TFun(..), TParam(..), Type(..), Schema(..), addTNames, kindOf)
import Cryptol.TypeCheck.Type (Kind(..), PC(..), TC(..), TCon(..),
TFun(..), TParam(..), Type(..), Schema(..), addTNames, kindOf,
NominalType(..), NominalTypeDef(..)
)
import Cryptol.Utils.Ident (mkIdent)
import Cryptol.Utils.PP (pp)
import Cryptol.Utils.RecordMap (canonicalFields)
Expand Down Expand Up @@ -213,8 +216,14 @@ instance JSON.ToJSON JSONType where
, "name" .= show (ppWithNames ns v)
]
convert (TUser _n _args def) = convert def
convert (TNewtype _nt _ts) =
JSON.object [ "type" .= T.pack "newtype" ]
convert (TNominal nt ts) =
JSON.object [ "type" .= case ntDef nt of
Struct {} -> T.pack "newtype"
Enum {} -> T.pack "enum"
Abstract -> T.pack "primitive"
, "name" .= show (pp (ntName nt))
, "arguments" .= map (JSONType ns) ts
]
convert (TRec fields) =
JSON.object
[ "type" .= T.pack "record"
Expand Down
2 changes: 1 addition & 1 deletion docs/ProgrammingCryptol/crashCourse/CrashCourse.tex
Original file line number Diff line number Diff line change
Expand Up @@ -791,7 +791,7 @@ \subsection{Appending and indexing}
[]
invalid sequence index: 12
-- Backtrace --
(Cryptol::@) called at Cryptol:885:14--885:20
(Cryptol::@) called at Cryptol:904:14--904:20
(Cryptol::@@) called at <interactive>:9:1--9:28
[9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
9
Expand Down
182 changes: 182 additions & 0 deletions docs/RefMan/TypeDeclarations.rst
Original file line number Diff line number Diff line change
Expand Up @@ -51,3 +51,185 @@ of newtypes to extract the values in the body of the type.
> sum x.seq
6

Enums
-----

.. code-block:: cryptol

enum Maybe a = Nothing | Just a

An ``enum`` declaration introduces a new named type, which is defined by a
collection of *constructors*. ``enum`` declarations correspond to the notion of
*algebraic data types*, which are commonly found in other programming
languages. Each named ``enum`` type is treated like a separate type, even if it
has the exact same constructors as another ``enum`` type---in this way ``enum``
is similar to ``newtype`` and unlike ``type`` synonyms.

**Constructors.** The only way to create a value of an ``enum`` type is to
use one of its constructors. When used in an expression, the constructors
behave like an ordinary function, which has one parameter for each field of the
constructor. For example, the constructor ``Just`` has a type like this:

.. code-block:: cryptol

Just: {a} a -> Maybe a

Constructors may have 0 or multiple fields, and values created with different
constructors are always distinct.

**Case Expressions.** The only way to examine a value of an ``enum`` type is
with a ``case`` expression, which are similar to ``if`` expressions:

.. code-block:: cryptol

case e of
Nothing -> 0
Just a -> a + 1

In this example, ``e`` is an expression of type ``Maybe``:

* if it was created with the ``Nothing`` constructor,
then we'll use the first branch of the ``case`` expression, and
the result of the whole expression would be 0;

* if, ``e`` was created by applying the ``Just`` constructor to some
value (e.g, ``Just 2``), then we'll use the second branch of the ``case``
expression, and the variable ``a`` will be bound to the value of the field
(e.g., ``2``), and the whole expression will evaluate to ``a + 1``
(e.g., ``3``).

It is also possible to use just a variable (or ``_``) in a case expression
to define a catch-all clause---if a value does not match any of the previous
cases, then this branch will be used:

.. code-block:: cryptol

isNothing x =
case x of
Nothing -> True
_ -> False

**``Option`` and ``Result``.** Currently, Cryptol defines two ``enum``
declarations in the Cryptol standard library: ``Option`` and ``Result``:

.. code-block:: cryptol

enum Option a = None | Some a

enum Result t e = Ok t | Err e

The ``Option a`` type represents an optional value, which can either be a value
of type ``a`` (``Some``) or no value at all ``None``. A value of type ``Result
t e`` can either be a successful value of type ``t`` (``Ok``) or an error value
of type ``e`` (``Err``).

``Option`` and ``Result`` values are commonly used to model the return type of
partial functions, i.e., functions that are not defined for all inputs. For
instance, if a function ``f`` is not defined on the input ``42``, then one
could model this with ``Option``:

.. code-block:: cryptol

f : [8] -> Option [8]
f x =
if x == 42
then None
else Some (x+1)

One could also model this with ``Result``:

.. code-block:: cryptol

f : [8] -> Result [8] (String [8])
f x =
if x == 42
then Err "`f 42` not defined"
else Ok (x+1)

With either result type, one can gracefully recover from ``f 42`` erroring by
matching on ``None`` or ``Err`` in a ``case`` expression.

**Upper Case Restriction.**
The names of the constructors in an ``enum`` declarations
need to start with an upper-case letter. This restriction makes it possible
to distinguish between constructors and variable
bindings in ``case`` patterns (e.g., between ``Just`` and ``a`` in the
previous example).

**Non Recursive.** The fields in a constructor may be of any value type,
as long as this type does not depend on the type to which the constructor
belongs. This means that we do not support defining recursive types,
such as linked lists.

**No Nested Constructor Patterns.** For simplicity, only non-constructor
patterns may be used in the fields of a constructor pattern. For example,
``Just (a,b)`` and ``Just (a # b)`` are OK, however, ``Just (Just a)``
will be rejected. This is a restriction that we may lift in the future.

**No Overlapping Patterns.** For simplicity, all patterns in a
``case`` expression must be disjoint. In particular, this means that:

* No two patterns in a ``case`` expression can match the same constructor.
This means that Cryptol will reject the following example:

.. code-block:: cryptol

isNothing x =
case x of
Nothing -> True
Nothing -> False

* If a ``case`` expression uses a catch-all clause, then that clause must
occur last in the expression. It is an error to match on additional
patterns after the catch-all clause. For instance, Cryptol will reject the
following example:

.. code-block:: cryptol

isNothing x =
case x of
Just _ -> False
_ -> True
Nothing -> False

**Patterns Must Be Exhaustive.** The patterns in a ``case`` expression must
cover all constructors in the ``enum`` type being matched on. For example,
Cryptol will reject the following example, as it does not cover the ``Just``
constructor:

.. code-block:: cryptol

isNothing x =
case x of
Nothing -> True

**The Matched Expression Must Have a Known Enum Type.** Cryptol will reject
the following definition of ``f``, where ``f`` lacks a type signature:

.. code-block:: cryptol

f x =
case x of
_ -> ()

This is because it is not clear what the type of ``x`` (the expression being
matched) should be. The only pattern is a catch-all case, which does not reveal
anything about the type of ``x``. It would be incorrect to give ``f`` this type:

.. code-block:: cryptol

f : {a} a -> ()

This is because ``f`` is not really polymorphic in its argument type, as the
only values that can be matched in a ``case`` expression are those whose type
was declared as an ``enum``. As such, Cryptol rejects this example.

Cryptol will also reject this definition, where the type of the value
being matched is not an ``enum`` type:

.. code-block:: cryptol

g : Integer -> ()
g x =
case x of
_ -> ()
Binary file modified docs/RefMan/_build/doctrees/BasicSyntax.doctree
Binary file not shown.
Binary file modified docs/RefMan/_build/doctrees/BasicTypes.doctree
Binary file not shown.
Binary file modified docs/RefMan/_build/doctrees/Expressions.doctree
Binary file not shown.
Binary file modified docs/RefMan/_build/doctrees/FFI.doctree
Binary file not shown.
Binary file modified docs/RefMan/_build/doctrees/Modules.doctree
Binary file not shown.
Binary file modified docs/RefMan/_build/doctrees/OverloadedOperations.doctree
Binary file not shown.
Binary file modified docs/RefMan/_build/doctrees/RefMan.doctree
Binary file not shown.
Binary file modified docs/RefMan/_build/doctrees/TypeDeclarations.doctree
Binary file not shown.
Binary file modified docs/RefMan/_build/doctrees/environment.pickle
Binary file not shown.
2 changes: 1 addition & 1 deletion docs/RefMan/_build/html/.buildinfo
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Sphinx build info version 1
# This file hashes the configuration used when building these files. When it is not found, a full rebuild will be done.
config: 49d6d89345e8bba027ef627e0adeb689
config: 41758cdb8fafe65367e2aa727ac7d826
tags: 645f666f9bcd5a90fca523b33c5a78b7
Loading
Loading