4 ms·
Haskell has a neat solution for dealing with operators/infix. - Anyone can define their own operators and set their precedence. - Any operator can be made int
by chancho 18y ago
Haskell has a neat solution for dealing with operators/infix.
- Anyone can define their own operators and set their precedence.
- Any operator can be made into a normal prefix function by putting parens around it. x + y = (+) x y
- Any function can be made into an infix operator by putting backquotes around it. mod x y = x `mod` y.
Sure you get a lot of people defining goofy operators: |||, .&., >>>, etc, but there are good API search engines ( http://www.haskell.org/hoogle/ http://www.haskell.org/hoogle/ , http://holumbus.fh-wedel.de/hayoo/hayoo.html http://holumbus.fh-wedel.de/hayoo/hayoo.html ) that will look these things up for you. They're just functions with funny names. A good API will not abuse this ability, but it can come in handy. If you're going to have infix operators at all I think this is the most elegant way to do it. Having just a few special operators is a kludge, better to go whole hog like Haskell or not at all like Lisp.
- jmillikin 18y agoAnother great part about Haskell's operators is that, like other functions, they can't be "overloaded". They are defined for a typeclass (Java users: read as "interface"), and trying to import different definitions for the same operator is a compile-time error.
- chancho 18y agoWell I think you're conflating the simple operator syntax with broader issues of static typing. There's no reason you couldn't have Haskell-style operators in a Python-like language, where the definition of each operator is given by a method of its first argument. The 'no-overloading' rule of Haskell is kind of annoying. You can import different definitions of the same function, its just that you then have to disambiguate with a prefix. Like 'List.foldl (+) 0 xs' versus 'Map.foldl (+) 0 xs'. This is made more annoying by the fact that core functions such as foldl aren't typeclass methods. AFAICT the only reason you are forced to disambiguate is because otherwise the compiler would need to use Prolog-style backtracking to typecheck your code on account of type inference. It's perfectly possible, its just that the designers didn't want to force this huge requirement on implementors. Plus a lot of people such as yourself feel that having multiple functions with the same name is a bad thing. I personally don't, as long as they have different types and the compiler is able to disambiguate which one you are referring to based on that type.
- dkarl 18y agoThere's no reason you couldn't have Haskell-style operators in a Python-like language, where the definition of each operator is given by a method of its first argument. That's how Scala does it. Scala also lets you define new operators and use infix notation for methods, which gives programmers no excuse for violating conventional operator semantics. (In C++, the limited choice of operators tempts people to commit such atrocities as using + and * to denote non-commutative operations.) Operators like + and * seem to cry out for generic treatment a la Common Lisp, but I don't know how well that technique scales to large codebases. Java's single-dispatch model is limited, but it seems to scale acceptably.
- jmillikin 18y agoPython, by necessity, is more Haskell-ish. I was primarily considering Haskell as opposed to C++, where multiple functions share a name and differ by parameter types. This sort of situation is a true nightmare to debug, and I believe is responsible for some of the backlash against user-defined operators in general.
- chancho 18y agoI don't see how Haskell typeclass methods are any different from C++ overloaded functions in this regard. In both languages you refer to a concrete function by its signature rather than its name. Eg in Haskell you'd have "(+) :: Int -> Int -> Int" or "(+) :: Float -> Float -> Float". You have this additional way to refer to the abstract version, "Num a => (+) :: a -> a -> a" but I don't see how that helps in debugging, other than to simultaneously set breakpoints on all the concrete functions which constitute the abstract method. Also C++0x concepts bring these two frameworks closer together. On the larger issue of free-for-all overloading I mostly agree with you: it causes unnecessary confusion. I'm comfortable with it and like it in my own personal code but I could see how you would want to avoid it in larger projects.
- SamReidHughes_ 18y agoC++ requires that all overloaded versions exist at compile time; Haskell allows them to exist at runtime. For example, in Haskell you can make a datatype like data Foo a = Blah a | Bar (Foo [a]) and you'll have no trouble making a function "showFoo :: Show a => Foo a -> String" using straightforward programming. If you try to do that in C++ you'll get infinite template recursion. Also, C++ templates don't restrict you to match the right type signatures, the way Haskell typeclasses do. But other than that, they are pretty much the same kind of thing and are used in the same way.