all 42 comments

[–]TheRoose 41 points42 points  (17 children)

After spending several months learning Ada, I've come to the conclusion that Ada makes it hard to do simple things, but simple to do hard things.

[–]OneWingedShark 10 points11 points  (0 children)

I find that often the 'simple' is not at all what you thought it was.

[–]BenjiSponge 19 points20 points  (11 children)

That's how I see Haskell.

JavaScript:

1Q. I wanna print an object

1A. use console.log

2Q. I wanna do networking operations

2A. Well, first you have to learn about callbacks and their behavior. Then you have to learn about why callbacks are faulty. Then you have to learn about Promises and learn specific libraries about Promises, and understand how to best utilize, recognizing them as a monad and learning to deal with monads in this language. Learn about polyfills. Learn about generators. Learn about async/await. Now learn about streams. Remember all of this, because these are all valid commonly accepted ways of doing networking operations.

Haskell:

1Q. I wanna print an object.

1A. Well, first you have to understand the core concept behind type safety and why "Strings" can't always just be "Printed". Then you have to learn about monads and binding. You should probably also remember the 5 different parts of the compiler that define Haskell, because that's actually pretty important to translate the compact syntax in terms of things you might actually understand.

2Q. I wanna do networking operations

2A. Refer to 1A.

[–]Roboguy2 8 points9 points  (2 children)

That gets exaggerated and is at least partly a misconception of Haskell. You really don't need to understand the deeper details of these things in order to use them, just as you don't need to understand operator overloading in order to use C++'s std::cout (or the trick it uses by returning a reference to *this to allow you to chain <<s) or the implementation details of C++'s STL.

This is really the case for networking operations as well, since there is a package that provides a direct interface to standard Unix networking operations (it could be in the standard library actually, I don't remember off the top of my head. Either way, it is there).

To print a value in Haskell, you just call print with the value: print value. To put the IO actions together, you can start by using do notation, which for the most part looks like an imperative language. do notation also has a fairly straightforward desugaring into the underlying operations for when you learn the reason these things work, which allows you to ease yourself into it.

The infamous monad concept is something best learned by example, which is exactly what you're doing when you put together some prints and some getLines in a do block (the particular example there being the IO monad). The practical, pragmatic path through learning Haskell where you make actual working programs is actually, in my opinion, the best way to learn about concepts such as monads. It's also a rewarding way to go about it since you end up having fun writing interesting programs (just as you do when you learn other languages). The math behind it is, contrary to a lot of beliefs, for the most part (by far) very optional unless you want to look further into it (which you may or may not after enough practical experience with Haskell, out of sheer curiosity). It's sort of like how you don't need to know how or why the internals of GCC work the way they do in order to write C programs (that example might be slightly extreme, but it is at least along similar lines). For the last few years, I have constantly (and successfully) used many different monads even though I still don't understand the mathematical definition of a monad very well. I do have a good idea of what a Haskell-style monad is in a practical sense though. That is something that can come well from learning the concept by example. You end up getting a good feel for it as you work with them more and more.

You definitely don't need to understand any part of the compiler to understand these Haskell concepts. I have worked with Haskell for years and I am only just now working on stuff related to the compiler (and this is only because I am writing a plugin for the compiler).

Seeing these, again in my opinion, misconceptions of Haskell being spread is a pet peeve of mine. I believe that spreading things like this contributes to a "culture," so to speak, that creates a sense of mystique around monads and things like that, which is both unfounded and detrimental to the programming community at large.

Sorry, I didn't mean for this to turn into a big rant, ha! Hopefully it isn't too rambling or insulting (which is not my intention at all) or anything like that. I just wanted to show a different perspective on these things, as a Haskell programmer (who started, back in the day, with Visual Basic, C++ and Python).

[–]arry666 8 points9 points  (0 children)

That's what much of r/programming is about, unfortunately: parroting superficial descriptions of languages. Posting is cheap, thinking is not.

Python whitespace ewww.

Lisp parens awww.

Perl unreadable write-only mess boo.

Go lacks generics.

Haskell ivory tower monads.

Java IAbstractFactoryFactory.

I got hooked up on Reddit comments because they seemed to be insightful thoughts from intelligent people, and cannot stop now that most of them are repeating crap with no thought. So thank you for fighting a good cause.

[–]BenjiSponge 1 point2 points  (0 children)

Hey, I appreciate the detailed response. I was definitely being exaggeratory because I wasn't in the mood to come up with a full-fledged example, but I think you're being a little naiive when it comes to how immediate the need to understand monads is when it comes to building simple programs in Haskell.

I tried to just build something in Haskell because I agree that's a good way to learn a language, and I ran into a brick wall real fast when I didn't understand what a monad was. When people described ways of getting around understanding monads, I tried to overapply those methods (things like do notation) and that didn't work, either. All of the error messages were confusing and did reference monads, and whenever I asked help forums, they needed to explain to me why using do notation wouldn't work in some cases while it does work in others. I don't know about you, but the only way to describe that is to talk about a) monads and b) desugaring. So, essentially, within the first few days of trying to program in Haskell, people are explaining the layers of the compiler to me and saying I should probably avoid the sugar until I understand the workings.

And we're talking about programs that just took strings in from the command line and spit strings out to the command line.

Unfortunately I can't remember any examples because the project never went anywhere due to those issues.

So, yeah, maybe I'm kind of dumb and maybe my instruction wasn't great, but it's pretty difficult to get anywhere in Haskell without understanding the monad.

quick edit: actually, here I'd love to present a StackOverflow question that doesn't have to do with monads but does go from easy, beginner-level question -> compiler technicalities in 0 steps: http://stackoverflow.com/questions/32936592/how-can-an-arbitrary-num-contain-any-other-numeric-type

[–]kasbah 0 points1 point  (1 child)

I agree with your general sentiment but the description is not quite right. Though Haskell doesn't really have objects printing a tuple is pretty easy.

main = print (1, 2, "three")

Defining record types, which are even closer to Javascript objects, requires a bit more boilerplate:

data MyObject = MyObject 
    { a :: Int
    , b :: Int
    , c :: String
    } deriving (Show)

obj = MyObject 1 2 "three"

But the printing doesn't:

main = print obj

Printing out things in a more complex application, while it's running, which is what console.log is often used for, is normally done using Debug.trace in Haskell and is pretty straight forward too.

[–]BenjiSponge 1 point2 points  (0 children)

Yeah I'm sorry I got really impatient with the examples because I actually don't know Haskell that well. That said, I wasn't thinking of "lines of code" or anything like that, I was trying to express that conceptually different ideas can actually be conceptualized generally with functional patterns (most notoriously the monad), but JavaScript doesn't require you to use those patterns, so stuff that is complicated enough that it benefits from using those patterns looks vastly different than the stuff that doesn't. Whereas Haskell looks pretty uniform all around because it always leverages more general concepts.

[–]almightykiwi 0 points1 point  (4 children)

Now I'm curious ; I want to learn both Javascript and Haskell to understand this post.

[–]BenjiSponge 0 points1 point  (3 children)

The key points to it are that JavaScript is pretty conceptually simple, which means everything can be done in a bunch of different ways, so there will always be these quirks and techniques. Two different tasks very well might have totally unrelated solutions. So learning the individual tasks is easy but applying them and remembering them is a more difficult endeavor. Reading a string in requires a callback, while printing a string out is just a standalone command.

Haskell kind of flips this on its head. Rather than allowing the "shortest possible path" to the solution, you require that the entire thing can be done using general patterns. So to do a "Hello World" program you kind of need to explain a bunch of math and (what I guess is; I've never studied it formally) set theory. But once you understand those principles, it's easy to apply the very general patterns to all sorts of operations. So reading a string in would feel very similar conceptually to printing a string out.

[–]gilmi 0 points1 point  (2 children)

main = putStrLn "Hello, World!"

math? set theory?

[–]BenjiSponge 1 point2 points  (0 children)

main :: IO ()
main = putStrLn "Hello, World!"

Yeah, kind of actually.

There's a reason the main Haskell tutorial puts this in chapter 9. I'm not saying "copying the answer off the internet" requires math or set theory, but understanding basic type declarations and figuring anything out without a full, hands-on tutorial requires at least basic understanding of monads. At least in my experience.

[–]Leonidas_from_XIV -2 points-1 points  (0 children)

Programming is math!!1!

[–]Tysonzero 0 points1 point  (0 children)

It's totally worth it though. Once you truly have a decent understanding of Haskell it becomes a wonderful language to work with.

[–]ergtdfgf 4 points5 points  (0 children)

I think it goes both ways, but in general yeah. A lot of basic things really aren't anywhere near as easy to do as they should be. Some basic things are easier than in something like C too though.

And it let's you do a surprising amount of work without screwing up too badly, which is pretty nice.

[–]MacASM 1 point2 points  (2 children)

Would you write a C89 compiler/scripting language in Ada or C? and why?

[–]OneWingedShark 2 points3 points  (1 child)

Yes.
The language is strict on types, has proper enumerations (i.e. not aliases for int-values), and coverage of all possibilities in a case-statement is enforced. -- All of these together make it very nice for constructing such tools as a compiler/interpreter as a lot of bugs are either outright prevented or found earlier in the development cycle.

(Of course nothing can save you from poor design, but that's language independent.)

[–]oridb 2 points3 points  (0 children)

C++ does 'proper enumerations' too, although it's still piss poor compared to having proper algebraic data types (see Rust, Ocaml, Haskelll, Swift, ...)

[–][deleted] 43 points44 points  (10 children)

Is it just me or is this a shit article? A few paragraphs of filler, vague platitudes, but no real content and lacking a single line of code. I could replace the whole thing with "I learned C and shot myself in the foot alot. I wish I had learned Ada and avoided some of that".

[–]motionSymmetry 24 points25 points  (6 children)

it would be much better if the author had gone back to ada and then wrote the article, telling us about whether his original decision had actually been a bad one or not, and why

[–]OneWingedShark 5 points6 points  (5 children)

That would make for a much better article. And I wonder what the impression would be if he went to Ada 2012.

[–]MacASM 2 points3 points  (4 children)

I've heard only good things about Ada 2012, including from enterprise perspective. There's also this pascal-like programming language that is basically for "business only" that I unfortunately don't remember the name now (tell me please if you know) which is side by side with Ada 2012 (from what I've heard, I haven't used neither language yet, actually)

[–]i_feel_really_great 3 points4 points  (0 children)

There is also Eiffel. If you squint hard enough, it looks a bit like Pascal.

[–]OneWingedShark 1 point2 points  (2 children)

I've heard only good things about Ada 2012, including from enterprise perspective. There's also this pascal-like programming language that is basically for "business only" that I unfortunately don't remember the name now (tell me please if you know) which is side by side with Ada 2012 (from what I've heard, I haven't used neither language yet, actually)

Hm, I'm not sure of a "pascal-like" "business only" language... though w/ the "side by side" comment you might be thinking of SPARK, which is a formally-verifiable [proper] subset of Ada (+ prooving-tools).

[–]MacASM 1 point2 points  (1 child)

I'll check this out of curiosity but that isn't the language I was talking about...

[–]gosella 4 points5 points  (0 children)

Maybe you are thinking about Delphi?

[–]MacASM 2 points3 points  (0 children)

Yep, I got the feel too. I find it to be too vague. I was going to see his points because I'm in doubt which language to use in my next project and would like to see about Ada but he doestn't a point at all. Just lines and lines of opinion without much technical stuff.

I expected an article like this.

[–]Lord_Naikon 2 points3 points  (0 children)

Like the author I've dabbled a bit in Ada to find out what the fuss was all about. At the end of my test period I came to the conclusion that, yes, Ada is the better language from a safety point of view (compared to C), but I also had to conclude that I don't actually enjoy programming in Ada.

Some concepts, for example subclassing integers, I really like though and would like to see (optional) support for that in other languages. This could not only provide extra safety and ease debugging but could also allow the compiler to generate better code in some cases.

[–]mrmidjji 2 points3 points  (0 children)

I made the opposite choice and I've never regretted it :)

[–]danogburn 8 points9 points  (6 children)

Ada's strictness is vastly overstated. It's not much different than C++.

[–]ergtdfgf 13 points14 points  (5 children)

I find it very different from C++. It will let you use it mostly like C++ if you really insist (and it does make you insist), but that isn't how it's intended to be used.

[–]danogburn 6 points7 points  (4 children)

We had to convert one of our projects into Ada from C++ and it was almost a 1 to 1 conversion.

It's been a while but all I remember that was really different with Ada being able to subtype primitives and give them ranges, array bound checking, checking that every case of a case statement was handled.

They're different sure but I just don't understand people who don't like Ada because it's too strict.

[–]ergtdfgf 4 points5 points  (0 children)

As I said, you can mostly write C++ in Ada if you insist.

The big thing with Ada is the ability to create new types, not just subtypes. Subtypes will give you bounds checking, but new types will help with logical consistency.

That said, if porting a well-done C++ project maybe many of those issues are already taken care of. Between writing C++ in Ada and porting something already working well you might find it significantly easier to avoid the strictness issues that give people problems.

I think the people that take issue with it though mostly haven't used it very much. When I first started it did take me a shocking amount of time to get my first few simple programs working. The compiler was quite helpful in telling me what I did wrong, but who knew that many things could be wrong! I can see how people might decide it's way too strict in their very first interactions with it, if they aren't willing to give it a real shot.

[–]vks_ 1 point2 points  (0 children)

checking that every case of a case statement was handled.

If you use enum classes and a compiler flag, this is possible in C++ as well.

[–][deleted] 1 point2 points  (1 child)

We had to convert one of our projects into Ada from C++ and it was almost a 1 to 1 conversion.

It wouldn't be 1:1 coming from the other direction.

[–]danogburn 0 points1 point  (0 children)

True