A Taste of Haskell
hookrace.net
hookrace.net
For example most don't know that Java actually does have macro like capabilities through APT (annotation processor that will work automagically I might add). Some might say ewww its not macros but then again annotation processing might just be 80/20 good enough (aka what OCaml learned with ppx vs camlp) and that Java now sort of has traits and that its code swapping is actually fairly good (jrebel). There are even sadly people that don't know Java has lambdas, and even some (usually C++ dudes) that don't know it has generics. Most don't even know that you can combine interfaces with generic types as in: `<T extends Comparable & Serializable> void someFunc(T t)`.
Yes Java is not Rust, Haskell or even OCaml in expressiveness but it certainly is more powerful than Golang and definitely compiles fast (albeit starts up slow).
That being said OCaml is still my favorite. Compiles fast and easier for me to understand. Super mature and lots of companies use it.
Curious, who uses ocaml other than FB and Jane Street?
[1]: http://stackoverflow.com/questions/7717691/why-is-the-minima...
I've been using Haskell for Mac IDE [0] while experimenting, and it's been great fun.
The thing that prompted me to give Haskell a a try was this video [1] of a guy using Haskell to generate Elm types, in order to maintain consistent types across the stack. This idea captivated my interests. I'm confident that GraphQL and Relay, or something along those lines, is gonna be the future.
My experience so far has been that Elixir feels more welcoming and beginner friendly. There's nothing wrong with the Haskell community (or my limited interactions with it so far), but so far it's just felt a bit drier.
As it stands I can't help but feel discouraged when learning Haskell. Haskell feels like a research tool for furthering other programming languages...
I'm still going to learn Haskell, promised myself I would! But I do look to the Elixir folk with a bit of jealousy..
Haskell restricts what you can do hugely. But also lets you accomplish a whole lot within what it thinks of as safety.
I like Haskell - but think it's really just a playground for trying how far functional purity can be pushed, rather than being the actual best version of that paradigm.
Probably in 10-20 years time there will be a language around which takes all those concepts and comes together as a really pleasant unsurprising friendly developer experience. Maybe it'll come from slow evolution of Haskell... stack helps a lot, and a whole bunch of GHC extensions... but it's still got a lot of very impractical elements and hangovers from the past that really need to go.
I think Scala can probably do anything.
I agree that Haskell is probably not great for writing real world code and should be reserved for toying with ideas about PL and type theory.
Because its a pain in the ass to learn those concepts from scratch when you're looking at code in one language but reading a tutorial in another...the concepts are language agnostic, the naming conventions however are not.
Where is it that Haskell shines?
[1]: https://github.com/Gabriel439/post-rfc/blob/master/sotu.md
However GHC's garbage collector can be an issue for real-time code, having high 99th percentile latencies.
map.get("foo") // returns null
is the value of the key null, or was there no key? Different languages have different approaches. Haskell let's you build up deep structures, a list of maps of list of maps ... and you'll have an indication of what path you took down the data structure to get a result. You can do this in any language, but it relies on programmer discipline, and everybody doing it the same way every time. In Haskell, it's the easy thing to do, and often the only way to do it. You'll wind up with something like[Just ["foo"], Just ["bar"], Nothing]
The first two maps had once answer each, the final map had nothing.
Laziness means you have control of the order of evaluation. in practice you can make your own control structures without macros. bracket is a good example,
bracket open_socket do_stuff close_socket
the socket always gets cleaned up, with an ordinary function. No try/catch.One of the biggest hassles in programing is leaky abstractions. The type system means you can steal abstractions from mathematicians that just don't leak. There's some good stuff here [1] about the kinds of things you can say.
Purity means values can't change behind your back. there's no function to call that'll change the state of some object you're working with.
All together, it's nice for languages. You can build up parse trees any way you wish, and traverse those trees to execute the semantics. So if you can think of a dsl for your problem, you can whip up an interpreter pretty quick. If you change your mind, and want to turn it into a compiler, it's relatively painless.
It's a fun language.
There are a couple of things. First, haskell lets you build up the language incrementally. So if your parse tree is like
data AST = Prim Op |
Immediate Int
you can just throw in Variable String
And the compiler will tell you all the functions with non-exhaustive matches. It's tough to do this in C because you have to remember all of your switch/case statements. Also, in C those switches are going to be based on a tag in a data structure rather than a direct type. The compiler won't really help very much.Second, the thing that implements the semantics for the parse tree can be a typeclass.
class Semantics where
eval (Prim op) :: Prim -> a
eval (Immediate i) :: Immediate -> a
eval (Var name) :: Variable -> a
In haskell, you are free to implement an interpreter which is way way easier than a full compiler. It's nice to have an interpreter for your language, because you can run the same program both ways, interpreted and compiled.so...
instance Semantics Interpreter where
eval (Prim op) = case op of
Plus l r = eval l + eval r
Minus l r = eval l - eval r
...
eval (Immediate n) = n
...
-- variables need an environment, but i think you get the idea, just look up the string in the env.
Then, you can write a compiler. instance Semantics Compiler where
eval (Prim op) = case op of
(Plus l r) = eval l ++ "MOV r1 r2 \n"
++ eval r "ADD r1 r2 -> r1\n"
...
eval (Immediate a) = "STORE " ++ a ++ " r1\n"
...
-- again, variables require an env to be passed around, it's just another arg to eval
-- this time around though, instead of a simple lookup
-- you need the memory address to load into r1
-- you have to encode what lookup would do.
-- [2] talks about this a bit.
This is a pretty crappy evaluation strategy, just sticking the last result in r1, but i'll work. You can do much much fancier things.Another super cute trick with haskell, you can use LLVM to generate your machine code at haskell run time, and then dynamically link it [1]. This lets you mix and match evaluation, interpret parts, compile parts, and use them interchangeably. Much much easier debugging. That link should get you generating machine code for a toy language in a day. Basically, instead of loading up the dynamic library, you just make an executable instead.
Anyway, there's lots of good stuff out there. I'd suggest out 3imp [2] (warning postscript file) That's Kent Dybvig's dissertation on writing scheme compilers. The second implementation is unbelievably elegant. full support for call with continuation, so much cool stuff. It compiles to a VM rather than assembly, but that makes a lot of complex things clear.
An obvious typeclass is a pretty printer for your parse tree, so you can make sense of what a program is supposed to be doing and you don't have to swear quite as much when your resulting assembly doesn't work.
A less obvious typeclass pass is a typechecker, if someone is adding a string to an integer, you can return a new AST that injects the Int -> String conversion. Constant folding is a good one to do as well.
An even less obvious typeclass will ensure that every malloc has a free. I don't think it's possible for any C program, but you can get a lot of it, and spit out a warning when your algorithm isn't sure.
If you can handle academic papers, Oleg's finally tagless is the coolest way to swap out interpreters, compilers, and partial compilation [3] (pdf)
C won't help you with that, like at all. you can play a game with a table of function pointers, that you swap out for the various eval cases. Debugging that, imho, is hard. With haskell the compiler just tells you you're being dumb.
I guess the main thing is, a compiler has a ton of special cases. Half the battle is just mananging those special cases. Haskell eases a lot of those burdens. The other half of the battle is coming up with good algorithms for getting stuff done (like register selection above!). Haskell lets you slice things up, so you can give your algorithm everything it needs to do its job efficiently. It's not that you can't do that in other languages (clearly, there are a ton of compilers in a ton of languages) it's more that it really encourages and supports the kinds of things you need to do when writing a compiler.
Again, writing compilers is hard. you will feel stupid. You are not stupid, you're doing something very few of the 7 billion people on the planet have even attempted. It takes years of practice to get good enough to even try. You're a badass. You'll solve it.
[1] http://augustss.blogspot.com/2009/01/llvm-llvm-low-level-vir...
[2] ftp://www.cs.indiana.edu/pub/scheme-repository/doc/pubs/3imp.ps.gz
[3] http://okmij.org/ftp/tagless-final/JFP.pdf
edit
In retrospect that register allocator blows. any right associative operations will get stomped on. It's what i get for slapping something together at 1 am. let's just pretend this is for fourth, and you always work on the top of the stack.
Equational reasoning[1] is immensely useful when debugging. It isn't unusual to debug pieces of Java code that look like this.
MyClass c = new MyClass(..);
c.method1(..);
c.method2(..);
c.method3(..);
assert(c.someField == ..) // fails!
To figure what went wrong, you have to read through all three methods and depending on whether you have dynamic dispatch coming into play, you might need to read through _multiple_ versions of all three methods.[1]: http://www.haskellforall.com/2013/12/equational-reasoning.ht...
Haskell shines on refactoring, API designing, and constrain setting (want to enforce safety? atomicity? thread ordering?). As a bonus, it'll also reduce your codebase an order of magnitude or two.
I mean, yes, if you never wrote a complex parser on Haskell, you should try. But that won't change your life too much.
The best example I can give you is the Haxl project at Facebook [0].
I'm also aware of another 5 or so big banks having/building Haskell teams for developing internal tools, but all this stuff is hidden. The biggest, public, project I'd say is FaceBook's Haxl (as pointed out elsewhere in this thread).
A side effect of being so strict (in the sense of picky) about what is allowed where is that it needs some really powerful abstractions to keep that from slowing you down. Its type system shines here. Often, these abstractions have a nice syntax in haskell and it makes for some really clean code, relative to a direct translation in other languages.
Elegance comes from the non-strict functional part, enabling succent and modular implementations.
Robustness comes from the static typing which forces you to be explicit about the range and domain of functions.
What's unique about Haskell is that the type system is _very_ expressive, making this a practical approach. IMO, the type system is absolutely key to Haskell.
Where Haskell disappoints: Int. For reasons that I still cannot phantom, someone chose to infest a beautiful language that is almost free of pitfalls with the atrocity of a "C-like" Int type. Indeed, in Haskell, this isn't always true:
a < a + 1 where a :: Int
which is a real shame IMO. It fails the largest value of Int (which BTW is implementation dependent). The correct answer would either to have (my preference) Int == Integer, that is arbitrary sized integers always, or at the _very_ least, have (+) fail on overflow, that is, return bottom like head [].Trifecta is a nice modern parsing library: https://hackage.haskell.org/package/trifecta
This goes into detail about how Facebook uses haskell to fight spam. There's a talk that goes alongside it and the talk was one of the motivating factors for me to start learning Haskell.
The service is a big deal, because every action or request that anyone makes on Facebook touches this service.
[0]: https://code.facebook.com/posts/302060973291128/open-sourcin...
I wrote a port of Haxl to C#[0]. At this point one might expect me to recite the whole Turing-completeness-means-all-languages-are-the-same banality, but my experience porting Haxl has actually led me to the opposite opinion.
Haxl probably wouldn't have been written in any other language other than Haskell. Because Haxl leans so heavily on concepts that other languages have no words for, I think it's inexpressible in C#, or most other mainstream languages. Obviously not literally inexpressible (there's an existence proof of a C# Haxl) but in an Orwellian, "these concepts are unthinkable in other languages" sort of way.
We're both solving some specific problems (ie inventory control, network/transport optimization) and building a pretty general framework for expressing, analyzing and solving large stochastic optimization problems. Haskell is a great fit because it's extremely expressive and lets us write and reason about our problems in a natural, expressive way. It helps that we have a very mathy team in a mathy field :).
Our team also has a bunch of people with programming language research backgrounds, and our long-term remit is to apply programming language ideas to operations research. Turns out that—for a whole class of PL problems and PL people—Haskell is the perfect language :).
I also know people use Haskell at Facebook (already mentioned), Google (Ganeti and some unnamed X project), a few banks (JP Morgan, Barclays and Standard Chartered) as well as a whole bunch of startups and small companies.
So far it's working extremely well for us and has been a great decision - letting us build complex/reliable systems really quickly and effectively. It is also very good for attracting amazing people, and we have found a small Haskell team can deliver truly spectacular product. This does on how you architect a Haskell codebase: e.g. you could spend a ton of time focusing on the most pure approach vs. pragmatism, so it's a balancing act.
You will regret it once your in that position and it is hard to get out of once your there.
This program is also too big to load in GHCi, at least on my laptop with 8GiB of memory.
That blog post just says that interpreting Haskell can be faster than compiling.
I worked with Haskell professionally on a pretty large codebase. It was split up into many separate libraries and binaries. There were a lot of coffee breaks when recompiling.
https://www.reddit.com/r/haskell/comments/45q90s/is_anything...
App MaybeT IO (StateT IO (EitherT IO Text SomeRecord))
Or something. I haven't figured this out.
You can slowly start writing generic types for your pure code after a while. For example, replace types such as `[t]` with `Functor f => f t` if the only list-related operation done in that function is `map` (fmap). Try to take advantage of the stuff here: https://wiki.haskell.org/Typeclassopedia
After a while of doing this, you can start looking into how you can write your code in a way to have more generic types, then specialise it for the concrete problems you're solving
Finally at this point transformers should become easier and more obvious. Perhaps the first one would be to replace passing the config everywhere with ReaderT. Implementing a few transformers by yourself should help. Start with MaybeT and continue with ReaderT. Tip: `liftIO` magically works but you don't have to always understand how. Maybe also try free monads if transformers seem too annoying!
https://hackage.haskell.org/package/base-4.9.0.0/docs/Contro...
It seemed I had two options: either I could pass a seed value around everywhere, or I could lift all my values into monads, necessitating the use of the 'monadic' version of all the operators etc everywhere.
I was unable to achieve what you suggest... I could not find a way to keep the 'inner' part of the code as pure functions just expressing simple operations.
I submitted here http://codereview.stackexchange.com/questions/114725/my-firs...
I had some useful feedback but no one addressed the "monad pollution" issue.
Apart from the different mindset of Haskell (which I found interesting) I struggled with having multiple ways to do everything, how to find the 'right' or monadic version of a particular function or operation, poor docs.
Ouch! I've written Haskell commercially for the last four years and I've never written (or had to read) anything like this!
IntelliJ also does not seem to have any problem with it either. (The IntelliJ authors being the the creator of Kotlin) In terms of code size, Kotlin is definitely smaller and easier to maintain. In terms of raw/clean compile speed, I would expect it to be a bit slower, but there are ways to improve it. One of the bigger ways is via incremental builds. I.e. Only build changed chunks of code, which Kotlin supports, and is comparable to, and in some cases faster than Java: https://medium.com/keepsafe-engineering/kotlin-vs-java-compi...
In terms of executable size, I don't have good data, but will investigate, but I'd say likely not a major deterrent given all the other value adds. :)
Full compile and changing one 200 line library file used by 1/4th of the project?
My hunch is that if you are using a modern compiler/IDE, you will not be coding -> compiling -> running that frequently. I typically code a lot before I actually actually run anything, so I'm not usually waiting around on a compiler. I don't even notice, at all, Java's incremental compilation when coding in IntelliJ. I refactor like a madman.
Also, Kotlin is not very ambiguous, and the type inference that it does enable does not escape the scope of a function so the compiler doesn't have to do that much work. (functions require strict typing). I would guess much of that lambda work gives it a small headache, but probably not that much worse than Java's. If Kotlin grows mainstream, it's already pretty good compiler, will only get better.
Of course, that is all a hunch. :)
The obvious choice for Haskell on the JVM is Scala.
Granted, Closure is probably a more pure alternative.
Other than that it is amazing the confidence it gives me; if it compiles it works (r).
https://www.reddit.com/r/haskell/comments/2z248l/language_ex...
data Foo = Foo { _bar :: String }
data Buzz = Buzz {_bar :: String}
And derive lenses? I think it doesn't - this is more than just a duplicate record name, you also end up with a duplicate `bar` lensAlso, modern popular languages tend to use arrows (Java, C#, ES6). Not to say that popularity matters, but people are used to it nowadays.
I have to revisit my base setup soon. The 1990s wants its bash_profile back
It did seem like less experienced people picked it up faster, perhaps because they had less to unlearn. The hardest things were learning to parse GHC's error messages and learning all of the names of the functions to transform between various data types.
Prior to this, I had some experience using it for hobby projects, but there was a lot more to learn for real-world use. My colleagues and I helped each other learn new concepts and write better code, as best we could, but we did end up making some questionable design decisions. I think you still need an experienced Haskeller to advise on good program architecture.
As a language, it's sterile. You can tell it was designed by committee.
If you think Haskell is a weird language that challenges what you think about programming, it's nothing compared to q! :)
This remark is puzzling. How do you tell if a language is sterile?
It's like the difference between Lojban/Esperanto (artificially constructed spoken languages) and English/Spanish. English/Spanish are "imperfect" but their warts are the results of thousands of years of culture (and may not be imperfections after all, they may be a case of some kind of coding theory emerging organically)
In any case, I think suggesting Haskell isn't a "real-world" language comes of a bit trollish, even though you probably didn't mean it like that.
As for what you learn from it... you say you didn't see any gains? I take this to mean in your previous languages you already programmed in a functional style, with purity, referential transparency and lazy-by-default evaluation? I find that hard to believe :)