Idris2/docs/source/reference/postfixprojs.rst

197 lines
5.5 KiB
ReStructuredText
Raw Normal View History

2020-06-21 16:51:09 +03:00
Postfix projections
===================
2020-05-20 13:23:04 +03:00
.. role:: idris(code)
:language: idris
2020-06-21 16:51:09 +03:00
Postfix projections are syntactic forms that represent postfix application,
such as ``person.name``. They were originally motivated by the need of dot syntax
to access fields of records, and generalised also for non-record use cases later.
The more advanced features require ``%language PostfixProjections``,
at least until it's clear whether they should be present in the language.
2020-05-20 13:23:04 +03:00
Lexical structure
-----------------
2020-06-21 16:51:09 +03:00
* ``.foo`` starting with lowercase ``f``, single identifier, and no space after dot,
is a valid lexeme, which stands for postfix application of ``foo``
* ``Foo.Bar.baz`` with uppercase ``F`` and ``B`` is one lexeme: a namespaced
identifier.
2020-05-20 13:23:04 +03:00
2020-06-21 16:51:09 +03:00
* ``Foo.Bar.baz.boo`` is two lexemes: ``Foo.Bar.baz`` and ``.boo``.
2020-05-20 13:23:04 +03:00
* ``foo.bar.baz`` starting with lowercase ``f`` is three lexemes: ``foo``,
``.bar``, ``.baz``
* ``.foo.bar.baz`` is three lexemes: ``.foo``, ``.bar``, ``.baz``
2020-06-21 16:51:09 +03:00
* ``foo . bar``, as well as ``foo. bar`` is three lexemes: ``foo``, ``.``, ``bar``,
and represents function composition as usual
* Beware that ``True.not`` is lexed as "name ``not`` in module ``True``".
If you want the postfix application of ``not`` to constructor ``True``,
you have to write ``(True).not``.
2020-06-21 17:09:22 +03:00
* Spaces *before* dots are optional; spaces *after* dots are forbidden
in postfix projections: ``foo. bar`` is parsed as function composition.
2020-05-20 13:23:04 +03:00
2020-06-21 17:09:22 +03:00
* All module names must start with an uppercase letter.
2020-05-20 13:23:04 +03:00
New syntax of ``simpleExpr``
----------------------------
2020-06-21 16:51:09 +03:00
Expressions binding tighter than application (``simpleExpr``),
such as variables or parenthesised expressions, have been renamed to ``simplerExpr``,
and an extra layer of postfix dot syntax has been inserted in ``simpleExpr``.
2020-05-20 13:23:04 +03:00
.. code-block:: idris
2020-06-21 16:51:09 +03:00
postfixProj ::= .ident -- identifiers need not be bracketed
| .(expr) -- arbitrary expression in brackets
2020-05-20 13:23:04 +03:00
2020-06-21 16:51:09 +03:00
simpleExpr ::= simplerExpr postfixProj+ -- postfix projection
| (postfixProj+ simpleExpr+) -- section of postfix projection
2020-06-21 09:42:30 +03:00
| simplerExpr -- (parses as whatever it used to)
2020-06-21 01:56:21 +03:00
2020-05-20 13:23:04 +03:00
Desugaring rules
----------------
2020-06-21 16:51:09 +03:00
* Postfix projections:
``simpleExpr .proj1 .proj2 .proj3`` desugars to ``(proj3 (proj2 (proj1 simpleExpr))``
* Sections of postfix projections:
``(.proj1 .proj2 .proj3 arg1 arg2)`` desugars to ``(\x => x.proj1.proj2.proj3 arg1 arg2)``
Examples of desugaring:
2020-05-20 13:23:04 +03:00
2020-06-21 16:51:09 +03:00
* ``foo.bar`` desugars as ``bar foo``
* ``True.not`` is a single lexeme: qualified name
* ``(True).not`` desugars as ``not True``
* ``(True).(not . not)`` desugars as ``(not . not) True``
* ``(not True).(not . not)`` desugars as ``(not . not) (not True)``
* ``(checkFile fileName).runState initialState`` desugars as ``runState (checkFile fileName) initialState``
* ``(MkPoint 3 4).x`` desugars as ``x (MkPoint 3 4)``
Examples of desugaring sections:
* bare ``.foo`` is invalid syntax (e.g. in ``f $ .foo``)
* ``(.foo)`` desugars as ``(\x => x.foo)``, i.e. ``(\x => foo x)``
* ``(.foo.bar m n)`` desugars as ``(\x => bar (foo x) m n)``
2020-05-20 13:23:04 +03:00
Example code
------------
.. code-block:: idris
record Point where
constructor MkPoint
x : Double
y : Double
This record creates two projections:
2020-06-21 17:09:22 +03:00
2020-05-20 13:23:04 +03:00
* ``x : Point -> Double``
* ``y : Point -> Double``
2020-06-21 00:49:34 +03:00
We add another record:
2020-05-20 13:23:04 +03:00
.. code-block:: idris
record Rect where
constructor MkRect
topLeft : Point
bottomRight : Point
Let's define some constants:
.. code-block:: idris
pt : Point
pt = MkPoint 4.2 6.6
rect : Rect
rect =
MkRect
(MkPoint 1.1 2.5)
(MkPoint 4.3 6.3)
2020-06-21 16:51:09 +03:00
squared : Num a => a -> a
squared x = x * x
2020-06-21 01:56:21 +03:00
Finally, some examples:
2020-05-20 13:23:04 +03:00
.. code-block:: idris
main : IO ()
main = do
2020-06-21 00:49:34 +03:00
-- desugars to (x pt)
2020-05-20 13:23:04 +03:00
-- prints 4.2
printLn $ pt.x
-- prints 4.2, too
-- maybe we want to make this a parse error?
printLn $ pt .x
-- prints 10.8
printLn $ pt.x + pt.y
-- works fine with namespacing
2020-06-21 16:51:09 +03:00
-- both print 4.2
printLn $ Main.pt.x
2020-05-20 13:23:04 +03:00
printLn $ (Main.pt).x
-- the LHS can be an arbitrary expression
-- prints 4.2
printLn $ (MkPoint pt.y pt.x).y
2020-06-21 16:51:09 +03:00
-- the RHS can be an arbitrary expression, too
-- prints 17.64
printLn $ (MkPoint pt.y pt.x).(squared . y)
-- user-defined function
2020-05-20 13:23:04 +03:00
-- prints 17.64
printLn $ pt.x.squared
-- prints [1.0, 3.0]
printLn $ map (.x) [MkPoint 1 2, MkPoint 3 4]
2020-06-21 00:49:34 +03:00
-- .topLeft.y desugars to (\x => y (topLeft x))
2020-05-20 13:23:04 +03:00
-- prints [2.5, 2.5]
printLn $ map (.topLeft.y) [rect, rect]
-- desugars to (.topLeft.x rect + .bottomRight.y rect)
-- prints 7.4
printLn $ rect.topLeft.x + rect.bottomRight.y
2020-06-21 01:56:21 +03:00
-- complex projections
-- prints 7.4
printLn $ rect.(x . topLeft) + rect.(y . bottomRight)
2020-05-20 13:23:04 +03:00
2020-06-21 01:56:21 +03:00
-- haskell-style projections
2020-05-20 13:23:04 +03:00
printLn $ Main.Point.x pt
printLn $ Point.x pt
printLn $ (x) pt
printLn $ x pt
-- record update syntax uses dots now
-- prints 3.0
printLn $ (record { topLeft.x = 3 } rect).topLeft.x
-- but for compatibility, we support the old syntax, too
printLn $ (record { topLeft->x = 3 } rect).topLeft.x
-- prints 2.1
printLn $ (record { topLeft.x $= (+1) } rect).topLeft.x
printLn $ (record { topLeft->x $= (+1) } rect).topLeft.x
Parses but does not typecheck:
.. code-block:: idris
-- parses as: map.x [MkPoint 1 2, MkPoint 3 4]
-- maybe we should disallow spaces before dots?
--
printLn $ map .x [MkPoint 1 2, MkPoint 3 4]