Most active commenters
  • behnamoh(6)
  • ks2048(3)

←back to thread

89 points Numerlor | 19 comments | | HN request time: 0.587s | source | bottom
1. behnamoh ◴[] No.41851517[source]
I want a Lisp-like REPL for my Python programs that is available to the user who runs my compiled Python program (so, I want compilation as well). The user will be able to interact with my program (instead of just running the main function), change function definitions, etc. and mold the program to their specific use case while it's running.
replies(5): >>41851780 #>>41852247 #>>41852604 #>>41853249 #>>41853727 #
2. ks2048 ◴[] No.41851780[source]
Some things are a bit awkward, but what specifically can't you really do in a Python REPL? You can dynamically overwrite functions in an imported module, you can re-import modules with importlib, etc. I ask because my Lisp experience is limited.
replies(1): >>41851857 #
3. behnamoh ◴[] No.41851857[source]
Lisp REPL keeps the state of the program because it is a live image. With Python REPL you need to rerun the program to set the variables to their values.
replies(1): >>41852345 #
4. Nab443 ◴[] No.41852247[source]
Something like https://hylang.org/ ? I suppose it won't work with compiled code though.
replies(1): >>41854929 #
5. ks2048 ◴[] No.41852345{3}[source]
You can create a file, example.py:

    import time
    value = "foo"
    def go():
      for i in range(10):
        print("...", i, value)
        time.sleep(3)
Then, in repl,

    import example
    import threading
    thread = threading.Thread(target=example.go)
    thread.start()
It will slowly print out messages and you can do "example.value = 'bar'" in the REPL and it will change.
replies(3): >>41852396 #>>41853872 #>>41854578 #
6. behnamoh ◴[] No.41852396{4}[source]
it's not the same though—In Lisp, when you compile your program, a Lisp run-time is attached to it so you can either run the program normally (like any binary) or you could REPL into the program and see what the values of variables are (these values were set at compile time). This is helpful when you have a large dataset you don't want to load over and over.
replies(2): >>41853928 #>>41854101 #
7. klibertp ◴[] No.41852604[source]
Go with Smalltalk instead. Not only do you get REPL-level interactivity[1], user actions are also automatically persisted, and it works with a GUI by default! With Glamorous Toolkit (a Pharo Smalltalk-based IDE) you even get good interop with Python out of the box. Really, if you have users who would benefit from being able to interact with your code directly, Smalltalk is THE way to go :) Ofc, the problem is finding such users in the first place...

[1] As Smalltalk is most often used with GUIs, you don't get a "REPL" by default - instead, anywhere you can enter text, you can right-click and execute that text as code. You can code a real REPL yourself if you want - 10 lines in the "on new line character" method in a generic text field and you're done.

8. halfcat ◴[] No.41853249[source]
I don’t know how this works with the compilation angle, but this is often a life saver:

  try:
    # problem
  except:
    import code
    code.interact(local=locals())
You can add globals() in addition to locals() if desired.

This drops you into the interactive shell and you can go wild. Even works inside interactive code like grabbing a live HTTP request or GUI event to see what’s going on.

9. zahlman ◴[] No.41853727[source]
>the user who runs my compiled Python program (so, I want compilation as well).

You'll be happy to know that Python .pyc files, which are created and cached by default, are the equivalent of Java's .class files or C#'s bytecode (which gets embedded inside an .exe wrapper but is still fundamentally not native code).

>The user will be able to... change function definitions, etc.

Of course, this requires recompilation in some form (in Lisp, too - `eval` is not that magic).

That said, if `import`ing your code at the REPL and then calling functions, setting attributes etc. (which has been possible in Python forever) isn't good enough, I really don't understand your use case.

10. Y_Y ◴[] No.41853872{4}[source]
That's a nice trick!

For reference, what I'd normally do if I wanted that is specify launching with the debugger, like

    python3 -mpdb example.py
and then step through. Also `ipdb` is nice if available.
11. zahlman ◴[] No.41853928{5}[source]
Have you tried out the Python standard library debugger (pdb)?
12. d0mine ◴[] No.41854101{5}[source]
Repl can be attached to a live python process (you don't need to restart anything)

https://stackoverflow.com/questions/1395913/how-to-drop-into...

You could do it even without cooperation from the python app using approach similar to pyrasite (though it is much easier with cooperation--just add a couple of lines).

replies(1): >>41854937 #
13. handman ◴[] No.41854578{4}[source]
This is nothing like the Lisp debugger. It is very hard to convince Pythonistas of the fact:

https://news.ycombinator.com/item?id=36888648

replies(1): >>41854724 #
14. ks2048 ◴[] No.41854724{5}[source]
Interesting thread. I only read some of it, but I did find one answer that tells me how Python and LISP differ:

  As an example, I suppose that when you’re developing code in Python’s pseudo-REPL you often reimport a file containing class definitions. When you do that, what happens to all the old objects with the old  class definition? Nothing, they still belong to the old class.

  ...

  In Lisp, it’s different. There is a defined protocol for what happens when a class is redefined. Every single object belonging to the old class gets updated to the new class.
replies(2): >>41854922 #>>41855975 #
15. behnamoh ◴[] No.41854922{6}[source]
> In Lisp, it’s different. There is a defined protocol for what happens when a class is redefined. Every single object belonging to the old class gets updated to the new class.

This. Lisp gives you ultimate power but of course, it means it unlocks more ways to shoot yourself in the foot in the process. Most organizations don't want "smart hacky" solution (i.e., they don't want you as the programmer to fix issues in creative ways that Lisp provides to you), instead, they want "standard processes" that can be taught to new hires (i.e., they want you as the programmer to write standard, idiomatic code that everyone understands). The latter comes at the cost of less flexibility (i.e., when your rock solid code crashes, it means you must update the source code instead of fixing it on the go like in Lisp restarts).

16. behnamoh ◴[] No.41854929[source]
Last I heard it was moving too fast and broke a bunch of stuff.
17. behnamoh ◴[] No.41854937{6}[source]
That only works where you specified (e.g., line 42 where you invoke `code.interact(local=locals()`). Lisp's approach works anywhere (anytime an exception is raised you're thrown in the REPL).
replies(1): >>41856211 #
18. kazinator ◴[] No.41855975{6}[source]
This protocol is in Common Lisp. Common Lisp is not Lisp. Not every Lisp has CLOS.

Common Lisp is not the be-all end-all in Lisp.

Redefining the classes of objects at run-time is not unilaterally a great idea.

It's not obvious how to implement it in such a way that applications which don't use it don't have to pay any extra cost for the support.

There being a defined protocol for what happens when a class being redefined does not add up to you automatically having an application that can upgrade itself while it is running, from any version to any version, bug free. You have to test and debug all the combinations, or else support only certain combinations, requiring multiple upgrades.

This is something that will add complexity to your upgrade plan.

I can see someone like NASA using such a tool for a very specific scenario, which can be replicated on Earth and tested, and then carried out remotely in some space probing vessel.

19. d0mine ◴[] No.41856211{7}[source]
gc allows to get any [non-leaky] object from anywhere. You can even drop into repl over the network.

Debugger can be started on exception if desired e.g., pytest provides `--pdb` arg. Though repl is different--you don't need to interrupt the app itself while you are inspecting it e.g., async repl can be run in the same thread alongside the rest of the app.

I can't remember a single case there Python's dynamism weren't enough for the task (most of the times it is the opposite--there could be less dynamism).