Syllabus

Basic Lean4 syntax

This page contains generic information about basic Lean4 syntax. It is intended as a very quick introduction, just to get you started. It is in no way meant to be comprehensive, but is mostly so that you can follow along some of the code. In my opinion, the best way to learn is to play yourself with the code, modify it, stretch it to its limits, try to make it do weird things!

Text that Lean4 does not parse

Text that Lean4 parses

Dot-notation

As we have seen, namespaces allow us to “repeat” names of declarations. They also allow us to organize names into common groups, by naming similar definitions in the same namespace.

An interesting feature of namespaces is what is called “dot-notation”. Let us begin with an example.

Suppose that we define a function List.twice as follows.

def List.twice (L : List Nat) : List Nat := L ++ L

#eval List.twice [1,2,3] -- [1,2,3,1,2,3]

Thus, List.twice L creates a list twice as long as L consisting of two copies of L, one after the other. Observe that there is a coincidence: the Type of L is List ... and the name of the declaration is List.[...]. In this situation, we can replace the namespace List with the term L and obtain that List.twice L = L.twice. Of course, this trick cannot work always: there is the need of the coincidence of an argument of a function having a Type that is the namespace of the function itself. Nevertheless, since we choose the namespace, we can get “dot-notation” to work fairly often. This is a further reason why definitions and theorems about Lists are very often in the List namespace: since they apply to Lists, they likely take a term of type List ... as an argument and then dot-notation can be used. In case you are wondering, the shortening can happen also if the function has several arguments: dot-notation allows you to merge the first explicit argument of the correct Type with the namespace of the declaration.

Here is a situation where we can use dot-notation twice. We first generalize the definition of List.twice above to work for lists of arbitrary Types. Next, we take a term of Type String and we apply String.toList to it, converting a String to a list of characters. Finally, we apply List.twice to the resulting list of characters.

/-- For an arbitrary Type `α`, and a list `L : List α`, `List.twice L = L.twice` is the
list obtained by concatenating `L` with itself. -/
def List.twice (L : List α) : List α := L ++ L

#eval String.toList "abc" -- ['a', 'b', 'c'], the list of characters `'a', 'b', 'c'`

#eval "abc".toList -- ['a', 'b', 'c'], same as above, using dot-notation "with `String`"

#eval "abc".toList.twice -- ['a', 'b', 'c', 'a', 'b', 'c'], dot-notation twice!
#eval List.twice (String.toList "abc") -- same as above

Let’s go over the expression "abc".toList.twice again. First, "abc".toList is dot-notation for String.toList "abc" (we know this, since "abc" is a String). Moreover, "abc".toList produces a List, as the name suggests! Thus, if we want to apply List.twice to "abc".toList, we can again take advantage of dot-notation: we can compress it to "abc".toList.twice.

You can find a more extended example here


Back to the Theorem Proving with Lean webpage

Back to the Mathlib project for the module

Open in Gitpod

Back to Moodle