Nope, it could throw. What could it throw? We don't know. There are many applications where this magnifies the dangers of any existing technical debt. One could desire strong exception-proofing of results without needing to go "all in" to a borrow-checked regime like Rust.
The Koda approach is lightweight and doesn't enforce this, but one could imagine a static checker that says "any function that returns a Result, and calls any function that does not itself return a Result or does any other risky operation, must have an exhaustive top-level try-except block, or be wrapped by a decorator that does the same."
Then, you could guarantee that all code explicitly handles exceptions, while still accessing the full Python library ecosystem - you can use non-Koda-wrapped code at any time, you just need to handle its exceptions.
Is this overkill? Possibly. But I can think of a handful of bugs offhand that this would have caught in our codebase if enforced.
All above is strongly imo of course since I only write Python and am just learning Zig.
Isn't a comprehension just a map/filter by another syntax? You can re-create them from a list comprehension pretty simply:
`map = lambda data, map_fn : [map_fn(x) for x in data]`
`filter = lambda data, filter_fn : [x for x in data if filter_fn(x)]`
Then, why even have the lambda-translations above since the comprehension is already "Pythonic"? Also, you have functools (https://docs.python.org/3/library/functools.html) which you can use for several functional programming paradigms as well.
Functional programming isn't what you call the techniques (map/filter/etc.) it's how you are programming that makes it functional. You can still use functional techniques in Python to make things a bit easier to reason about. A lot of the things people thing about functional programming aren't inherent to the actual functional programming paradigm: like laziness or immutability.
Python is fine with maps and filters, and has lazy calculations.
x = [i*2 for i in range(5)] #[0, 2, 4, 6, 8]
y = [i for i in range(8) if i%2 == 0] #[0, 2, 4, 6, 8]
z = [[1],[2],[3],[4]]
w = [j for i in z for j in i] #[1,2,3,4]
t = {str(j):j*2 for j in w} #{"1":1, "2":4, "3":6, "4":8}
#reduce (note the generator (i for i in range(20))... that's a lazy list essentially)
reduce(lambda acc, x: x + acc, (i for i in range(20)), 0) #sum(0 ... 20)
#get max value from a bunch of numbers:
f = max(i for i in range(30) if 30 % 3 == 0) #max(0, 3, 9, 12, 15, 18, 21, 24, 27, 30) = 30
#basic recursion
def myreverse(x: str) -> str:
return x if len(x) <= 1 else x[-1] + myreverse(x[:-1]) #O(N) on each slice, (However x[-1] is O(1) unlike the haskell list).
#qsort haskell style
def qsort(l: List[int]) -> List[int]:
return l if len(l) <= 1 else qsort([i for i in l if i <= l[0]]) + qsort([i for i in l if i > l[0]])
Python also supports pattern matching shown here: https://www.python.org/dev/peps/pep-0636/Additionally lambda syntax, aka python anonymous first class functions HAVE to be functional.
f = lambda x: x + 1
There is no procedural concepts like variable assignment allowed in a python lambda. This is done because they wanted to force the python lambda to be properly functional and short.The type hints that python uses are also quite good with support for generics sum types and even type level programming (however your type checker needs to support it too and basically none of them do right now) You can literally assign a type to a variable in python. See: https://mypy.readthedocs.io/en/stable/cheat_sheet_py3.html
I would say the main weakness in python is that the IMPLEMENTATION is not well suited for functional programming (but the syntax VERY much is). Tail slices on lists like x[1:] cost O(N) and that's the biggest issue right now imo. It makes some valid solutions on leetcode exceed the time limit because it adds and additional N complexity on every slice when the head and tail functions in haskell cost O(1)
I'm sure some library can easily provide the underlying functional primitives python needs to be faster. Actually on that note, does anyone know of a library that solves the head tail slicing problem I described above? Probably should use the python deque, but the api for that is inherently not functional.
There's no need to construct a static type to hold both possibilities because variables can already hold all types.
0: https://www.greenbird.com/news/railway-oriented-programming-....
foo | None
is not a tolerable replacement for Maybe(foo)
When None is a valid value of type foo.Which is obviously true when foo is NoneType, but more to the point is true when foo is (bar | None).
Maybe composes, “... | None” does not.
[0]: https://en.wikipedia.org/wiki/Tony_Hoare#Apologies_and_retra...
x.map(f) means "apply the function f to the value within x, where x is a container of some sort"
[].map(f) would do nothing, because there is nothing to pull out.
['adam', 'bill'].map(f) takes 'adam' out of the array, applies f to it, and puts it back in the array, then does the same with 'bill'.
Just(5).map(f) takes 5 out of the Maybe, applies f to it, and puts it back in
nothing.map(f) returns nothing.
So we can map functions to values in Arrays, map functions to values in Maybes and even map functions to values in Eithers. Each time the function doesnt care that the value is in a Maybe or an Array or an Either.
The approach taken is a bit different I think, since they rely heavily on `mypy` plugins to reach type safety and functional constructs otherwise impossible to get, without runtime inspections.
0: https://github.com/dry-python 1: https://github.com/dry-python/returns
1
===================
user: Optional[User]
discount_program: Optional['DiscountProgram'] = None
if user is not None:
balance = user.get_balance()
if balance is not None:
credit = balance.credit_amount()
if credit is not None and credit > 0:
discount_program = choose_discount(credit)
2===================
user: Optional[User]
discount_program: Maybe['DiscountProgram'] = Maybe.from_optional(
user,
).bind_optional( # This won't be called if `user is None` lambda real_user: real_user.get_balance(),
).bind_optional( # This won't be called if
`real_user.get_balance()` is None lambda balance: balance.credit_amount(),
).bind_optional( # And so on! lambda credit: choose_discount(credit) if credit > 0 else None,
) if (balance := user.get_balance()) and (credit := balance.credit_amount()):
discount_program = choose_discount(credit)
(Omitting the credit > 0, and without getting into other fundamental issues with the implied design, since it's just a readme example.)