Zulip Chat Archive
Stream: general
Topic: using a widget proof state
Moritz Firsching (Aug 28 2024 at 09:34):
For @David Renshaw's great chess implementation, I added a widget to render the chess position a little bit more pretty: https://github.com/dwrensha/animate-lean-proofs/pull/4
Now we are wondering: is it possible to use that widget directly in the proof state? Are there good examples of widgets that do that somehow?
I can only find lean3 examples and no good lean4 examples of the usage of widgets.
Anand Rao Tadipatri (Aug 28 2024 at 10:24):
Have you already looked into docs#Lean.Widget.savePanelWidgetInfo or with_panel_widgets
? This demo in the ProofWidgets
repository gives a nice example of displaying an additional visualisation of the proof state.
Moritz Firsching (Sep 24 2024 at 19:28):
Now I was looking into this a bit more, and I thought I would mimic what the StringDiagram widget does and also what you linked to above. So I wrote this @[server_rpc_method] myrpc
method which is supposed to return some html for the Position
if we are our result prop comes from ForcedWin (Side → Position → Prop)
evaluated at a Side
and Position
,
https://github.com/mo271/animate-lean-proofs/blob/4b84bb0a955a55c8127d0cb0033d34b66bdff7b1/ChessWidgetExample.lean#L53C20-L78
open scoped Jsx in
/-- The RPC method for displaying FEN for ForcedWin (Side → Position → Prop). -/
@[server_rpc_method]
def myrpc (props : PanelWidgetProps) : RequestM (RequestTask Html) :=
RequestM.asTask do
let html : Option Html ← (do
if props.goals.isEmpty then
return none -- No goals, return none
else
let some g := props.goals[0]? | unreachable!
g.ctx.val.runMetaM {} do
g.mvarId.withContext do
-- Get the goal's type
let type ← g.mvarId.getType
-- Extract the function name and arguments using `getAppFnArgs`
let (fn, args) := type.getAppFnArgs
if fn == ``ForcedWin && args.size == 2 then
-- Extract the `Position` argument
let posExpr := args[1]! -- The second argument should be the Position
-- Try to manually interpret `posExpr` as a `Position` somehow
let posVal ← evalExpr _root_.Position (mkConst ``_root_.Position) posExpr
-- Convert the interpreted position to FEN
let fenStr := fenFromPosition posVal
return some <span>FEN: {Html.text fenStr}</span>
return none)
match html with
| none => return <span>No ForcedWin goal found or invalid type.</span>
| some inner => return inner
I think it is all more or less correct, but I get (kernel) invalid declaration, it uses unsafe declaration 'Lean.Meta.evalExpr'
and when I use unsafe
for the def myrpc, I get just get a similar error down the road:
(kernel) invalid declaration, it uses unsafe declaration 'Chess.myrpc'
So therefore my question is:
Is there a safe alternative to Lean.Meta.evalExpr
?
𝚠𝚘𝚓𝚌𝚒𝚎𝚌𝚑 𝚗𝚊𝚠𝚛𝚘𝚌𝚔𝚒 (Sep 24 2024 at 20:50):
@Moritz Firsching at first glance I think the issue is that evalExpr
can only evaluate _closed_ (variable-free) expressions, whereas you are trying to evaluate part of the goal state which might contain free variables. I think the only solution is to pattern-match on the position Expr
and get out what you need from it, otherwise fail.
𝚠𝚘𝚓𝚌𝚒𝚎𝚌𝚑 𝚗𝚊𝚠𝚛𝚘𝚌𝚔𝚒 (Sep 24 2024 at 20:51):
If you are sure that the Position
you see there is closed, so that evalExpr
can run, the standard solution is to write a safe wrapper around evalExpr
at a specific type.
𝚠𝚘𝚓𝚌𝚒𝚎𝚌𝚑 𝚗𝚊𝚠𝚛𝚘𝚌𝚔𝚒 (Sep 24 2024 at 20:52):
Like this.
Eric Wieser (Sep 25 2024 at 00:31):
Or just use the unsafe
term syntax?
Moritz Firsching (Sep 25 2024 at 04:47):
Thank you both! the unsafe
term syntax worked, so I didn't try the other approach.
docs#Lean.Parser.Term.unsafe
Moritz Firsching (Sep 25 2024 at 07:01):
Now it works great for displaying the FEN. For also displaying the nicely rendered board, I turned my ChessPositionWidget
into a component like so:
structure ChessPositionWidgetProps where
pos? : Option Position := none
deriving Lean.Server.RpcEncodable
open ProofWidgets
@[widget]
def ChessPositionWidget : Component ChessPositionWidgetProps where
javascript := "[all the javascript that renders the board, using stuff from `pos`]"
and while it displays the board fine in calls like this:
#widget ChessPositionWidget with { pos? := get_pos Chess.morphy_mates_in_two : ChessPositionWidgetProps }
when I use it as a component in the ForcedWinWidget, it only displays the empty board.
https://github.com/mo271/animate-lean-proofs/blob/5aa3cd3820c9779164be39dee491fc6bb18d718f/ChessWidgetExample.lean#L79
while the FEN for the same Position
gets extracted and displayed correctly.
(full working code in that commit)
What am I doing wrong?
𝚠𝚘𝚓𝚌𝚒𝚎𝚌𝚑 𝚗𝚊𝚠𝚛𝚘𝚌𝚔𝚒 (Sep 25 2024 at 15:23):
Hah, I was worried this would happen to somebody eventually. Widgets have a builtin prop called pos
, and it is overwriting the one you are providing. If you just change it to boardPos
or something, it should work. We should have a warning shown in this edge case, though.
Moritz Firsching (Sep 25 2024 at 18:43):
Thanks @Wojciech Nawrocki, it works now: https://github.com/dwrensha/animate-lean-proofs/blob/e5e8a4a7c30e4a9e17910aed4c843a17ad226b67/Chess/Widgets.lean,
review/comments welcome at https://github.com/dwrensha/animate-lean-proofs/pull/7
So I suppose the other forbidden prop names are goals
, termGoal
and selectedLocations
(from docs#ProofWidgets.PanelWidgetProps ) ?
Where would we best show a warning, just in the documentation somewhere (where?) or in the code when you try to define such a prop name?
Last updated: May 02 2025 at 03:31 UTC