Closed brandonwillard closed 6 years ago
The purpose of Hy's let
is to create lexical variables. I'm not certain from your example what you want, but I think you're asking for dynamic variables hylang/hyrule#51, which is the default let
behavior in Emacs.
Python does this kind of thing with context managers and the with
statement, which Hy can also use. If you want to temporarily mutate a dict, then put it back at the end of the block, you want with
, not let
. You'd have to write the code to both do and undo your assignment, but it will be reusable.
Another option might be to use a collections.ChainMap
in combination with let
, to preserve the underlying dict.
=> (let [some-dict (ChainMap {} some-dict)]
... (setv (get some-dict "some-key") 2)
... (print some-dict)
... (print (get some-dict "some-key")))
ChainMap({'some-key': 2}, {'some-key': None})
2
=> (print some-dict)
{'some-key': None}
I didn't think about it in terms of broader variable scoping and/or binding, but I suppose that could be part of it.
However, does that still apply in a situation like the following?
> (let [some-dict {"some-key" None}
... (get some-dict "some-key") 1]
... (print some-dict))
File "<input>", line 2, column 6
(get some-dict "some-key") 1]
^------------------------^
HyMacroExpansionError: bind targets must be symbols
Another way to state the functionality I'm describing: alter let
to allow any valid assignment target as a target in its bindings.
If you want to permanently update a target, use setv
. And recall that macros can return a do
block (named from Clojure's do
, which is like the Common Lisp PROGN
or Scheme begin
).
If you want to update a target for the duration of a block (and then put it back), use with
and a patching context manager.
=> (import [unittest.mock [patch]])
None
=> (setv a-dict {"key" None})
None
=> (with [(patch.dict a-dict {"key" 2})]
... (print a-dict))
{'key': 2}
=> (print a-dict)
{'key': None}
See also: patch.object
, patch.multiple
, and contextvars
. This is dynamic scope.
If you want some kind of lexically-scoped update of a generalized target, then I don't understand what that means.
If you want some kind of destructuring, let
doesn't support that, but see hylang/hy#1328. I think a let=:
would be possible.
Maybe what we need is a hy.cl
library. It'd be nice to be able to use letf
, flet
, labels
, etc.
I wonder if that problem is no reason to fork? It would be hard to change the muscle-memory from (let (...) foo) to (with [x 5] foo) or something like that. But I think not even that would work.
What is the goal of hy, it should be a as much as possible lisp with a ptyhon backend?
I thought it's just not possible that python has no block scope so you can't implement a lisp like (let) construct, but I just learned aboutd the del() function.
So you could compile:
(let (foo 5) (bar))
into:
foo = 5
bar()
del (foo)
I just wonder, because without let I can't take a lisp like language serious. I don't want to programm python code with a bit different parens syntax.
A library is not a fork.
Hy should be as much like Python as possible, but homoiconic with macros and expression-oriented like Lisp, with features mostly taken from Clojure, but also Common Lisp.
Python does not have arbitrary block scope, but it does have late-binding lexically-scoped function closures.
del
is a statement/special form, not a function. You could certainly write your own macro to do that with del
, but that's not how let
works, even in Emacs. Think about what happens if you nest them.
let
doesn't mean what you think it means. What you're trying to do is dynamic scope. But Hy's let
is lexically scoped, like it is in Clojure and Common Lisp, neither of which have a letf
. Python doesn't have a separate namespace for function identifiers like Common Lisp and Emacs do. So Hy is more of a Lisp-1, like Scheme and Clojure.
@spiderbit As already stated above, Hy has a let
implementation that mostly does what you want. Using del
has been discussed before but rejected because it interferes with closures.
that's good to hear, why do I not see it then in the documentation homepage?
neither there: http://docs.hylang.org/en/stable/language/api.html#built-ins
nor there: http://docs.hylang.org/en/stable/tutorial.html#hy-is-a-lisp-flavored-python
where is it hidden? :D
@gilch, on a somewhat related note, I came across this thread (and this one, too) a while back when searching for letf
in CL.
Anyway, I think @gilch's diagnosis of why the idea of letf
doesn't really make sense (at least in Python and with a lexical let
) is spot on, so I'm closing this issue.
After a macro expansion, I ended up in a situation (this one, to be exact) like the following:
For this situation to work out, it seems like
let
would have to behave like Emacs'cl-letf
.A very related functionality — namely, generalized variables in Emacs — does appear to work in Hy:
That said, is there any place/interest in having an extended
let
that allows this (when reasonable) or a separateletf
?