I always have been interested on haskell.... but it doesn't look to be enough practical. For example there is almost no information about how to write a smartphone videogame in haskell, even if it possible to trasnpile it to C or to Javascript. It is possible to interoperate with the host language? Does it perform well for high performance activities? Is it possible to isolate some kind of events in a thread?
Edit: This lack of practical approach is what makes me hesitate in contrast to clojure (just to mention a language), especially if the only goal is to become again a "better developer" rediscovering functional terms and abstractions also documented in other languages.
Your fears are not warranted. Haskell is the most practical of the "research languages" out there, it's very mature and has good facilities for interfacing with the "real world".
> For example there is almost no information about how to write a smartphone videogame in haskell
This has been done before but most of the "hard stuff" here is getting your game built and deployed on a smartphone. You can find Haskell for Android articles if you look around.
There are examples of game programming in Haskell too.
> It is possible to interoperate with the host language?
There is no concept of "host" lanugage, Haskell emits native code.
You can call native code from Haskell and Haskell code from C or other native languages.
> Does it perform well for high performance activities?
Haskell is good enough for most "high performance" activities (it's one of the highest performing high level languages according to the debian shootout), but caveats may apply with long running processes and garbage collection, etc.
> Is it possible to isolate some kind of events in a thread?
Yes. Haskell has very nice threading facilities and high-level concurrency primitives, including stuff similar to "channels" from golang.
> For example there is almost no information about how to write a smartphone videogame in haskell, even if it possible to trasnpile it to C or to Javascript.
And frankly a smartphone videogame is a pretty specialized use case; a lot of pretty mainstream languages (perl?) don't have a good story for how to do that. In fact I've never heard of anyone writing a smartphone game in Clojure - I guess you could do one for Android since it's just java bytecode, but does anyone?
"Practical" for most programmers is server-side business apps, and Haskell is very very good at those.
> Does it perform well for high performance activities?
I would not say that Haskell is currently ideal for writing native smartphone apps. It's possible and people are working on making it easier, but it's rough territory still.
Javascript is easier, ghcjs (while still a bit tricky to install) is 1.0 and lets you compile arbitrary GHC Haskell (including all extensions, lightweight threads and STM) to JS for both Node.js and the browser. Template Haskell support is on the way too.
Calling the C FFI from Haskell is fairly trivial in my experience. If you have structs you want to manipulate from inside haskell it's a bit cumbersome and confusing initially, but if you're just using primitive types and pointers it's easy.
Haskell does very well for high-performance when it comes to IO, as-of GHC 7.8's new IO manager the fastest Software-Defined Networking (SDN) application is written in Haskell [1]. In terms of pure computation bound performance Haskell, does well enough, optimising truly high-performance Haskell is as much arcane art as optimising C (we're talking "I want microsecond inner-loops" level of optimising). In general I would say that you should expect Haskell to be around JIT Java levels of speed for most cases where the code is not blatantly inefficient. So certainly an order of magnitude faster than python/ruby.
Lots of people complain about performance reasoning in Haskell being hard, which is partially true. But mostly, IMO it is simply different from doing so in C, so you need to unlearn/relearn all your performance tweaking skills.
As for Haskell vs Clojure and becoming a better developer. I think Clojure looks like an interesting language, but I would recommend learning Haskell anyway. Why? Because Haskell is one of the "most different" languages in which people are still writing lots of real world code. Which means that it will expose you to the largest number of new concepts while still having decent practical library support. Key features, IMO are: Purity, type system, laziness, and type classes.
Incidentally, I would urge everyone to please stop using this silly "transpile" word. Compile is already source-to-source and introducing a new silly sounding word for it isn't helping anyone.
Lots of people complain about performance reasoning in Haskell being hard, which is partially true. But mostly, IMO it is simply different from doing so in C, so you need to unlearn/relearn all your performance tweaking skills.
I don't agree. First, of all, the naive solution in C is often pretty fast, while the naive solution in Haskell often has obnoxious runtime properties.
Then, most C optimization performance reasoning applies to Haskell as well: keep your data small, in contiguous memory, etc. The first thing that complicates optimization in Haskell is that defacto Haskell data structures are the opposite: pointer/thunk-rich. So, you usually end up with libraries such as 'vector', doing the heavy lifting in the ST monad.
Then, laziness adds even more complications, both in that it adds a new class of bugs (of course, Haskell removes buffer overflows et al.) which tend to be relatively hard to debug (the heap explodes in less expected places).
When you write production code, you usually accumulate some monads. Unfortunately, the relatively obvious way to use multiple monads (monad transformers) is slow. So, you have to roll your own monad stacks:
I enjoyed working through this book. Actually it helped me learn enough Haskell to realize that I wasn't that interested in it. I found that it wasn't as safe as it claimed to be and that the tooling was not very good. There are some refreshing and interesting ideas, but overall I'm not sure it was worth my time.
* Non-exhaustive patterns error was disappointing after being told that "if it compiles it will run". It seems to be the Haskell equivalent of NullPointerException (I'm told OCaml doesn't have this problem).
* I found Yesod API to be inelegant. I found it odd that it needs so many non-standard compiler extensions, which might be a sign that the language is not expressive enough.
* One-letter variables seem to be idiomatic. This is unappealing to me because I try to make my code readable.
* For the same reason, frequent use of special characters in operators (, <>, ///, etc.) in the standard library and in third-party libraries was not appealing.
I was disappointed because I was attracted by referential integrity, strong compiler checks, pattern matching and function composition but overall I was left with the impression that the Haskell environment was not very well polished from a software engineering point of view.
> * Non-exhaustive patterns error was disappointing after being told that "if it compiles it will run". It seems to be the Haskell equivalent of NullPointerException (I'm told OCaml doesn't have this problem).
I agree that this should be an error by default, however -Wall would fix this issue for you now.
> * Installing libraries is slow
It is slow, but can be made much faster by using "cabal install -j${num_cpu_cores}". I also use -O0 to disable optimizations during development.
There is also Halcyon, which I believe allows you to do some caching of libraries.
A little more extreme (but complete imo) solution is to use the nix package manager or if you want to go all in, NixOS.
> * The Haskell platform is not batteries included
I keep going back and forth on how I feel about this. It's harder in the beginning because you aren't sure what to use. Once you get a feeling for what libraries are best though, it's a great advantage that those libraries move forward so quickly without being tied to GHC development practices which have to move more slowly.
> * JSON parsing seem to be more complicated than it needs to be
Really? Here is some JSON parsing code I wrote the other day[0] to show how pattern matching and exhaustiveness checks in Haskell are useful:
Those are just things you have to get used to and imo don't really seem like a big deal. Every language has these kinds of things, but Haskell has less than most. To qualify that last statement, once you've learned their very different set of abstractions you know everything you need to know. It is less work in general dealing with programmer defined abstractions built on top of functor, monoid, etc because those custom abstractions more closely resemble them, which is an effect of them having to follow laws.
> * I found Yesod API to be inelegant. I found it odd that it needs so many non-standard compiler extensions, which might be a sign that the language is not expressive enough.
Looking at a Yesod example[1], the most language extensions I saw were:
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE TypeFamilies #-}
TemplateHaskell and OverloadedStrings are both just conveniences, I suppose there could be an argument that MutliParamTypeClasses and TypeFamilies mean the language is not expressive enough. One of the reasons for use of language extensions so much in Haskell is that it is designed by committee, it's quite possible that other languages might have already deprecated these language extensions and moved them into the core language.
> * One-letter variables seem to be idiomatic. This is unappealing to me because I try to make my code readable.
The only place I usually see one letter variable names is where it's quite obvious what they are. I don't think anyone would argue it's better to use something besides x and y in this function definition for instance:
add x y = x + y
To check out your claims, let's grab a few random snippets of code (I'm hitting the spacebar randomly then choosing first function I see) from the most popular[2] Haskell libraries:
containers[3]:
isSubmapOf :: (Ord k,Eq a) => Map k a -> Map k a -> Bool
isSubmapOf m1 m2 = isSubmapOfBy (==) m1 m2
bytestring[4]:
-- | Add a list of non-negative numbers. Errors out on overflow.
checkedSum :: String -> [Int] -> Int
checkedSum fun = go 0
where go !a (x:xs)
| ax >= 0 = go ax xs
| otherwise = overflowError fun
where ax = a + x
go a _ = a
directory[5]:
setOwnerReadable :: Bool -> Permissions -> Permissions
setOwnerReadable b p = p { readable = b }
What I personally take from above is that while Haskell does use single letter variables, the types make it obvious what those variables are. As a test, I'll go to the next popular library and look for a longer function to see if this line of thought breaks down.
text[6]:
fromString :: String -> Builder
fromString str = Builder $ \k (Buffer p0 o0 u0 l0) ->
let loop !marr !o !u !l [] = k (Buffer marr o u l)
loop marr o u l s@(c:cs)
| l <= 1 = do
arr <- A.unsafeFreeze marr
let !t = Text arr o u
marr' <- A.new chunkSize
ts <- inlineInterleaveST (loop marr' 0 0 chunkSize s)
return $ t : ts
| otherwise = do
n <- unsafeWrite marr (o+u) c
loop marr o (u+n) (l-n) cs
in loop p0 o0 u0 l0 str
where
chunkSize = smallChunkSize
Alright... str is obviously string, marr is mutable array.. o, u, and l... what could those be? Well it's getting set from the Buffer type, so it looks like I'll have to see what the type of that is.
Prelude GHC.IO.Buffer> :i Buffer
type role Buffer phantom
data Buffer e
= Buffer {bufRaw :: {-# UNPACK #-} !RawBuffer e,
bufState :: BufferState,
bufSize :: {-# UNPACK #-} !Int,
bufL :: {-# UNPACK #-} !Int,
bufR :: {-# UNPACK #-} !Int}
-- Defined in ‘GHC.IO.Buffer’
Prelude GHC.IO.Buffer> :t Buffer
Buffer
:: RawBuffer e -> BufferState -> Int -> Int -> Int -> Buffer e
I'll concede that names other than o, u, l, and k would have helped here. Perhaps even renaming ts as texts would have been more clear.
> * For the same reason, frequent use of special characters in operators (, <>, ///, etc.) in the standard library and in third-party libraries was not appealing.
"Special characters" in operators? When most people say special characters they mean unicode. <> and /// look to be normal characters to me. With that said I go back and forth between finding operators appealing/unappealing. For instance I find this to be understandable and a good choice:
"home" </> "cody" </> "Downloads"
I also think that making users learn a few symbols isn't so bad, as long as you don't go to far. The main symbols I'd say you need to know are >>=, <$>, <>, and maybe <> and >=>. >>= doesn't have a non-symbol alternative I don't believe, <$> is fmap, <> is ap, <> is mappend, and I'm not sure if >=> has a non-symbol alternative.
> I was disappointed because I was attracted by referential integrity, strong compiler checks, pattern matching and function composition
Those are many of the reasons I was attracted to Haskell and still use it today, are you claiming Haskell doesn't have those things? If you are making that claim just because exhaustiveness checking isn't an error by default, I'd say that seems disingenious.
> but overall I was left with the impression that the Haskell environment was not very well polished from a software engineering point of view.
Was it just the above that left you with that impression, or was it other things as well? Can you give an example of a language that is well polished from a software engineering point of view and what makes it "polished"?
In fact of all the points I was raising, only the first was about the core language. It's more the ecosystem and some cultural aspects that bothered me. To take a symmetrical example, JavaScript has great tooling and community despite being a language with countless flaws.
Over the years, Python and Ruby ecosystem have gotten pretty OK. Go, Clojure and Elixir are younger and have learned from previous languages, their tooling is not as extensive but they're starting on firmer ground. I'm not sure how to define what makes a well polished software development environment, it's a good question really. I suppose one could write books about subject. But Haskell gave me a fuzzy feeling that things were cumbersome and sometimes buggy. Package management was the show stopper for me. I'm glad Haskell exists and people enjoy coding in it but for me after a while it wasn't that fun so I went to do other things. My arguments are shallow and are more like opinions, but if you care to spread Haskell usage, maybe you can hear them nonetheless.
>> I was disappointed because I was attracted by referential integrity, strong compiler checks, pattern matching and function composition
> Those are many of the reasons I was attracted to Haskell and still use it today, are you claiming Haskell doesn't have those things?
No sorry I phrased that bad. It think Haskell has those things.
I just want to help myself and everyone to have the right information so we can all make the best decisions for ourselves ;)
I get the feeling and I've had it before as well, mostly due to cabal hell. I've found it to be the biggest culprit of making the entire language buggy, but now that I'm using nixos for example I don't feel it anymore.
I can't expect everyone that wants to use haskell to switch to using a different package manager I guess, but I also hear that Halcyon and Haskell LTS (from stackage) solve this problem.
As for the future, I believe this will be the solution until other (partial?) solutions in the works such as backpack (ML like module system) are finished.
Edit: This lack of practical approach is what makes me hesitate in contrast to clojure (just to mention a language), especially if the only goal is to become again a "better developer" rediscovering functional terms and abstractions also documented in other languages.