Zulip Chat Archive
Stream: lean4
Topic: Undefine lambda
Sky Wilshaw (Sep 28 2023 at 10:31):
Is there a way to undefine the special function syntax for λ?
variable {λ : Type _} -- unexpected token 'λ'; expected '_' or identifier
Mario Carneiro (Sep 28 2023 at 11:52):
No, undefining tokens is not allowed. But let's do it anyway:
import Lean
open Lean Parser
def Lean.Parser.Trie.isEmpty {α} (t : Trie α) : Bool := t matches .Node none .leaf
partial def Lean.Parser.Trie.erase {α} (t : Trie α) (s : String) : Trie α :=
let rec loop : Trie α → String.Pos → Option (Trie α)
| ⟨val, m⟩, i =>
match s.atEnd i with
| true => some (Trie.Node none m)
| false => do
let c := s.get i
let i := s.next i
let t ← m.find compare c
let t ← loop t i
let m := if t.isEmpty then m.erase compare c else m.insert compare c t
some ⟨val, m⟩
(loop t 0).getD t
elab "delete_token" tk:str : command => do
let tk := tk.getString
let env ← getEnv
let s := parserExtension.ext.getState env
if let top :: states := s.stateStack then
modifyEnv fun env =>
let top := { top with state.tokens := top.state.tokens.erase tk }
parserExtension.ext.setState env { s with stateStack := top :: states }
delete_token "λ"
example : Nat → Nat := fun λ => λ
-- ^ expected token
Sky Wilshaw (Sep 28 2023 at 11:54):
Good to know, thanks!
Mario Carneiro (Sep 28 2023 at 11:55):
It doesn't work, though. The reason is that even though we have undefined lambda as a token, it is still not an identifier character as determined by this function:
def isLetterLike (c : Char) : Bool :=
(0x3b1 ≤ c.val && c.val ≤ 0x3c9 && c.val ≠ 0x3bb) || -- Lower greek, but lambda
(0x391 ≤ c.val && c.val ≤ 0x3A9 && c.val ≠ 0x3A0 && c.val ≠ 0x3A3) || -- Upper greek, but Pi and Sigma
(0x3ca ≤ c.val && c.val ≤ 0x3fb) || -- Coptic letters
(0x1f00 ≤ c.val && c.val ≤ 0x1ffe) || -- Polytonic Greek Extended Character Set
(0x2100 ≤ c.val && c.val ≤ 0x214f) || -- Letter like block
(0x1d49c ≤ c.val && c.val ≤ 0x1d59f) -- Latin letters, Script, Double-struck, Fractur
So we get the same error that one would get with fun д => д
: it's not a legal identifier character
Mario Carneiro (Sep 28 2023 at 11:55):
sadly this definition is not extensible, so we can't do anything about it
Sky Wilshaw (Sep 28 2023 at 11:55):
Ah, I see, U+03BB
is explicitly banned.
Mario Carneiro (Sep 28 2023 at 11:57):
However, that's not the end of the story: even if it's a token we can still make it a macro that expands to an identifier:
import Lean
macro "λ" : term => pure (Lean.mkIdent `«λ»)
example : Nat → Nat := fun λ => λ
and it works! This is the same trick that used to be used to make this
work. (It's a bit leaky but you should be able to use it most places an identifier is legal.)
Sky Wilshaw (Sep 28 2023 at 11:58):
Huh, that's really interesting.
Mario Carneiro (Sep 28 2023 at 12:00):
also, just in case you were not aware, even reserved words and non-identifier characters can be used in identifiers if you wrap them in the guillemets as in «λ»
Sky Wilshaw (Sep 28 2023 at 12:00):
I think I've seen that trick before in a couple of places like lakefiles and with weird filenames.
Sky Wilshaw (Sep 28 2023 at 12:01):
And protected exists
/forall
lemmas.
Henrik Böving (Sep 28 2023 at 12:03):
Mario Carneiro said:
No, undefining tokens is not allowed. But let's do it anyway:
import Lean open Lean Parser def Lean.Parser.Trie.isEmpty {α} (t : Trie α) : Bool := t matches .Node none .leaf partial def Lean.Parser.Trie.erase {α} (t : Trie α) (s : String) : Trie α := let rec loop : Trie α → String.Pos → Option (Trie α) | ⟨val, m⟩, i => match s.atEnd i with | true => some (Trie.Node none m) | false => do let c := s.get i let i := s.next i let t ← m.find compare c let t ← loop t i let m := if t.isEmpty then m.erase compare c else m.insert compare c t some ⟨val, m⟩ (loop t 0).getD t elab "delete_token" tk:str : command => do let tk := tk.getString let env ← getEnv let s := parserExtension.ext.getState env if let top :: states := s.stateStack then modifyEnv fun env => let top := { top with state.tokens := top.state.tokens.erase tk } parserExtension.ext.setState env { s with stateStack := top :: states } delete_token "λ" example : Nat → Nat := fun λ => λ -- ^ expected token
This is the most cursed meta program I've seen you produce so far :P
Mario Carneiro (Sep 28 2023 at 12:07):
You can see I've taken the lessons from #general > PSA about trusting Lean proofs to heart. Lean is the language where no means "well..."
Eric Wieser (Sep 28 2023 at 12:09):
Why did we decide to change all the λ
s to fun
in mathlib if it's so hard to actually use λ
as a name anyway?
Eric Wieser (Sep 28 2023 at 12:09):
I thought the argument was always "I want to use λ
for a variable"
Mario Carneiro (Sep 28 2023 at 12:10):
It is, but lambda is defined in core
Mario Carneiro (Sep 28 2023 at 12:10):
and it wasn't designed to be optional
Eric Wieser (Sep 28 2023 at 12:10):
Was the thinking that step one was remove λ
from mathlib, and step 2 is complain loudly at core until it becomes optional?
Mario Carneiro (Sep 28 2023 at 12:11):
I think so
Mario Carneiro (Sep 28 2023 at 12:12):
My understanding is that the reason λ
is actually a non-identifier character rather than simply a token like other reserved words is so that people can write λa => a
(compare with funny => ny
which does not work)
Eric Wieser (Sep 28 2023 at 12:15):
That seems pretty reasonable, but totally at odds with the decision by mathlib to not use λ
at all
Mario Carneiro (Sep 28 2023 at 12:16):
This whole part of the code is directly lifted from lean 3
Mario Carneiro (Sep 28 2023 at 12:16):
including the bizarre identifier list
Mario Carneiro (Sep 28 2023 at 12:16):
which also excludes Pi even though it's not a token anymore
Sky Wilshaw (Sep 28 2023 at 12:17):
For some context, the project I'm working on has some ordinal parameters that on paper are named λ, κ, μ, so I have to awkwardly capitalise λ in code.
Sky Wilshaw (Sep 28 2023 at 12:17):
I'd hoped that with fun
this wouldn't be an issue in Lean 4.
Sky Wilshaw (Sep 28 2023 at 12:18):
Mario Carneiro said:
so that people can write
λa => a
But on the other hand this seems quite useful!
Jannis Limperg (Sep 28 2023 at 12:19):
You can also write «λ»
, which would perhaps be less crazy.
Sky Wilshaw (Sep 28 2023 at 12:19):
λ currently shows up in over 200 places, I don't really want to litter my code with guillemets that much.
Jannis Limperg (Sep 28 2023 at 12:19):
Oh sorry, Mario already said so. :man_facepalming:
Sky Wilshaw (Sep 28 2023 at 12:20):
It doesn't really make sense to rename the ordinals either, because λ takes the role of a limit ordinal.
Mario Carneiro (Sep 28 2023 at 12:21):
how about ⲗ
or Ⲗ
?
Sky Wilshaw (Sep 28 2023 at 12:23):
Somewhat expectedly I get expected token
using either of those.
Mario Carneiro (Sep 28 2023 at 12:24):
oh, they are coptic letters so I would have guessed they would fall under the identifier classification
Sky Wilshaw (Sep 28 2023 at 12:25):
They're U+2C9(6|7), which apparently aren't in the range.
Sky Wilshaw (Sep 28 2023 at 12:25):
Sky Wilshaw (Sep 28 2023 at 12:25):
Not to be confused with...
image.png
James Gallicchio (Oct 02 2023 at 17:40):
if mathlib can survive fine without lambda for functions, I think that's pretty good evidence that everyone will survive without lambdas for functions :p
James Gallicchio (Oct 02 2023 at 17:40):
(wrt the core change)
Last updated: Dec 20 2023 at 11:08 UTC