Hey, returns author here! Thanks a lot for showcasing my library, I am happy to answer any questions people have in the comments :)
@ArjanCodes25 күн бұрын
Thank you for your great work! It’s really nice to see an implementation of these ideas for Python.
@mattk691023 күн бұрын
How would I see tracebacks if I use this library?
@jonbezeau312417 күн бұрын
What is it specifically that we gain by avoiding exceptions? There's still flow control that has to be implemented around error cases.
@sobolevn17 күн бұрын
@@jonbezeau3124 You gain an API where you cannot ignore an exception so easily. This way you can clarify very important exceptions to handle. Like when you create an order and there's not enough funds to do that. Or not enough products in stock. Or whatever. But you will clearly show that these things are really important and must be handled.
@sophiophile16 күн бұрын
Can you outline a real-world case where this has significant advantages? I see this as something that would be fun to help teach noobs about execution flow, typing, etc if their only experience is Python. And when writing code for personal projects, it really does enhance readability (which is impressive for python). But I can't imagine a place where this would make sense when working on a large collaborative codebase in the real world (where you need to be writing unit tests, your IDE has a language server, etc). Maybe that isn't the intention of the project, and if so- I think maybe that should be emphasized. Because as an educational or convenience module, this is awesome.
@squishy-tomatoАй бұрын
it's an interesting package, but I don't see myself trying to emulate errors by value in Python anymore than exceptions in Rust. Deviating from how the language itself - and all libraries you rely on - uses may create more problems than solutions.
@Quetalocatl28 күн бұрын
If you know Rust, the power of the result type is that you can handle all exceptions at compile time and get rid of any runtime errors. However, Python lacks type checking here, so you don’t see any benefit. Anyway, Rust is future-proof, and pattern matching will surely be the best practice. I believe using it earlier cannot be a bad thing.
@squishy-tomato26 күн бұрын
@@Quetalocatl I know Rust, and I see the point of it there. But it'll never work well in Python because the language and everything around it is built with exceptions. That makes returns as values in Python an anti-pattern and really unlikely to catch on.
@senhajirhazihamza771825 күн бұрын
rustifying python is not pythonic, I agree, the right tool for the right problem, rust and python don't have the same targeted scope of problems, at a certain point we just gotta accept and switch to Rust if we target a problem more suited for rust than python or maybe wait for Mojo to mature
@edwardcullen173924 күн бұрын
@@senhajirhazihamza7718 "Python is slow!" "If you need high performance, why are you using Python, instead of C?" Gist of a real conversation with Guido.
@jakobullmann758622 күн бұрын
@@QuetalocatlWell, if you don’t use a type checker, what you say is true. But if you use MyPy or Pyright, you should get all the benefits. Or am I missing some subtle detail here?
@anamoyeeeАй бұрын
7:28 to match an instance of Nothing you probably want to use case Nothing():, otherwise if you use case Nothing: it will think you're trying to make an irrefutable case with Nothing as a variable name for any item that didn't match any other case. While this technically will work because Maybe has only two cases: Some() (which is already matched) and Nothing() it's most likely not what you meant
@max.caimitsАй бұрын
Documentation suggests using `case Maybe.empty:`
@2010edward1978Ай бұрын
thanks Arjan, it's always fun to learn smth new from you on Friday :)
@ArjanCodesАй бұрын
Glad you liked it!
@elieobeid77Ай бұрын
What I do is def func(): return status, result just like go, and I use exceptions just so I can log exceptions. Exceptions are useful for logging perpuses anyway, and if the function is written correctly, you can have a status of true or false and then decide what you want to do with the data. I find this to be the cleanest approach. You get the best of all worlds
@gentlemanbirdlakeАй бұрын
yes and this also maps well onto http response codes if needing those effects at the end of the chain
@shubhamkaudewar7297Ай бұрын
Yes It is better to capture and log so that we can know what is happening
@bfdhtfyjhjjАй бұрын
The problem with this approach is that you need to write a check if status == "success" at all levels where this function is used. And as a result, the code turns into a hell of 'if's
@dankprole7884Ай бұрын
Something feels very wrong with wrapping my return value with "Maybe" 😂
@chudchadanstudАй бұрын
Takes up more memories. Result is a Union type. It will take up as much memory as the largest type.
@edgeeffectАй бұрын
I'm working on a command line program at the moment which processes a text stream and provides a report of (e.g. validation) failures at the end. I'm continuing to use exceptions for cases where the system itself has failed (in your example, database connection failures) - unrecoverable errors. But for the user errors that are compiled in the report at the end, I'm using `result` style errors. It SEEMS to be working fine so far.
@eryqeryqАй бұрын
Validation with error-reporting seems like an excellent use case for this
@uqamsАй бұрын
That's right. In your case, "error" is a domain-specific thing, it's no different from any other things defined in your domain, and it should be modeled the same way (using dataclasses or smth). Exceptions, however, are an implementation detail.
@teluial18 күн бұрын
The reason “case Nothing” was giving you an error is because it was a binding, not a pattern. That is, it was like “case _:”. To match against a class you must write “case Nothing():” Alternatively to match against a value you must use dot notation: “case result.Nothing:” like an enumeration, but it works other things like globals in modules.
@Descensusadinferi6 күн бұрын
We started using Returns for quite a while in our Production Python Code. As we have been using type annotations and static type checkers for quite some time, annotating our code with clear Success/Failure flags wasn't too cumbersome. What started the adoption was actually the 'lack' of easily annotating Exceptions in our code, so that unhandled exceptions could sneak unfortunately into some parts of our codebase, especially code that wasn't unfortunately tested as thoroughly as necessary. With ROP, and especially type annotations, such mistakes could be identified in the development stage already (directly by the developer) and not just while the test or review phase.
@pythonlibrarian22421 күн бұрын
The domain informs which paradigm will be most useful. I usually start out with non-OOP and upgrade to OOP when I find an OOP-like problem. I think functional programming and this sort of "forcing" the user to think about contracts makes the most sense if you're in a domain where errors are easy to make, exceptions are likely to happen. If you never go down the alternate path of the railroad, carefully building all those side paths would be a side quest.
@yibowei9636Ай бұрын
After trying returns, I decided to stay away from the IO part. This is very contagious and quickly makes your code never understandable by anyone but yourself, adding a whole layer of complexity and mental load. The result and option are fine
@RogerValorАй бұрын
Oh, and one downside of this vs. exceptions is the missing stack trace, so you should probably always return a captured exception in your Error, so you can unwind it
@thinksiamallamacomАй бұрын
This video came just in time! We use a lib (u-case) in Ruby that provides us a service object framework that uses almost identical behaviour. I love using it and was searching for a similar lib in Python lately. Thank you for finding it for me ☺️
@GOTHICforLIFE1Ай бұрын
Still a big advocate for Go if you want the middleway between Python and Rust. In my opinion Python does this more than well enough with Pydandic for raised errors on incorrect data types/validation of input values on new instances or simply isinstance() to validate expected types (assuming you use class types), and custom errors for better explanation of raised expected errors. So it might be boring from the PoV of a Python developer to make so many custom error handlers and type validations, but that is literally what you do in some way or form in every other language. Only difference is that they are statically typed, so you don't really have a choice.
@DudebeinaguyАй бұрын
I wish scala got more love… Its capabilities are so vast and it’s not overly complex to get started once familiar with python. You can really ease into the complexity. Its use cases are vast with libraries and frameworks for pretty much everything. I just think it’s a good place to move from python that give a world of new features, capabilities, and safeties that you don’t get in python even if you try to make python more “functional” and “type safe” And it’s not as large of leap like rust is and it has more general purpose use cases for most people. Rant over. Great video by the way! lol
@quillajaАй бұрын
The thing is that Python has so many useful libraries that make non-trivial things trivial (in my case, geoprocessing and lidar data manipulation) that to switch languages means you'd ALSO have to re-write most of those.
@DudebeinaguyАй бұрын
@ I agree, the breadth of python libraries is incredibly vast and makes getting things up and running very easy. I’m not saying python is bad by any means, I use it in my day to day work and for personal projects and PoCs, but its overused imo and people start trying to squeeze features out of it that are half baked when there are other languages much better suited. As with everything it all depends on use case, I just think scala is very underutilized in particular
@digirydeАй бұрын
What is the benefit? I don't really see it. Maybe due to me being very used to try... catch. I did grow up on not having the try catch tools (late 70s), but now that I do... Hard to see not using them anymore.
@Zer0DesignsАй бұрын
The benefit is being more explicit about functions being able to return an error. You could (conceptually) try to read the Rust book chapter on errors (it's well explained). Since this is a 1:1 replication of how errors are handled in Rust.
@edwardcullen173924 күн бұрын
@@Zer0Designs Holy shit balls, the arrogance of youth...
@edwardcullen173924 күн бұрын
This is what people, fundamentally, are missing - _why_ exceptions came into being in the first place.
@digiryde23 күн бұрын
@@Zer0Designs I have been thinking about your post stating "The benefit is being more explicit about functions being able to return an error." I don't see how try except is less granular in nature. Exceptions and the messages and data that go with them can be as granular as you want. I, personally, see no limit to the amount of information or the specificity of that information from an exception. I am aware of the work you are referring to about Rust, so I will give it a read over the coming weekend. It may be that having had to write custom error management systems before exceptions has tainted my view of doing so. :)
@Zer0Designs23 күн бұрын
@digiryde The benefit is explicitly rating functions as functions that could produce errors. This helps Rust because the language is built up in a way that every error needs to be handled. Try except can handle errors but it doesn't force you to. Try: Somefuc() Except: print("WHOOPSIE") Is not error handling. The error needs to be named and handled. I'm not saying this isn't possible. But being explicit & deterministic in error handling is what makes it possible for Rust to be so fast. The same concepts here just are applied to Python, not that I think this helps if you're working with Python devs, but being explicit about functions returning an error in their return typehint (->) could definitely be a benefit. Instant: oh this function can fail. Instead of some nested raised error. Rewritting Python code, I would not advice for the same reasond others managed (mostly packages use try except)
@Han-ve8uhАй бұрын
1. 9:56 Is the return type always in the order of sucess, failure and not failure, success? What if there are multiple types of failures? This video only showed 1 failure path in all examples. Are the failures always str type, like Failure always returns string (i assume this string refers to "Invalid number format" 2. 10:05 Why def add_ten only has 1 path success but has Result[int, str], unlike def divide with 2 paths using if-else, or def parse_number with 2 paths using try-except.Is the str defined implicitly somewhere? 3, 10:25 "add_ten is not going to be called automatically"? Why would we want such complexity where the code is written but reader has to reason whether it's called or not? In this video "NaN" is hardcoded which makes the failure obvious, but it's harder to reason if the input was a variable. Do functional programmers think of if-else whenever see bind()? 4. What does @safe contribute? It isn't self document what are the various ways failure can occur, and the programmer still has to know what exceptions nested in Failure to catch. How is this better than doing try-except directly? 5. 18:03 how does railway programming using returns make code more predictable or allow explicit handling of non-values? With or without returns, any code seems just as unpredictable to me. Predictability is a function of the developer's familiarity with the libraries he is using, and the inputs that would come in. Using try-except also explicitly defines all the intended error paths to handle, minus the extra burden of thinking am i dealing with a container or the value inside when using returns.
@bubblefoil10 күн бұрын
1. If there are multiple types of failures, your function is either too complex and should be broken down or it should define its own failure type. 2. add_ten returns Result[int, str] instead of e.g. Success[int] because the second would break the chain when used in bind after parse_number. You'd loose the Failure type and couldn't deal with it at the end. Although personally, I'd define add_ten as a plain function int -> int, which I'd pass to Result.map() instead of bind() - I'm just assuming map() exists and is used to apply functions that don't return a Result. This way, you keep the original Failure type but can change the value or even type of Success. 3. Yes, the concept of a monad is a lot about hiding the chain of if-else which you know always needs to be there in a data pipeline with uncertainties. 4. I don't know and don't like it. Where I can see a valid usage is rather inline at a call site to wrap something you know how it can fail and want to use in your pipeline, like e.g. get_url().bind(safe(requests.get)).bind(check_status)... 5. Often, the only thing you can do with failures is log them and stop processing, possibly returning a 500 to the client. In these cases, you don't need monads and you wouldn't even need try/except if you didn't need to add some useful context to the logged error. However, in data processing pipelines, the situation can be different. If you need to process more items by a pipeline which can fail at various places, you often cannot just log errors for the failed items and continue processing the rest. Because you know both rails go through the whole pipeline, it could be easier to build the pipeline as a function item -> Result and do results: list[Result] = [pipeline(item) for item in items] and deal with failures at one place. As a real world example, I've used such railway for scraping a complex website with many pages and badly structured HTML with ugly surprises. At the end, I had a nice report and it's always up to me to output as useful error message as possible for failure investigation.
@vadymmudryi797820 күн бұрын
I was going to leave a comment on first minute of the video: don’t use it in production. Author already mentioned that ❤
@nothingisreal6345Ай бұрын
In principal this is a good idea. BUT: we would need a mechanism / library in the Python standard. Else you will us a library and now you are forced to use the classes that library has chosen as wrappers. With one library still not a big deal. But with multiple libraries the number of wrapper libraries might grow quickly. And for most existing libraries throwing exceptions is still the norm and they can not change it without breaking existing code. They would need to rewrite all their functions to use a wrapper library. The advantage of exceptions is that they are a normed part of the language. Another thing of interest is the performance impact of using this as a general approach. For each wrapper additional memory must be allocated. That is not a big deal if a function does something complex. But for e.g. a function sqrt(x) this would be really expensive.
@mrc1500Ай бұрын
I think that if you have a strong desire to embrace monads and combine with structural matching, why not just use Scala and get strong typing, case matching, Option and Either. As an alternative, Python lends itself very well to Go-style result, error tuples.
@eklok500029 күн бұрын
One of the first good comments in the section! 🎉fully agree. If all that is needed one might better opt in for Scala. It also has its quirks. But they’ve put much more effort and thought into all those concepts around typing, matching, error handling. They just have way more experience in that. Of course it is switching to a complete new language. But hey. If python continues on its current way and then this also holds for python itself..
@mage1over13726 күн бұрын
Let me preface this, I think Scala is an awesome language and beautiful language very well designed for data pipelines, with some major drawbacks which hold it back. First it's runs on the JVM, which makes it harder to just get something up in running, it doesn't play well with non-jvm languages, it doesn't have nearly the same library support as python particularly data science libraries. Further python can be strongly typed if you run it through a type checker like mypy as part of your build process (this just automatically done for you in Scala because it has to be compiled). Further monads are pretty natural to implement in python, you can write python to be functional just like you can write Scala to be OOP if you like. Lastly match case has been a core part of python since 3.11.
@echoexplore419023 күн бұрын
how is this a class of endofunctors if we map into abother type?
@teluial18 күн бұрын
You could also confine its use to specific layers of your code. E.g., the IO inside the handler layer of a web API, but keep all the core logic pure functions.
@samjimanАй бұрын
I honestly don't know what is wrong with exceptions. Was one of the strangest things when using Go to be honest.
@aldebaranakosАй бұрын
Its not so much the exceptions themselves but rather you have no clue what exception a function might raise
@edwardcullen173924 күн бұрын
@@aldebaranakos Does it matter? That's where people fall down. Also, have you seen all the errors your typical system call can return? To 'correctly' handle all the possible error states of something like a simple read() is insane - dozens of lines of code. The issue is one of mindset and documentation, not mechanism.
@DataPastorАй бұрын
Arjan, would it be possible to use slightly bigger fonts, so that the codes are easier to see on a mobile screen? Otherwise it is a great video. Yes, I plan to use returns in production code.
@dpo3576 күн бұрын
There are far better use-cases than the ones that were proposed, educational and cross-compatibility, for example, using Results with @safe would be a very straightforward way to implement complex and mission-critical workflows, where the final success of the workflow allows for some failures in some of the steps, and you can have centralized error handling if needed. Just using exception handling in such a scenario would riddle the code with try/except blocks everywhere, and the overall structure of the program is probably going to be more complicated. However, I disagree a 100% with what was said at 19:10, I believe it would be a terrible idea to go all in with monads in a Python program, that would just add unnecessary complexity and the language isn't really made for that, use them just where they can simplify the whole architecture of the program, remember: practicality beats purity.
@What_do_I_Think19 күн бұрын
Exceptions are an accepted standard and there is no reason to abandon them just because some really SMALL examples look more elegant. While the examples seem to show same intriguing examples, not all code just consists of print statements --- and the danger, that those containers mask problems instead of helping against them is huge. Like all other programs, Python programs must be thoroughly tested. Libraries must be understood and have to be well documented.
@entitledOneАй бұрын
Honestly I don't really see a big benefit from this library. Now I have a result object that might have a value, or it might have an exception. So now I use patter matching for value, error type 1, type 2, type 3... Why just not do the same with just catching the exceptions themselves? This isn't really solving the issue mentioned at the start, it just moves the problem around. You will still have to have either if statements or pattern matching to handle the happy path vs any of the potential error paths, while trying to force something that goes against the language. Rust has other parts which make this approach nice. Python doesn't. So now you wrap wrapper into wrapper in order to move the path handling somewhere else without much benefit.
@hannesjvvАй бұрын
Exactly. This choosing exceptions or monadic error handling in no way addresses the more important question of how to gracefully respond to the various causes of an error.
@luiseduardoballoterosado69421 күн бұрын
It gives you some things, locality: the ability to lint against non exclusive matches, and they are referencially transparent, you can know from the return type if your function might fail instead of needing to look at the implementation. Now, of course it is a trade-off, but it is definitely not a useless pattern.
@vsolyomiАй бұрын
What's more work for programmer: insuring programs' invariants or debugging for insidious errors when those invariants are implicitly violated? It's not a simple question, but in my experience it's usually the latter by a wide margin.
@biscotty6669Ай бұрын
Ty. A nice, balanced analysis. I love Haskell so it's nice to learn that Python has this library.
@echoexplore419023 күн бұрын
i hate the maybe pattern for the obscurity it has introduced into our code, as well as people thinking wrapping things in maybe successful handles branching state i think an excellent addendum to this is the primeagens thoughts on using asserts for negative space programming failures not at the top level, which will not crash the program, should be raised as quickly as possible. the biggest failure, ive seen is that devs think that wrapping in maybe and masking failures, handles their branching state. especially when not stopping to question "why do i have 2**8 states right now?" and wrapping in maybes for graceful failing our application is failing in hundreds of ways, its not just async threads colliding on our db. its the underlying state management.
@froazin20 күн бұрын
In the albeit limited cases I've needed to write really safe python in the past, I've always just gone for a go-style approach and returned a tuple with my value and an optional exception: someval, err = some_function() if err: ......
@jlp2011Ай бұрын
I don’t see this as necessarily just about exception. Say you have a handler that will “return something” on certain inputs. If condition is wrong however, you want to route to another handler. I can see this being useful. Throwing an exception is a bit off for the normal flow of code. And returning None can look a bit off if there is normally a defined type. Hmmm, will think about it. My biggest concern is that becomes a hugely coupled dependency to a relatively obscure library.
@a-yon_nАй бұрын
Something you can do doesn't mean something you should do. I’ve seen a lot of people trying to copy Rust’s Result pattern in other languages, but in fact, that’s just not working. In Rust we can use the ? operator to propagate errors for function calls that return Results, so we don't end up writing lots of useless code like we do in Golang. Imitate Result in other languages without the ? operator will just lead us to write Go-style error handling and I can tell you it's painful, and even more painful since we can wrap and unwrap errors in Golang that gives we the ability to manually record the error stack trace, which is no possible in other languages natively.
@jlp2011Ай бұрын
How does compare to say pymonad? Same thing, more clearly designed? Different?
@ordensritter3783Ай бұрын
I really like the approach. But it only help a bit. Because in Python you often do not know where I error can be raised, so whether you forget to add the try except or the Result, causes the same problem. So what Python should used at least in the core libs are Lists of Exceptions each function might raise in the return type declaration as Union.
@delir025 күн бұрын
Result doesn't have this problem if you use mypy. This is the most important difference: mypy tells you if your code doesn't process all of the bad things could go wrong
@Axman6Ай бұрын
Thanks for showing a long time Haskell dev that it’s possible to write moderately sane code in python, I’ve wanted sum-types in python for so long, and this at least shows it’s possible.
@edwardcullen173924 күн бұрын
You can write perfectly 'sane' code in Python. The assertion that you cannot make immutable objects is false. Though, it's true that, generally, you cannot force others to use your preferred paradigm (but this smells more like a 'you' problem...) The choice to enable deferred error handling (i.e. exceptions) or to force immediate error handling (i.e. mixing error in return values) is entirely a choice, even when using external code - almost all exceptions can be caught, including SystemExit, and converted to return values.
@emanuelblei7699Ай бұрын
It reminds me very much of Scala. It looks good, but it does seem like a big commitment to add this to production code.
@Axman6Ай бұрын
These types and ideas have been in Haskell’s standard library for 30 years, before Java existed.
@ivanmaglica26420 күн бұрын
I still think Java has this one solved best. It uses "throws", which forces developer to deal with it.
@eklok500029 күн бұрын
Great video! From all the arguments against the package I really only see one as relevant: the style of programming concepts in one project might be fuzzy. As this package is quite „invasive“. arguments like “you would need to think about null values” I don’t see at all as a downside or a argument against the library. Quite contrary! Wouldn’t it be good if you are forced to think about stuff like that before it goes to production…? I also don’t like arguments like that this is not so clean. It is actually way cleaner when you are used to it. I think what you are referring to is rather what one feels comfortable with - and that I fully agree to.
@anandraj2895Ай бұрын
interesting and informative , thanks arjan
@delir025 күн бұрын
I've been using Result in my python projects for a year. This is way much safer and easier approach than exceptions. Not everything is Result'ed yet, but the part that's "Resulted" became much more understandable and reliable. The only real problem we faced is the absence of error-proparation (like the '?' operator in Rust). It's not a showstopper (come on, Gophers call it a feature) but makes you write a bit more boilerplate to bubble up the errors The most important part of using Result is to use mypy. Without mypy Result is barely better from exceptions.
@PrivateKeroАй бұрын
Hmm looks like you are making longer lines instead of multiple lines. Somehow I don't see the advantage yet. Also looks a bit awful for debugging if each line performs 3 actions.
@mattk691023 күн бұрын
The main problem I see here is having to do work to see the trace back. It would be great if Returns would provide tracebacks
@maleldil1Ай бұрын
Result types in Python don't work well because the ecosystem is already focused on exceptions, so errors as values have to work around that, and the result isn't good. However, it'd be good of Optional could've been a real Maybe type, with `map`, `or`, `default`, etc. I end up with a lot of conditionals over a value being None that could be much cleaner. Still, it's hard to support those things when Python has _the worst_ implementation of lambdas out of any modern language. C++ lambdas are great, Java's are passable, but Python's are nerfed on purpose (Guido doesn't like FP).
@nlptranslationgroup226 күн бұрын
Why not to use ?? i have different thoughts to use. if i have multiple sub modules in my program, and they work individually , eg, data pipeline or training or evaluation , i can use returns library in data pipeline though training , evaluation is developed using traditional pythonic way.
@kevincodes674Ай бұрын
I'm very glad to see a library like this for Python. I have always preferred error handling in Rust, it is easier to work with in my opinion.
@worldpeace182225 күн бұрын
Nice but I wonder if anyone would use a library as this for projects that are supposed to run for a long time, while you don’t know if this library will also be supported with future updates to python. It would be a pain in the ass if you have to refactor your entire error handling …
@nuurnwui29 күн бұрын
brilliant video!
@johnidouglasmarangonАй бұрын
Is is interesting technique,
@Andrumen012 күн бұрын
If you don't know about exceptions being raised, most likely you don't know how to handle them...let them through! Exceptions are not meant to be handled unless you really know how to handle them!
@dankprole7884Ай бұрын
I would use this but i can barely convince my team to use type hints so I'm gonna stick to returning None
@edwardcullen173924 күн бұрын
It's really hard to deal with these conversations. All the reasons for using Returns are all reasons you should just be using a different language. Python is NOT "brittle" any more than any other language - any complex system, written without care, becomes 'brittle'. Issue of scale, not language. Exceptions are an excellent error reporting mechanism, that provides flexibility to developers as to _where_ they handle errors. Without exceptions, you _have_ to handle all errors where they occur. Exceptions came about because people would forget to handle errors and would then find it hard to work out where the error was occuring. Exceptions _enable_ deferred error handling, but require other things along side, such as RAII. The fundamental issue is that people are not taught that the majority of software is error handling. The discussion around "railway programming" is precisely what exceptions are for! Exceptions separate the return value from error information the most distinct of distinct code paths!!! 'People' don't like exeptions because they FORCE them to handle errors and they either don't understand the error or see it as a distraction, rather than simply part of the job.
@AloisMahdalАй бұрын
About the "adds complexity" argument: I think it would be more fair to say it moves and standardizes complexity. It's hard to see in examples or tiny projects because there is not much complexity to move yet. Any larger project used in real world, though, will inevitably keep bumping into reality, which is going to cause it to gather dozens of little fixes, handles, solutions to corner cases, logging, etc. So the question is: What are the odds that all these fixes done over years, perhaps by different people, under different amounts of stress, are actually somehow consistent? When I think about railway-oriented vs. exception-based makes a difference is that long-term the code is going to be more readable since 1. most of these fixes and handles will more likely to be done on first writing, 2. even those made later will still be in a more straightforward "language". Of course, all of that comes with a big caveat -- it only works if this style is used consistently (close to 100%) and understood very well by the team. That can be a big investment upfront but then again, I feel this is similar to learning something like Pydantic: if it's "de-facto standard" and usable on other projects, then even from a single dev career point of view, that investment will pay itself in dividends many times over. Another question is, why not just use another language altogether, such as Go (or Zig, if you really want to go deeper) which implements this or similar concept natively.
@edwardcullen173924 күн бұрын
Exceptions came about precisely _because_ people didn't "write all the fixes the first time". They are a mechanism for deferred error handling that makes it clear _where_ the error occured.
@mbk0mbkАй бұрын
Mojo jojo is comming to solve most of these limitations but its primary ment for AI, GPU architecture .
@hvulinАй бұрын
not sure this is any better then handling exceptions... at least we already know how to do that...
@jruere121 күн бұрын
"None" is a value. Receiving that instead of what you expected is as much of a problem as receiving any other unexpected value. I think you are trying to reference the problem of null in statically typed languages. Null is problematic because it is accepted for pointers of any type. This problem fundamentally does not exist if there's no type safety, since no expectation is broken. When using type annotations in python, you also don't have this problem, as None is not automatically accepted by all types.
@hoi-polloi186326 күн бұрын
"Ew. Brother ... ew!" I want to know if I can't find my user, before I start throwing it into a bunch of other functions. If I do want to be sly, I'll put my vals from get-user into a list, filter out None, and then know that my list contains users only.
@UNgineeringАй бұрын
i prefer to leave rust tricks to rust. python is meant to be quick and simple, just get it done type of deal.
@slafoos19 күн бұрын
Good content, thanks! 👍 On the Maybe/Nothing example with pattern matching I think you should have used Nothing() (note the brackets) on the second branch. Otherwise it becomes a catch-all case (that's why your example worked) and it shadows the Nothing value with a variable Nothing (which funny enough would probably always be Nothing 😅), but you don't use it (hence the squiggle from Pylance).
@ArjanCodes19 күн бұрын
That explains a lot - thanks for pointing that out 😊.
@sharp76428 күн бұрын
wait so you guys handle errors in Python?
@edwardcullen173924 күн бұрын
😂
@JurekOKАй бұрын
python is still good for prototyping. It's good for "happy path" programming and checking what if. For a deployment in error-intolerant environment, use another language that is suited for that, that's all. Can be rust, typescript, e.t.c.
@philbertzhang321517 күн бұрын
when I see Some, it brings my PTSD to rust😂
@amiganer68113021 күн бұрын
That looks like the system Rust is using.
@dew_reddit7 күн бұрын
I would choose try catch over this type of non-deterministic way of handling errors. You are just shifting the responsibility of handling errors from your apis to frontend or some other helper functions. Ultimately, the end use remains as it is. I see no benefit in this alternative approach than traditional exception handling except reduced line of codes. Less code doesn’t mean better or cleaner code. Also, exception handling is a solved problem and doesn’t need a reinvention.
@BladeAndrade21 күн бұрын
That package makes python more or less like Ocaml
@kenny-kvibe24 күн бұрын
I use True/False/None a lot, won't be switching to a package just for rust-like words. for more info about the exception try this: ``` import traceback try: raise Exception() except: print(traceback.format_exc()) ```
@AK-vx4dyАй бұрын
Java way of mandatory declaring all possible exceptions is .... maybe not worse...but...
@be1tubeАй бұрын
@@AK-vx4dy The problem with Java is that you can't include the exceptions that would be thrown by callables. So to allow higher order functions, everyone just throws runtime exceptions that don't need to be declared.
@hannesjvvАй бұрын
@@be1tube Exactly . Down the path of declaring exceptions lies madness unless the entire ecosystem is built around a formally verifiable language like GCL.
@edwardcullen173924 күн бұрын
@@be1tube True, but this is a failure of responsibility, as in I should shield my users from my dependencies. So many of these issues have little, if anything, to do with language and everything to managing the complexity of larger codebases.
@be1tube24 күн бұрын
@@edwardcullen1739 In higher order functions, the user of the function is the one providing the dependency. Consider myStream.map(strToFloat). The user of myStream is the one choosing strToFloat. So, the user should be free to choose a strToFloat that throws FormatException. The pleasant thing would be to then have the compiler understand that myStream.map(strToFloat) can throw FormatException and that the caller should either catch or declare it. Unfortunately, Java's type system can't do this, so you end up losing exception safety when you use higher order functions. The higher order functions are declared to take arguments that don't throw and everyone throws RuntimeError - which doesn't need to be declared.
@EliasHansenu7f22 күн бұрын
Even when Python isn't made for this, it opens a way to handle issues with a more linear approach. The processing of results is no longer depending on various conventions and relies only on pattern matching. I can't see why this is bad for readability, quite the opposite. What is missing is the enforcement of catching all patterns, like in Rust.
@mage1over13726 күн бұрын
Type annotations are type-checking if you add the extra step of checking the types with mypy. It's basically the same thing type script does, and technically C++ does during one of the steps of complications, the only difference is that those languages don't let you run it if they fail while mypy will only politely suggest you don't. If you politely listen, it will be the same thing as if it was "strongly typed", and allows you to make exceptions to the typing if you know better.
@partywumpus526725 күн бұрын
this relies on called library functions telling the complete truth about their contracts. Same problem typescript has. For sure not as brittle as he said though.
@be1tubeАй бұрын
Vavr for Python. I'll talk to my team, but I think they'll choose exceptions. (We use Vavr when writing Java, though.)
@vikingthedudeАй бұрын
Vavr is java turned upside down. Whats the equivalent in python
@richardcoppin5332Ай бұрын
I see this package being useful it you're writing code in Mojo to use alongside the code in Python.
@digirydeАй бұрын
Miss that biting humor. :)
@StingSting844Ай бұрын
Python is simple only for casual code. Any serious project is just flooded with projects and some syntax is just bizarre. This library is good for building your own custom library in your organisation. Everyone will say it's bloat until they want to do a massive refactor. I don't get why people complain about stricter coding standards and also complain about poorly written software
@marckiezeenderАй бұрын
Good idea, but no thanks. "Maybe" is a worse version of "typing.Optional", and wrapping every line of code in .map() methods is going to be much less readable than wrapping a whole block of code in a try block.
@balodocАй бұрын
You're making simple things complicated
@Infobiz8720 күн бұрын
Honestly, as much as I love the effort and love the idea, I don't like this since I find that it removes a lot of the elegance of python.
@santiagohal674724 күн бұрын
All of this can be avoided by using haskell
@mfeyxАй бұрын
nah... fail fast, and improve your code... if you need more info about the exception and stack just use the traceback module (format_exc) :)
@ravenecho2410Ай бұрын
stop programming in rust arjan (jk)
@AK-vx4dyАй бұрын
I can bare some exceptions but throwing exceptions on not finding index of string.....why Python, why ? To not be one sided, i have the same feud with C# .substring throwing instead of returing empty string...
@maleldil1Ай бұрын
You can use str.find, which returns -1 if a match cannot be found. It's still weird and should return None instead, but it's better than str.index.
@edwardcullen173924 күн бұрын
Because you were searching for the string and expecting it to be there. If it's not, that's clearly an error. It's 'separating errors from return values', something that used to be considered 'normal' (i.e. it is normal and good, but people don't understand why it is necessary).
@AK-vx4dy24 күн бұрын
@edwardcullen1739 I understand it, but imho not finding string is not an error, just one of possible results cases. Trying to search in not existing string (null) is error, trying to get element out of range is error.
@edwardcullen173924 күн бұрын
@AK-vx4dy "... is not an error." That depends, entirely, on your perspective. The function _defines_ that the absence of the string is an exception, which you _could_ think of as an error, but shouldn't. Forget your preconceptions, then look at it from a "what is the purest Happy Path this code could take?" then layer-on "everything outside the Happy Path is an Exception". From this perspective, a function that returns the location of a substring _should_ throw an exception, because "why are you looking for the location when the string doesn't exist?"; if you want to know if it contains the string, why aren't you using 'in' or 'contains()'? This is the "railway programming" he was talking about, just expressed differently. Does the Exception qualify as an error? That's for you to decide, not the language.
@AK-vx4dy24 күн бұрын
@@edwardcullen1739i know what about video was, my comment was partially unrelated revied memory when exceptions in Python assulted me 😜 if i assume happy path then i need two ifs one for contains second for index ;) as twisted child of assembly where both are the same and early languages with no such baroque count of functions my brain screams "suboptimal" especially in interpreted language. But i understand your view point and soon old mutants like me will extinct 😉
@wizardfix20 күн бұрын
I used to enjoy your videos Arjan, and learn useful things. Alas, now I cannot listen, because, like so many idiot sheep on KZbin, you have added background muzak, which streeses my brain and totally distracts me from the audio content. If it continues, I will, reluctantly, be unsubscribing from this channel.
@digirydeАй бұрын
Seriously. Interpreter level type enforcement and type checking as an OPTION should be something being looked at. There are many great reasons to use python. This is one of the big reasons not to.
@maleldil1Ай бұрын
Are you talking about errors as values or static typing? The former is a good idea for certain languages (e.g. Rust, Zig. Go does it poorly), but I don't think it's a good fit with for the Python ecosystem, but the latter is an obvious improvement over previous dynamically typed Python. All my new projects use strict typing through pyright (anything untyped that can't be inferred is an error), and it runs as a required check in the PR. This has improved my experience with Python immensely, and I can't imagine any serious user of the language today not using it.
@Axman6Ай бұрын
@@maleldil1I’ll have to check out pyright, I’m a Haskell developer forced to write python at the moment and it’s a nightmare trying to write code when you have no idea what the types are.
@Zer0DesignsАй бұрын
Python slowly becoming Rust lmao
@fcolecumberriАй бұрын
All of this to replicate PHP's ?-> operator
@vikingthedudeАй бұрын
monads
@bobby_ridgeАй бұрын
useless, python - traceback so usefull. This method deprives uss this python trick Wait yours discuss in comments :P
@michaelcummings7246Ай бұрын
Rust/these libs vs python: Fail early and handle once or fail everywhere often and hope you catch all the exceptions or can figure it when you don't 🤔😜😂
@Axman6Ай бұрын
These ideas come from Haskell, where it’s pretty simple to add a stack trace to an error if you want. Is there an easy way to get the current stack trace as a value in python?
@glensmith49128 күн бұрын
Handle exceptions only. Errors just mean your code is too buggy for deployment.
@michaelcummings724628 күн бұрын
@@glensmith491 In rust errors and exceptions are the same thing and you are forced to make sure they are always handled correctly but any simple errors in your code the compiler catches and have to be fix before you can ever run your program that's why its with rust that if it compiles you know 75% of all bugs are already taken care of and you are just making sure to take care of the GIGO part😉