Open JesterXL opened 3 years ago
Hey, thanks so much for the kind words and I'm really glad that you find the project useful!
Let me start with my now standard apology for taking so long to reply. Life's been hectic lately so pymonad, sadly, tends to get pushed to the side.
I like this idea and I'm pretty sure there are similar functions in haskell (for example) for doing this kind of thing. I'm a little wary of the mixed type arguments since that's not something you would typically be able to do in "proper" typed functional language and allowing mixed types seems like it's asking for errors. On the other hand, the 'then' method already blurs that line somewhat so maybe it's not such a big deal as long as it's documented. Little "quality of life" improvements like this can certainly be handy.
I think this might actually be as simple (famous last words) as implementing monoid instances on everything. I have vague recollections of a function which turns a list of monoids to a monoid of a list, which is basically what you're after here. That wouldn't allow for mixed type arguments but should be fairly easy to extend to that use case. And then it would work with all, or at least most, of the existing monad types.
Thanks for the suggestion! I'll try to work on it in the next few days and let you know what I come up with.
Hah, dude, even if we weren't in a global pandemic, noooo expectations and no worries.
Didn't really think of the typing. On the one hand, I like how dry/returns expects you to use MyPy and enforce typings. However, no Python people where I work use it. Like one person other than me used async/await once. So I feel like even basic using of Typings just ain't happening. Those not using Go are happy with Python's dynamic nature.
However, having that enforce the type rules is interesting. The challenge there is how do we combine things? For example, if it becomes hard to do so, then I'd just end up using Result for everything instead of Maybe. I just figured both Nothing and Error were both "left type things", but on the other hand, you'd not want to combine them. It's the all use case which is hard.
Like, if I need a configuration from environment variables, a local JSON parsing service, and a value, the first and last can't really fail because I'll have a default; only the 2nd would be bad.
# No env? No big deal.
def get_environment():
if os.environ.get('env') is not None:
return Just(os.environ['env']
return Nothing
# Dude, this fails, we're toast.
def get_endpoint():
try:
url = json.loads('config.json').read()['endpoint']
return Ok(url)
except Exception as e:
return Error(str(e))
def get_log_prefix():
return 'testing-'
Now, normally, you'd go:
env, url, prefix = all(get_environment(), get_endpoint(), get_log_prefix())
But some interesting behavior here, and I'm biased by JavaScript so bear with me.
What happens if get_endpoint
fails? I'd assume url
would be a Result.Error(...)
in this case. Then I'd have to unpack everything.
... but why do I have to unpack? Why I can't it just flatMap
like Promise does?
Because we don't have higher kinded types and I had to emulate them in Python, Jesse... Oh, right. That goes back to your type argument as it's hard to support all safely.
BUT, let's say you did. What happens then for get_endpoint
? Does it raise an Exception? In dry/returns or Rust, you'd call unwrap
and either a value would come out, or it'd raise an Exception. JavaScript Promise, about the same using then/catch.
Folktale assumes you map & chain the same types like you mentioned, so it's on you to unwrap or re-wrap when you need to combine, which is probably why Quil didn't create combine methods like all/then, etc. HOWEVER, she does have various `Maybe.fromNullable
or maybeToResult
type methods. Perhaps those combined with Maybe.all
and Result.all
would give you a fighting chance.
@jasondelaat DUDE https://www.python.org/dev/peps/pep-0636/
This changes everything!
Yeah, I've been meaning to look into that for a while. Looks like a great addition to the language.
Two things.
First, I really love this project, and have been teaching FP to programmers using Python. While I've been a huge fan of dry/returns, I keep coming back to PyMonad, especially the new version with .then. Thanks for all the hard work, it's changing lives.
Second, curious about y'alls thoughts for an all construct. Putting things in tuples/lists and then extracting then in-between thens is quite verbose. For example, in JavaScript, we can do multiple values:
And can mix and match values too:
It'd be nice to have both an all for
Maybe
, and all forResult
, and have them work within athen
. For example, here's pseudo all for Maybe:Maybe the results should contain the Just, but you get what I'm saying. There's no way to do 2 things at once which means we'll get tuples/lists that have mixed mixed values and Maybes/Results and it's quite painful to debug without types. Maybe
all
isn't such at a great name. Likeall
could get all values, but Nothing and Error would stop, or maybe we could enhance .then to take lists of Maybe/Result?Anyway, I'm used to Promises handling this kind of stuff, and Folktale.js v2 in JavaScript has this same problem; there's no way to do multiple things at once.