When does this nonsense ever stop?
f . g y . h z a . f'
is certainly much clearer than f `compose` g y `compose` h z a `compose` f'
The same goes for (>>=) with Ada.Text_IO; use Ada.Text_IO;
procedure Hello is
begin
Put_Line ("Hello, world!");
end Hello;
Not that it's at comparable complexity, but I know what the following code does, but I still can't understand it. It's basically obfuscated: module Main where
import Control.Monad
import Control.Concurrent
import Control.Concurrent.STM
main = do shared <- atomically $ newTVar 0
before <- atomRead shared
putStrLn $ "Before: " ++ show before
forkIO $ 25 `timesDo` (dispVar shared >> milliSleep 20)
forkIO $ 10 `timesDo` (appV ((+) 2) shared >> milliSleep 50)
forkIO $ 20 `timesDo` (appV pred shared >> milliSleep 25)
milliSleep 800
after <- atomRead shared
putStrLn $ "After: " ++ show after
where timesDo = replicateM_
milliSleep = threadDelay . (*) 1000
atomRead = atomically . readTVar
dispVar x = atomRead x >>= print
appV fn x = atomically $ readTVar x >>= writeTVar x . fnThere's $, backticks, >>=, >>, ++, ., and <-.
All of these are very frequent things you use in Haskell, and deserve short notation. Besides >>, they're all learned in day 0-1 of Haskell.
(.) :: (b -> c) -> (a -> b) -> (a -> c)
It is clear that it takes two functions, and chains them together to create a new function f . g = \x -> f (g x)
So double (addOne 3)
is equivalent to (double . addOne) 3
Similarly, (!!) has type (!!) :: [a] -> Int -> a
It is immediately obvious from the type that it acceses the object at a particular index in a list, so ['a', 'b', 'c'] !! 1 == 'b'
Also, the syntax complements currying extremely well f g h x
is equivalent to (((f g) h) x)
This allows for some very neat things. addOne :: Int -> Int
-- addOne 3 == 4
map :: (a -> b) -> ([a] -> [b]) -- which is equivalent to '(a -> b) -> [a] -> [b]'
map is an extremely neat function, and is used in many languages. It applies a function to every element of a list, producing a new list.Now, there are two ways to use map
map addOne [1, 2, 3, 4] == [2, 3, 4, 5]
However, the above is equivalent to (map addOne) [1, 2, 3, 4]
From this we see there is another way to use map addOneList :: [Int] -> [Int]
addOneList = map addOne
-- addOneList [1, 2, 3, 4] = [2, 3, 4, 5]
Note how map was partially applied. In Haskell, map can be seen as doing two things. One is taking a function and a list, and applying the function to every element in it to produce a new list. However, you can also see map as a function transformer, taking an ordinary function, and converting it into a function that works on lists! map :: (a -> b) -> ([a] -> [b]) -- which is equivalent to '(a -> b) -> [a] -> [b]'C style:
a.b.c = 1;
Haskell: let b' = b a
c' = 1 + c b'
b'' = b' { c = c' }
in c' { b = b'' } a { b { c = 1 } }The equivalent in Haskell to your Ada program is:
main = putStrLn "Hello, World!"
I don't even think about taking the time to write the equivalent in Ada to the Haskell program you posted.
(def x (ref 1))
(defn increment [i]
(if (> i 0)
(
(dosync
(alter x inc)
)
(Thread/sleep 1)
(increment (- i 1))
)
)
)
(defn decrement [i]
(if (> i 0)
(
(dosync
(alter x dec)
)
(Thread/sleep 1)
(decrement (- i 1))
)
)
)
(defn printref [i]
(if (> i 0)
(
(dosync
(println (format "in printref %d" @x))
)
(Thread/sleep 1)
(printref (- i 1))
)
)
)
(future
(increment 10)
)
(future
(printref 15)
)
(future
(decrement 10)
)
Isn't this much nicer? It's not immediately obvious that x is an atomic variable, but aside from that it's a lot better than the Haskell example. It took me along the lines of 2-3 hours from never having touched a Lisp to writing this. import Data.IORef
import Control.Concurrent
increment _ 0 = return ()
increment x i = do
alter x succ
threadDelay 1000
increment x (i - 1)
decrement _ 0 = return ()
decrement x i = do
alter x pred
threadDelay 1000
decrement x (i - 1)
printref _ 0 = return ()
printref x i = do
val <- readIORef x
putStrLn ("in printref " ++ (show val))
threadDelay 1000
printref x (i - 1)
main = do
x <- newIORef 1
forkIO (increment x 10)
forkIO (printref x 15)
forkIO (decrement x 10)
threadDelay 100000
-- This is just a helper to more closely match the clojure
alter x fn = atomicModifyIORef' x (\y -> (fn y, ()))
I'd argue the Haskell is even nicer.I'm not sure why you chose the two examples you did. The Haskell version of the Ada program you wrote is as simple as can be:
hello = putStrLn "Hello, world!"
While I'm sure an Ada program that did what the code you pasted does would be of comparable complexity to the Haskell version. And being familiar with how monadic functions work lets me guess pretty well what the code does, despite having very little knowledge of the libraries involved. main = do -- atomically create a new transactional variable init'd to 0
shared <- atomically $ newTVar 0
-- atomically read the variable and print it
before <- atomRead shared
putStrLn $ "Before: " ++ show before
-- Fork a thread where we show the variable and sleep 25 times
forkIO $ 25 `timesDo` (dispVar shared >> milliSleep 20)
-- Fork a thread where we add 2 to the variable and sleep 10 times
forkIO $ 10 `timesDo` (appV ((+) 2) shared >> milliSleep 50)
-- Fork a thread where we subtract 1 from the variable and sleep 20 times
forkIO $ 20 `timesDo` (appV pred shared >> milliSleep 25)
-- sleep 800 ms in the main thread
milliSleep 800
-- read the variable and print it
after <- atomRead shared
putStrLn $ "After: " ++ show after
where -- define some convenience functions
timesDo = replicateM_
milliSleep = threadDelay . (*) 1000
atomRead = atomically . readTVar -- perform an atomic read
dispVar x = atomRead x >>= print -- read then print what was read
appV fn x = atomically $ readTVar x >>= writeTVar x . fn -- read, apply a function and then writeI don't understand what distinction you're making here. Named functions can also be members of typeclasses (and frequently are - return, mempty...)
How could you ever think that is a fair comparison?
Furthermore, the specific operators you cite are confusing examples, given that ">>=" can be elided using do notation, "\" looks enough like a lambda that it took me a while to remember what you were referring to, and "!!" is rarely used. A slightly more reasonable argument for your case would be to cite the operators used in the lens library, but those are all aliases for regularly-named functions.
There are some arguments against Haskell in terms of practicality, but this is not one of them.
We just finished the lists chapter in Haskell Programming and we never even mentioned that operator. It's a silly function ^_^
This is a pretty self-selecting set. One of the reasons I don't use Haskell professionally is that I hate its operator-naming culture, and Haskell isn't big enough in industry yet that it has lots of involuntary users (like C++ or PHP or Java does).
If there were a language written in Latin, and it was useful for some reason, people would quickly write aliases for all the Latin functions, named in English. This is very easy to do in Haskell: if your argument held, you would expect to see aliases for all of the operators. With a few exceptions (again, lens), this isn't the case.
You're choosing where to live based on the color of the neighbor's bikeshed, against evidence from previous tenants that the current color is quite pleasant.
Well, I guess we disagree there. And remember that some people may only work a little bit in a language, and it matters, to them, how much time they need to get into the groove with a language. Once you work enough in a language you get used to all the weirdness anyway, which is how you end up with people claiming C++ and PHP are actually kinda neat languages. The only good judges of language syntax are people who have never seen that syntax before.
It's all well and good to say that people should be able to bounce around between languages within a paradigm. A Rubyist should be able to read Python, Perl, and PHP, sure. A Java programmer should be able to read C++ and C#. Claiming that people should be able to read any programming language, though, means that languages aren't allowed to explore significantly different semantics. A JavaScript programmer isn't going to be able to pick up something written in Prolog or Forth and quickly understand exactly what's going on, and that's as it should be. The whole point of having multiple paradigms is that they can express different ideas and ways of approaching problems.
So, I agree that an SML or OCaml programmer should be able to "get into the groove" with Haskell quickly. In my experience, this is usually the case.
Would you expect to understand Forth or other concatenative languages even if all the operators are named?
The new set of semantics and patterns are the part that take time to understand. The syntax is not really the actual stumbling block.
>>= += <<= -= *= ...
?: ! ~ ^ % ...I think people have a preconception that "Haskell is not practical" -- and then anything that they do not find appealing becomes a source of its impracticality. Despite the fact that the same traits are shared with vastly practical languages.
I can vaguely sense somewhere in my memory that <<= and it's ilk are bit shift operators. How am I supposed to know that? Fuck it. It's bad library design.
I like Python. How do you append to aList? Answer: aList.append(anElement). Scala, on the other hand, seems to believe that ":+" is an acceptable append syntax. The compiler won't let me do stupid stuff with it, which is good, but I would prefer it if errors was caught by English-proficiency instead of rote knowledge/the compiler. I think that's a very powerful distinction.
Haskell has 21 reserved keywords that cannot be used
as names for values or types. This is a relatively low
number (Erlang has 28, OCaml has 48, Java has 50, C++
has 63.
In particular, some of the operators you mention represent such fundamental and common tasks that it makes sense to have a shortened form. I'm in agreement that descriptive names are preferable in general, but not always. Haskell syntax is quite simple and predictable (I say that as a former Rubyist); it only appears strange in the beginning because there are so many new concepts to learn.[0] https://github.com/kqr/gists/blob/master/articles/simple-syn...
Regardless of its practicality or lack thereof, Haskell is definitely at least hard to get started with.
1. It's not a panacea
2. It's still more work overall to teach Haskell (or any language) to somebody that has never programmed before
3. It's more fun teaching them because they don't argue with you or waste time trying to apply nouns/verbs they already know to Haskell.
Given that we're largely speaking from anecdotal evidence anyway, I'll share mine. The first non-ALGOL language I learned was Smalltalk, using Pharo. Its relative lack of syntax pretty much meant I could dive right in, with most practical programming involving the class libraries and navigating the Smalltalk environment with the integration intricacies of the binary image. I never really felt it as all that difficult though, even though imperative mindsets are allegedly supposed to make it that way.
The first functional language I learned (and the one I'm most proficient in) was Erlang. Despite being infamous for its unconventional Prolog-esque syntax, it took me only about 2-3 days to get comfortable with it and I breezed through Programming Erlang relatively quickly. I have since found it quite enjoyable to work with.
On the other hand, I never did manage to learn Haskell to any notable degree. I've always given up eventually, and haven't retried in a while. It's just a more complicated language.
Eric Hinton (whose first programming language was Haskell) gave a phenomenal talk about the cultural shift to Haskell at NYT, and the interesting ways in which they use it.
Some examples include Fashion Week image processing to analyze which colors are in style, building composite images by hue (and the massive speedups they get from parallel processing), book review analysis, high-speed video processing, and so on.
Worth checking out just to gain a newcomer's perspective to programming, what they were able to accomplish with Haskell, and how they integrated it into their existing environment.
[0] http://www.infoq.com/presentations/haskell-newsroom-nyt
EDIT: Clarity.
The problem that I see is more that the quality of the working environment is part of the practicality of using the language. That is, I think his criticism is self-contradictory. (Plus, the way it's worded just smells like a troll...)
There are other threads that discuss the merits of Haskell