4 ms·
It would be nice if in Python you could define new operators instead of overloading existing ones. It would make matrix multiplication look nicer. I'm thinking
by jkabrg 8y ago
It would be nice if in Python you could define new operators instead of overloading existing ones. It would make matrix multiplication look nicer.
I'm thinking it could look like this:
import numpy as np
def M1 %*% M2 with same precedence as *:
return M1.matmul(M2)
foo_matrix = np.matrix([[1,1],[1,1]])
bar_matrix = np.matrix([[2,2],[2,2]])
print(foo_matrix %*% bar_matrix)
Also, it would be nice to have a pipe operator `%>%` such that
foo %>% f()
is equivalent to
f(foo)
The alternative is to make f a method of foo, and then you can write
foo.f()
But what happens if I don't want f to be a method? I just want the style of writing the f after the foo, but I don't want the baggage of OOP. Is that anti-Pythonic?
- misnome 8y agoI know your question isn't specifically about the exact example, but are you aware that they added the @ operator for matmul in 3.5? I think anything general would probably be considered as cluttering up the grammar or non-pythonic. Completely custom unicode operators was something I loved about Swift (open to abuse, but really useful in moderation).
- Franciscouzo 8y agoMaybe you already know, but the @ operator already exists for matrix multiplication.
- ben509 8y agoI think the problem with custom operators is they have custom associativity and precedence. Not saying this is a deal breaker, just the biggest issue you'll run into. In toy examples, you can see all that because they'll show the declarations. Here's a case[1] trying to explain monads by showing a simpler example: parseP5_take2 s = matchHeader (L8.pack "P5") s >>? \s -> skipSpace ((), s) >>? (getNat . snd) >>? skipSpace >>? \(width, s) -> getNat s >>? skipSpace >>? \(height, s) -> getNat s >>? \(maxGrey, s) -> getBytes 1 s >>? (getBytes (width * height) . snd) >>? \(bitmap, s) -> Just (Greymap width height maxGrey bitmap, s) Is all that left or right associative? Generally, to read an expression when you have custom operators, you have to dig up the associativity and precedence. That's why I see many newer libraries avoiding custom operators, [2] vs [3]. I think you can do custom operators in a language, you just need to require that the associativity and precedence be declared in import. I'd also have named levels of associativity, so maybe: import MyModule.Foo.Bar (op(!$!, additive, right), ...) Now, that's some boilerplate in your imports, but for readability, it'd be huge. And automated tools can tweak stuff like this. [1] http://book.realworldhaskell.org/read/monads.html http://book.realworldhaskell.org/read/monads.html [2] http://hackage.haskell.org/package/pretty-1.1.3.6/docs/Text-PrettyPrint.html#g:6 http://hackage.haskell.org/package/pretty-1.1.3.6/docs/Text-... [3] https://hackage.haskell.org/package/prettyprinter-1.2.1/docs/Data-Text-Prettyprint-Doc.html#g:17 https://hackage.haskell.org/package/prettyprinter-1.2.1/docs...
- hodgesrm 8y agoOn my first C++ project we enthusiastically implemented customized versions of operators like '+' on new types. It turned out to be really confusing for exactly the reasons you mention. Operator overloading in general only seems to be practical for mathematical types like sets or sequences where the rules are well-defined thanks to generations of people thinking about them. Yet, even the set case works poorly for C++ because in addition to associativity new operators also inherit the built-in operator precedence rules. For example should * take precedence over + in set operations? (Assuming you implement * for cartesian product and + for union.) Maybe C++ has changed since I used it but this sort of thing really gets in the way of writing correct code.
- ktpsns 8y agoFrom my feeling, C++ makes it verbose to overload operators in every possible context. Thinking of Foo::operator()+, there are all these border cases where custom overload functions have to be provided (such as "(int) + (Foo)", "(Foo) + (int)", "... += Foo", etc.). I assume that in other languages it is probably simpler to fully implement own operators.
- hodgesrm 8y agoExactly. This part of C++ always felt experimental to me--something that was plausible to try but ended up digging a hole with a lot more complexity than the language designers perhaps intended.
- ASalazarMX 8y agoWhy not a repipe gluing operator so that foo%$%bar %>%>% f() is equivalent to (f(foo(bar)), f(bar(foo))) I'm just kidding, but please no more cryptic symbols in Python's syntax, if it can be solved with functions instead. We have other languages to scratch that itch. I haven't read the PEP, so I don't know the trade-offs, but I'm not loving this (:=) syntactic sugar either.
- ericfrederich 8y agoCheck this one out. https://github.com/borntyping/python-infix https://github.com/borntyping/python-infix
- joshuamorton 8y ago>Also, it would be nice to have a pipe operator `%>%` such that You can absolutely define a pipe operator to apply functions. Something like class Pipeable(object): def __init__(self, f: Callable): self.f = f def __or__(self, other: Callable): return Pipeable(other(self.f)) Then use it like composed = Pipeable(foo) | f Replace `|` with `>>` if you feel that conveys what you want better. Libraries already exist which provide this interface. >Is that anti-Pythonic? In a sense, yes. Operators and methods are defined in terms of objects, not in terms of abstract types.