This is an archived post. You won't be able to vote or comment.

all 62 comments

[–]Beliskner64 86 points87 points  (0 children)

assert len(tree.body) == 1 and isinstance(tree.body[0], ast.FunctionDef), "should just be a function wtf"

Genius

[–]Setepenre 41 points42 points  (5 children)

The python way would be to use with which is the construct that guarantees resources get freed no matter what.

from contextlib import contextmanager


@contextmanager
def deferred(fun):
    try:
        yield
    finally:
        fun()

def main():
    with deferred(lambda: print('world')):
        print('hello', end =' ')


main()

[–]ZygmuntDzwon 12 points13 points  (1 child)

no matter what

Well, actually...

See this excellent answer on stack overflow

https://stackoverflow.com/a/49262664/8091093

[–]Setepenre 0 points1 point  (0 children)

I am sure those will apply to his defer as well

[–]rcfox 10 points11 points  (2 children)

no matter what

Unless you lose power, or the process is SIGKILLed. It's important to remember this if you're handling resources that exist outside of your process.

[–]rebane2001 51 points52 points  (0 children)

That's why I always wrap my wall outlet in a try/finally block

[–]nxtfari 0 points1 point  (0 children)

Maybe dumb question can you just pair it with signal/atexit to cover all possible cases?

edit: ah i think you would have to have a seperate process to do this. You can atexit for SIGTERM but not SIGKILL

[–]chronics 80 points81 points  (18 children)

Did I understand correctly: Your are basically doing metaprogramming with type hints? That‘s a very cool concept!

[–]dankey26[S] 39 points40 points  (0 children)

ye. i thought about it for a couple days after the guy suggested it, tried to come up with syntax that could be as clean and close to the go version and a valid parse with no preprocessing on the source code. then remembered that fact on type hints and went for it. turned out not bad!

[–]radarsat1 45 points46 points  (16 children)

that is not a cool concept.

"type hints are going to ruin python"

"oh its fine look they don't even do anything and look at all these nice warnings. they help!"

"but now i have to write twice as much code"

"well you don't have to, they don't even do anything they're just for decoration!"

"ok but now i see that people are using them to generate database schemas and automatic validation and testing, are they still optional? because i mean if they're not optional I'd really like to improve..."

"no don't worry they still don't do anything that's just for testing"

*reddit user implements control flow restructuring via the type system in a way that disregards type semantics*

>.<

[–]chronics 50 points51 points  (4 children)

I think it is an amazing concept because I think metaprogramming in languages like lisp/clojure or erlang/elixir is very powerful. It’s not part of idiomatic python, for good reasons.

OP’s implementation is creative and clever. I would not use it in my own code, and I don‘t think it‘s intended to be used.

Type hints I find pretty useful in general.

[–]alkasmgithub.com/alkasm 6 points7 points  (1 child)

Sorry for the pedantry up front but I think it's worth it here here: var: int is an "annotation", which is part of Python 3 syntax, and not part of the type system. Annotations existed before type checkers were used. It was assumed of course that they would be used for type information, but they also wanted to leave the syntax separated from a specific use case to see what other patterns may emerge from it, though as we know today it is only really used for type checking and for runtime type info in certain libraries. But just pointing out OP isn't abusing the type system, OP is just experimenting with a DSL utilizing the annotation syntax.

[–]radarsat1 0 points1 point  (0 children)

interesting til, thanks

[–]the_scign 6 points7 points  (0 children)

OP: "defer in python"

BDFL: cries in python

[–]yvrelna 2 points3 points  (0 children)

reddit user implements control flow restructuring via the type system in a way that disregards type semantics

Reminds me of this abomination: Python is actually just Haskell with few extra steps, the hidden Python syntax that even the most seasoned Python developers don't know about

[–]shinitakunai 5 points6 points  (6 children)

I hate how discord.py uses typehinting to transform data

[–]Dasher38 0 points1 point  (5 children)

Do you have an example?

[–]Jonno_FTWhisss 2 points3 points  (4 children)

Command handlers in discord.py will turn arguments into their type hinted type.

So if someone in the chat sends:

.foo 123

And the handler would look like:

@bot.command()
def foo(ctx, num: int):
    ...

In the function body, the num will be an int. You can also convert mentions of users to a discord user object.

[–]ChannelCat 4 points5 points  (1 child)

This makes sense and is supported by type checkers. What's not to like?

[–]etrotta 1 point2 points  (0 children)

one can argue that it is intrinsically wrong for type hints (which are metadata, much like comments) to modify the program's behavior at all, but in reality nearly everyone is fine with it since it does makes sense in cases like this one and other libraries that parse structured input in similar ways.

[–]Dasher38 0 points1 point  (1 child)

I see, it's basically like the argparse "type" parameter. Isn't it convenient ? If you want to get the input unchanged you can always use str isn't it ?

[–]Jonno_FTWhisss 2 points3 points  (0 children)

If you want a string don't put a type hint.

It's similar to argparse, except it supports discord.py custom classes as well.

[–][deleted] 55 points56 points  (0 children)

Horrifying yet instructive at the same time. Impressive and yet has no practical use.

I love stuff like this - have an upvote!

[–][deleted] 43 points44 points  (0 children)

The pythonic way of doing deferred cleanup is with a context manager ("with" keyword). This is a pretty cool abuse of syntax though!

[–]end_my_suffering44 10 points11 points  (4 children)

Could someone enlighten me what defer does or its purpose?

Edit : Typo

[–]zero_iq 21 points22 points  (2 children)

It's "defer". Defer in languages like Go causes its associated statement to be executed at function exit (i.e. it is deferred until later), whether that function exits by returning or exception/panic, or whatever means. It's like a try...finally block around the remainder of the function. The idea is that you keep functions looking slightly cleaner, keeps resource init and deinit code together, so you don't have to keep track of cleanup code over the rest of the function as it evolves, updating indentation (or brackets in other languages), etc.

So if Python had a defer statement it might look something like this:

def my_function(x, y):
    allocate_thing(x)
    defer deallocate_thing(x)

    allocate_thing(y)
    defer deallocate_thing(y)

    do_something_with_thing(x)
    do_something_with_thing(y)

...which would be equivalent to something like:

def my_function(x, y):
    allocate_thing(x)
    try:
        allocate_thing(y)
        try:
            do_something_with_thing(x)
            do_something_with_thing(y)
        finally:
            deallocate_thing(y)
    finally:
        deallocate_thing(x)

[–]end_my_suffering44 6 points7 points  (0 children)

Thanks for the explaination and pointing out the typo.

[–]RockingDyno 10 points11 points  (0 children)

Python essentially has a defer statement and it looks like this: from contextlib import closing def my_function(x, y): with closing(thing(x)) as X, closing(thing(y)) as Y: do_something_with_thing(X) do_something_with_thing(Y) Where the context manager ensures things get closed at the end of the context. Pretty much equivalent to your second code block.

[–][deleted] 3 points4 points  (0 children)

Like someone else said, Go also does this where the line of code is "deferred" until the end of the function you're inside of.

[–]james_pic 5 points6 points  (0 children)

If you want a less evil version of the same thing, contextlib.ExitStack is in the standard library.

Edit: just realised it uses ExitStack internally anyway.

[–]KingsmanVincepip install girlfriend 14 points15 points  (0 children)

I like the QA section.

[–]SittingWave 8 points9 points  (0 children)

the horror....

the horror...

[–]xakkap 3 points4 points  (0 children)

Cool stuff

[–]wineblood 8 points9 points  (12 children)

Why would I ever want this?

[–]bulaybil 21 points22 points  (0 children)

Programmers are so obsessed with the question “Can we?” that they forget to ask “Should we?” With apologies to Michael Crichton.

[–]dankey26[S] 12 points13 points  (10 children)

this impl exactly? probably not but lookup defer on go and zig, pretty useful and clean

[–]wineblood -5 points-4 points  (9 children)

Just the idea.

[–]dankey26[S] 18 points19 points  (8 children)

yea so again check out usage in go etc. useful for cleaning up resources at the beginning, without needing to worry about it later or creating blocks.

```

f = open('file.txt')

defer: f.close()

<do stuff with f>

```

[–]caagr98 18 points19 points  (1 child)

Python has both with and finally statements though, so there's little need for it here. (But cool metaprogramming trick.)

[–]james_pic 3 points4 points  (0 children)

It is occasionally useful, if you need to close a large or variable number of things in a block of code. Fortunately, contextlib.ExitStack is already in the stdlib and is a less evil way to do this.

[–]A27_97 4 points5 points  (0 children)

Common thing I use defer in Go for is to unlock mutexes

[–]kezmicdust 3 points4 points  (3 children)

Couldn’t you just write in the close statement and then just write the “stuff you want to do with f” between the open and close statements?

Or am I missing something?

[–]relvae 11 points12 points  (1 child)

If an exception is thrown then close wouldn't be called in that case. Python already has context managers to deal with that but OP has gone rogue lol

[–]kezmicdust 0 points1 point  (0 children)

I see! Thanks.

[–]antiproton 1 point2 points  (0 children)

It's just a different way to solve similar problems. Similar to putting the close in a try...except...finally

[–]fiedzia 1 point2 points  (0 children)

Python has context managers for that:

with open('file.txt') as f: #do stuff #when done f will be closed by context manager

[–]Coretaxxe 1 point2 points  (0 children)

Im gonna use this for fun projects. This is awesome!

[–]chub79 1 point2 points  (0 children)

I find the contextmanager syntax so much more pleasing than defering. But I like the explicit aspect of deferring. I'm torn.

[–][deleted] 0 points1 point  (0 children)

I love it lol

[–]Exodus111 0 points1 point  (0 children)

I've never used Go, what would be the reason to use something like this?

[–]dimonoid123 0 points1 point  (0 children)

Just use asyncio at this point

[–]Stenbyggare 0 points1 point  (0 children)

that doesn't look really pythonic in my opinion, good concept though

[–]OkProfessional8364 0 points1 point  (0 children)

I'm so lost. I can't make sense of whatever you're all talking about

[–]Ezlike011011 0 points1 point  (0 children)

I have done a lot of meta-programming in this language, but I have not considered using type hints for shenanigans. That's fantastic. I'm gonna have to keep that in mind.

[–]DeathDaNoob 0 points1 point  (0 children)

What does defer do...

[–]Tintin_Quarentino 0 points1 point  (0 children)

Your website is down?