That's I believe the main reason I find SML more attractive: it seems to use operators to a much, much lesser extent, apparently preferring alphanumeric function names in the core, and in 3rdparty libraries too.
That's I believe the main reason I find SML more attractive: it seems to use operators to a much, much lesser extent, apparently preferring alphanumeric function names in the core, and in 3rdparty libraries too.
Nevertheless, I have learned to like some operators,
<$> <*>, *>, <*
are ones that come to my mind. The operator <$>
is basically fmap as infix notation, so `fmap f [1,2,3]` would be `f <$> [1,2,3]`. This makes mapping as easy as a normal function call (and the operator name `<$>` was not choosen arbitrarily. `$` by itsself is the function application operator, so `f $ 3` does on a single element, what `f <$> x` maps over an instance of Functor.With applicatives, the pattern of
f <$> a <*> b <*> c
is very common (with a type signature f :: a -> b -> c -> .... <*>
, is supposed to be `<$>` (based on the rest of your discussion). <*>
takes function(s) out of a mappable and applies it (them) to argument(s) within a mappable. (+) <$> [1,2,3] = [add 1, add 2, add 3]
(+) <$> [1,2,3] <*> [10, 20] = [(1 +), (2 +), (3 +)] <*> [10, 20] = [11, 21, 12, 22, 13, 23]
(+) <$> Just 3 <*> Just 5 = Just (3 + ) <*> Just 5 = Just 8
(+) <$> readLn <*> readLn = ...
well that depends on the inputs :)I guess the solution is just to put parentheses everywhere, but why is it that, whenever I show people code where I'm doing that, they always point out "Oh, you don't need parentheses there." I feel like that just goes to show that the community underestimates the confusion most people feel when they encounter a jumble of custom operators and have to recall to themselves which parts of an expression are evaluated first or even in what direction. This language feature feels more like an antipattern.
I really think the benefits of making operators so easy to define are quickly outweighed by the drawbacks for most programmers. The infix culture seems like one of the biggest barriers for this language.
But: For Functors and Applicatives for example, they do make sense. If you are using them, you can use them in any program you write. Just like we found addition and multiplication with numbers build an abelian group and chose to represent these operations by "+" and "-" (with the difference that in Haskell Programs, Functor-capable data structures pop up everywhere).
So that being sad about the motivational level, I would like to address some of your arguments against operators on the syntax level.
Precendence can be a problem and obviously it is something that is and has to be learned and might confuse beginners.
Nevertheless, Haskell's typechecker will often tell you when you missed up. I started of parenthesizing the hell out of these expressions and let hlint tell me when I could remove the parens, after a few days I hardly made any of these mistakes anymore.
While - as a Lisp guy - i do prefer functions over operators quite a bit, it is not like it is a terribly difficult thing. People have committed to learn inheritance, and multiple inheritance. Write classes where the hold and manage a lot of state and let that state interact, etc. That stuff is magnitudes more complicated than Haskell operators, already on a conceptional level.
In comparison, J is all about symbols, but provide a canonical dictionary and strict semantics about how those symbols work. Still complex to learn, but easier overall.
I suppose the other extreme is Lisp, which I've played around with and found quite pleasant because of its very unambiguous syntax.
They are all declared as left-associative with precedence 1, which means that they read left-to-right. i.e: they're the separators, so you don't need extra parenthesis.
So you can have something like:
employees database
<&> salary
& sum
& writeReport title
>>= sendReport destination
This lets you read your data processing pipe-line, while also seeing what kind of effects are being composed together at each step.You are, of course, free to object to the lack of separators between (and parentheses around) function arguments. I find it pretty natural when everything is curried.
A lot of these weird "operators" are also typically defined with a synonymous named function. These types of operators are usually functions after all, not actual language operators.
https://www.fpcomplete.com/hoogle
It searches on all the stackage set of packages, afaik (another explanation: http://stackoverflow.com/a/21955591/293735 )
It seems like my ability to hold logic in my head is partly visuo-spatial, and the terseness makes it easier for me to tell what's going on. (I also do Scientific Computing, so a general resemblance to mathematical notation is hugely helpful for me reading code).
let A be sth
X be sth else, ...
and then the equations and predicates and pseudocode using one-letter names). Then I change these to regular variable names of course (my friends don't think it's readable).I also really like the fact that in clojure identifiers can use math symbols exactly for this reason. I love the fact that + is a regular function, that I don't have to wrap it to reduce or map with it, and don't have to remember how to turn it from infix to prefix or vice-versa. That it works with any number of arguments. Same with <, >, =.
This the prettiest definition of dot product I've seen in any programming language
(reduce + (map * v1 v2))
It's great because with 2 very universal language constructs you get to express tons of stuff cleanly. There's no incidental complexity there.But I just can't force myself to like Haskell operator-happy style. I've tried learning Haskell a few times, and never sticked to it for more than a week, because operators everywhere just make my uneasy, and there's too much specialized version of functions.
I tried to learn common lisp a few times before clojure and nvere managed to get through the weird historical naming nonsense. Haskell sometimes feels like common lisp in that respect.
https://www.haskell.org/hoogle/ lets you search for functions by name, including symbol-named functions like <$> and co.
(You can also search for functions by type, which is a pretty cool feature, but not what you were asking for.)
https://hackage.haskell.org/package/base-4.8.2.0/docs/Prelud...
Edit: Granted ">>=" (bind is the word you are after) is kind of special and you need to understand more of the underlying language mechanics to get it. The provided explanation tells you little unless you can grok the function signature. I think it's almost universally accepted that a lot of the documentation is atrocious.
I'm very curious about this. Why do you think you have to learn it all by heart before you start with the language?
Incidentally, this is why prefix notation seems so foreign. (+ 1 2) reads as "plus one two."
It's possible to overcome this, with dedication. (And it's worth doing.)
It might just be the Lisp version of Stockholm Syndrome, but it seems perfectly natural to me to gloss that as "Add one and two."
"Would you consider equal the sum of 1 and 2 to 3?"
Though now that I type that out, I'm acutely aware of how strange it sounds. Interesting.
It's also not true in the case of macros, e.g. let. So, you're right. No wonder it seems so foreign.
EQ is object sameness. Number objects of the same value are not necessarily the same object.
Thus, I read “(+ 1 2)“ as “the sum of 1 and 2” rather than ”add 1 and 2“. Similarly, I read ”(eq (+ 1 2) 3)“ as ”the difference between 3 and the sum of 1 and 2 is zero“.
It's nice because it shows it's a value not a question.
But I don't really verbalize code, I think in symbols, maybe that's why prefix doesn't bother me.