notes-computer-programming-programmingLanguageDesign-prosAndCons-haskell

my personal opinion: there's a lot to like about Haskell. I list some of the cons at [1].


toread:

---

toread http://news.ycombinator.com/item?id=4721550

http://neugierig.org/software/blog/2011/10/why-not-haskell.html

---

"

> Haskell and Ruby have continuations

I wonder where this meme of Haskell having continuations started. It pops up here and there, like some mystical incantation that makes the speaker closer to the gods of programming languages.

Haskell has precisely the same continuations JS has, it's just capable of abstracting CPS away. Somewhat.

See http://hackage.haskell.org/pac... . "

"

DanWaterworth? 4 days ago

link

Haskell doesn't have first-class continuations, it has syntactic sugar that allows you to write continuation passing code in a direct style.

reply "

--- http://www.quora.com/Haskell/What-are-the-main-weaknesses-of-Haskell-as-a-programming-language

" What are the main weaknesses of Haskell as a programming language?

Jesse Tov, writes Haskell all day. 92 votes by Marc Bodnick, Omer Zach, Matthew Hill, (more) Haskell’s main technical weakness compared to popular, mainstream languages is probably the lack of a comprehensible cost model. Essentially, laziness makes it difficult to reason about when computation happens, and it can result in serious space leaks. While GHC can produce very efficient code, convincing it to do so in cases where that isn’t the default behavior is a dark art with few real masters. I’ve been writing Haskell for over a decade and using it as my main language at work for at least five years, and I’ve never learned to do this beyond simple cases—mainly because I don’t write code that needs to run efficiently.

There are some other things that are easy in many other languages but difficult in Haskell. For example, purity makes it slightly painful to discover and propagate run-time configuration information. In most languages it’s easy to read a configuration file at startup and store the result in a global variable, but in Haskell that requires dirty tricks.

Haskell has some other weaknesses compared to other academic/research languages. For example, its module system (if you can even call it that) is clearly inferior to Standard ML’s and OCaml’s module systems. Its compile-time meta-programming facility, Template Haskell, is occasionally useful, but it’s a poor substitute for a real, state-of-the art macro system. And of course, there are many things that some languages can do but others can’t. For example, if you want to prove your programs correct, Haskell’s type system, even with recent extensions in that direction, won’t let you prove as much as Coq or Agda—but that’s asking a language to do something that it wasn’t designed for, so I don’t really consider it a valid criticism.

From a theoretical perspective, some people have complained that call-by-need makes Haskell harder to reason about. It’s true that Haskell’s function space is a bit messier what you get in a language that’s both pure and total, but I don’t buy that it’s worse than your standard impure, call-by-value language—it’s just different.

Finally, Haskell does suffer some social weaknesses by being weird and researchy. Hiring large numbers of Haskell programmers is going to be difficult (though the ones you do find are likely to be very good). Because it’s so different from the languages that most people know, it presents a potentially steep learning curve. And of course, the community, while helpful and prodigious, is tiny compared to more popular languages. The variety and quality of libraries available on Hackage is impressive given the number of Haskell programmers out there, but it cannot compete with Java for mindshare. Finally, because Haskell is still very research-oriented, it changes quickly and sometimes unpredictably. I’ve found that my Haskell code breaks and needs to be updated not only for major GHC version increments but often for minor versions as well. This is annoying, but it’s a price I’m willing to pay to program in a beautiful language with top-notch abstraction facilities.

--

http://www.yesodweb.com/blog/2011/09/limitations-of-haskell

---

 "Namespace clashing, particularly for record fields

data Record = Record {a :: String } data RecordClash? = RecordClash? {a :: String }

Compiling this file results in:

record.hs:2:34: Multiple declarations of `Main.a' Declared at: record.hs:1:24 record.hs:2:34 "

solution ideas:

http://ghc.haskell.org/trac/haskell-prime/wiki/TypeDirectedNameResolution

however a comment on http://www.yesodweb.com/blog/2011/09/limitations-of-haskell notes that typeclasses are a better soln (so we should have everythingIsATypeclass , just as i've been saying for jasper)

---

history via Peyton-Jones

http://research.microsoft.com/en-us/um/people/simonpj/papers/haskell-retrospective/index.htm

http://research.microsoft.com/en-us/um/people/simonpj/papers/history-of-haskell/

typeclasses, also with a useful intro to how the compiler implements typeclasses http://research.microsoft.com/en-us/um/people/simonpj/papers/haskell-retrospective/ECOOP-July09.pdf

vs erlang http://research.microsoft.com/en-us/um/people/simonpj/papers/haskell-retrospective/Haskell-Erlang-Jun09.pdf

http://research.microsoft.com/en-us/um/people/simonpj/papers/haskell-retrospective/HaskellRetrospective-2.pdf

---

--

" The second problem with Monads is related to their great strength - they are synonymous with Domain Specific Languages. The mission statement for domain specific languages is stupendous - don't write a complex program to solve your problem, write a simple program in a programming language you have designed solely for that task. I've already mentioned the best use of DSL/Monads - Haskell's parsec module. With Parsec the Haskell function to parse a file is identical to the Backus Naur Form description of the parse grammar - how much clearer could it be? They say that imitation is the highest form of flattery, and every parser function I have written outside of Haskell since meeting Parsec has been a shoddy facsimile of Parsec in the chosen language. The success of Parsec and its ilk has filled Hackage (the Haskell module repository) with hundreds of DSLs covering any task you care to mention.

Yes, literally hundreds of them. Hundreds of little programming languages, one for BNF parsing, one for parsing xml, one for creating PDFs, all perfectly suited to their task. Each is different, and each has its own learning curve. Consider a common task such as parsing an XML file, mutating it according to some JSON you pulled out of a web API and then writing to a PDF. In Ruby or a similar object oriented language you expect to find three APIs/gems, all with a similar object oriented syntax, but for three Haskell DSLs designed for three different tasks to share syntax implies that their authors failed to optimise them for those tasks, hence instead of five minutes with API documentation you have hours of DSL tutorials ahead of you before you can begin work. It is this subtle difference that plants Haskell on the academic side of the town and gown divide. With Haskell the focus is on the language, not the problems you are solving with the language. "

---

probs with laziness, esp. lazy I/O: http://users.aber.ac.uk/afc/stricthaskell.html

--

"

In Haskell it’s easy parallelize code, but performance is not only using your cpu cores. And it’s hard to write code that can have paragonabile performance to C++ code

Today a cpu silicon is invested principally in: cache

You solve one problem, but you regres in others: memory usage

Haskell promote a coding style where your data is sparse (terrible data locality), and even basic data types are boxed

Use a vector? Not idiomatic Haskell! You have to use some tree!! So your code will execute on all your core, but all your core will spend the majority on time on frontend stall! Not a great speedup "

--

" If people write custom allocators, is not because they are fools

And having a bad cache usage in a multi threading environment is also worser that in a single threads environment: MESI protocol will take even additional time

And in all of this, not a word on NUMA architecture, with will be probably predominant in the future. How Haskell can be made NUMA aware?

You are making it like “We don’t need to worry about low level details”…where is my api for cpu affinity? When a thread will be scheduled on another code, the data set has to be readed twice from the memory This matters in high performance code

If the OS expose api like that, is not because they are fools "

" Bartosz Milewski Says:

September 19, 2013 at 6:24 pm

@Nicola: I really recommend Simon Marlow’s book. It will answer your questions. Locality and unboxing is very important indeed and, at least for matrix calculations, is provided by the Repa library in Haskell. GPU code is produced by the Accelerate library. On GPUs you usually have to copy your data to private GPU memory, and Accelerated does it for you. These libraries are all about scalable performance. Parallel programming is pretty useless if it can’t speed programs up. "

" Edward Kmett Says:

September 19, 2013 at 8:17 pm

Nicola,

I’ve been slowly building up a set of cache-oblivious yet still purely functional data structures in Haskell. Ryan Newton has been working towards NUMA-awareness. I have a decent high performance lock-free work-stealing deque and tbb-style task monad. With GHC 7.8 we’re getting primops for working with SIMD, prefetching, etc. and filling in other gaps in the high-performance spectrum.

Moreover I would challenge that the ‘vector isn’t idiomatic haskell’ argument is a few years old. Nowadays vector, repa, and accelerate are all pretty well en-meshed in the culture.

That said, CPU affinity _is_ still something we don’t have a story for. It bites me too. HECs don’t tend to move around, but that isn’t the best guarantee.

I won’t lie and say we have all of these things today, but we’re not ignoring them. Haskell is continuing to evolve and borrow good ideas. "

--

" thirsteh 36 days ago

link parent flag

This is highly anecdotal, but I've built and been part of very practical/non-theoretical, large Haskell projects (100k+ lines, which is a lot for Haskell). The only big complaint I have is that it's somewhat hard to do loose coupling, i.e. for something somewhere to reference a type without either redeclaring the type (when that's possible), or having a huge, centralized Types.hs that declares all the types that are used in different places (to avoid cyclic imports.) (Contrast with e.g. Go or ML where you have interfaces/modules without 'implements'.)

This isn't unique to Haskell by any means, but it's the only real complaint I have about Haskell as a language for non-toy projects. The benefits definitely make it my go-to language. It's hard to list them all, but by far the nicest feeling is the correctness: when your code compiles, 60% of the time your program works every time. (Not to imply that tests aren't necessary--QuickCheck? is great for that.) It's an otherworldly feeling to write a program not in terms of what to do, but what kinds of filters you want to put on something, and have it just work (and either stay working, or break future compiles if something's changed!) after compiling 10 lines of code, when you would have written at least 50-70 and had to debug it in almost any other language.

Edit: I'll add another complaint: Haskell is like C++ in that it's incredibly easy for a codebase to become completely unmanageable if your team doesn't have a common style/discipline. Go is a nicer language for "average"/"enterprise" teamwork, I think, since it almost forces you to write programs in a way everyone will understand. If you're in a team with good programmers that you trust not to abuse the language, this is a non-issue.

Edit: Okay, another one: If you change your Types.hs, the recompilation can take a long time in a large codebase, similar to C++. But GHC/Cabal keep getting faster.

Think that's it.

upvote

evincarofautumn 36 days ago

link

Have you tried .hs-boot files for separate compilation with circular dependencies? It’s not bad, but it does lead to some extra crapwork when changing interfaces, much like with C++ headers.


upvote

thirsteh 36 days ago

link

I haven't, and it does look pretty cumbersome. So far a couple of Types.hs, i.e. for functionally separate components, have worked out fine, but I'll check it out if that becomes unwieldy.


upvote

Dewie 36 days ago

link

How long does it take to compile the whole codebase? If you know.


upvote

thirsteh 36 days ago

link

My biggest project probably takes about 5-7 minutes to compile in dev mode (no optimizations), and about 15-20 minutes with -fllvm and -O2 on a not-super-beefy i7 laptop (I haven't really timed it recently.)

It's not noticeable in general, since only the Types.hs affect many different files, and they're rarely changed. Also, 99% of the compiles are partial, i.e. only the files that have changed (or dependencies of them) are recompiled. Partial compiles don't feel slower than ones on my smaller Haskell projects.

Is it annoying compared to e.g. Go? Definitely. But it isn't ruining my life. And there's a lot of optimization, fusion particularly, that goes on in the background.


upvote

rybosome 36 days ago

link

You frequently mention nice things about Go in comparison to Haskell. I'm learning Go at the moment, and must admit that I find myself struggling to stay motivated. Given your background in FP (and apparent enjoyment), what do you find appealing about Go?


upvote

thirsteh 36 days ago

link

I like both Go and Haskell. They are wildly different languages, of course (even though some things are at least a little bit similar, like typeclasses and interfaces, cabal and go get, type signatures/inference, 'forkIO' and 'go', etc.)

If it's just me making something, 99% of the time I'll pick Haskell, unless I know everything that I'm going to do is mutate a hash table or array, in which case I use Go. (Not that that's not doable in Haskell, it's just not as intuitive/easy to do efficiently. Keep in mind that I said "if it's the only thing"--Haskell's downsides in this area aren't significant if you're also doing other things, and especially so if just some of those things are pure/don't have side effects.)

The reason why I say "if it's just me" is that Go is much nicer to use in normal teams. To me, it's a Java/Python/Ruby/JS competitor. Let's face it, a lot of enterprise teams aren't as disciplined or as good at/interested in programming as they could be. For this, Go is perfect. You could read that as "Go is for average programmers", and that is true--in a good way. It's extremely easy to pick up for new members of the team (Haskell is very hard, I have to admit!), everybody can collaborate without asking a lot of questions, because of 'go fmt' there's never any indent wars, and it's a snap to compile and deploy binaries.

If I have to do anything like map/reduce/filter, really any kind of operation on a set, I hate using Go. But it's not Go that I hate; it's most imperative languages.

...

When friends ask me which language to look at, I say "get to know Python, then learn Go" nowadays, mainly because pointers and pass-by-value can be a little hard to understand. Go is a great Python replacement. Web applications and APIs/backends I've particularly enjoyed implementing in it. Haskell is for when you've spent so much time coding in imperative languages that it's all boring, and you want to step into an interesting, but extremely frustrating (at first) new world.

A lot of people say e.g. Haskell would be as easy to pick up if it was your first language. I don't think that's true, if only because of the amount of syntax you have to learn--Scheme is probably better--but I do wish I could go back and try.

upvote

wtetzner 36 days ago

link

Any reason not to use, say, OCaml when working on a team? Its module system seems like it would make it well-suited to working with other people.


upvote

thirsteh 36 days ago

link

Or F#. No particular reason, I/we just don't like them as much. (Haskell has many other unique qualities, e.g. proper STM.)

---

"

If a language cannot define lazy function it simply lacks in abstraction power. I think any kind of fragment of the language should be nameable and reusable. (Haskell lacks this ability for patterns and contexts; a wart in the design.) So if you notice a repeated expression pattern, like

    if c then t else False

and cannot give this a name, like

    and c t = if c then t else False

and then use it with the same effect as the orginal expression, well, then your language is lacking.

For some language constructs the solution adopted by Smalltalk (and later Ruby), i.e., a very lightweight way on constructing closures is acceptable. So, for instance, I could accept writing

    ... myAnd x {y} ...

(In SML you could make something using functors, but it's just too ugly to contemplate.) "

-- " Cyclic data structures This is related to the last point.

Sometimes you really want cyclic data structures. An example are the Haskell data types in Data.Data that describe data types and constructors. A data type descriptor needs to contain a list of its constructors and a constructor descriptor needs to contain the data type descriptor. In Haskell this can be described very naturally by having the two descriptors reference each other. In SML this is not possible. You will have to break the cycle by somthing like a reference (or a function). In OCaml you can define cyclic data structures in a similar fashion to Haskell, so this isn't really a problem with strict languages, but rather a feature that you can have if you like. Of course, being able to define cyclic data leads to non-standard elements in your data types, like

    data Nat = Zero | Succ Zero
    omega :: Nat
    omega = Succ omega

So having the ability to define cyclic data structures is a double edged sword. I find the lack of a simple way to define cyclic data a minor nuisance only. "

--

"

upvote

orclev 36 days ago

link

I've been trying to come up with a general way to rank Haskell experience, and so far I've got:

Beginner: Can read the basic syntax, knows about E.G. Monads and folds

Intermediate: Understands and can use the more advanced classes like Applicative, Arrow, lenses, and how to use things like fix

Advanced: ??? Makes new powerful classes/libraries like Pipe, Conduit, or Netwire? Can easily translate a problem domain into a concise and elegant type representation.

Maybe there needs to be some more layers in there, I don't know... also the advanced level feels weak to me. I'm still somewhere between beginner and intermediate myself.


upvote

sseveran 36 days ago

link

Well I don't claim to understand the mathematical basis of Monads, and I don't use arrows and just started using applicative more often. Lenses rock and I have used those for a long time. I do build my own Monad Transformers so maybe I am still a beginning with intermediate tendencies. :-) "

--

" But nobody has successfully made functional code approachable either, because too often it's contaminated by syntactic sugar that's too symbolic, various handcuffs that make even the simplest tasks arduous (poor handling of global state), or learning curves that can't be scaled when it comes to interacting with real-world time-based data (monads etc). " --- " No, you're definitely not an idiot. Haskell's type system is a completely different language, semantically speaking, from its language of terms. This type language is much more like a logic programming language than a functional one. Type variables are unified by the type inference engine, a concept with which most people are unfamiliar. Type constructors are easily mistaken for data constructors (they often use the same name). A lot of Haskell tutorials gloss over these details which is unfortunate. "

--

upvote

boothead 2 days ago

link

How much interest would there be in a 0 to full Haskell development environment set of ansible scripts (and or Vagrantfile)? I'm working on a start up using Haskell at the moment and I've been capturing all of my set up in this way. If folks are interested I can make some of this stuff available.

reply

upvote

jcurbo 2 days ago

link

Yes, but I'd be more interested in how you put it together than the end product. A good blog post breaking your process and components down would be fantastic.

reply

upvote

Keyframe 2 days ago

link

What do you install and setup apart from ghc/i?

reply

upvote

boothead 2 days ago

link

Emacs config. Latest versions of cabal cabal-install. A work around for the brokenness of the Haskell platform on ubuntu 13.04. Threadscope. Proper cabal setup for testing. Jenkins setup for CI from the start.

Also investigation dockerizing Haskell services too.

reply

upvote

Peaker 2 days ago

link

Do you have a good ~/.cabal/config auto-installed with library profiling enabled?

reply

upvote

boothead 2 days ago

link

Yes, but I have moved to doing most stuff in sandboxes now and cabal repl is not playing nice with profiling at the moment.

reply

upvote

Keyframe 2 days ago

link

I'd be more than interested to see what you got. I want to introduce Haskell to our production soon, but am still not sure about the whole process of managing it at scale. This would definitely help as an info if nothing else.

reply

upvote

dmmalam 2 days ago

link

this would be very interesting

reply

upvote

harichinnan 2 days ago

link

Up voted. Please post a link.

reply

upvote

nbouscal 2 days ago

link

hdevtools, hoogle, hlint

reply

upvote

boothead 2 days ago

link

Yep, those are all installed as part of my emacs set up.

reply

---

freyrs3 2 days ago

link

Ok, here's a book on doing Natural Language Processing with Haskell: http://nlpwp.org/book/index.xhtml

reply

--

http://www.willamette.edu/~fruehr/haskell/evolution.html

--

"

barrkel 6 hours ago

link

Java classes have to be declared to implement an interface. Haskell types don't need to be declared to "implement" a typeclass, since the typeclass instance is declared separately. You don't need to modify the original definition in order to package up a value + functions into an existential; to me, that's the essential advantage of duck typing for larger projects, where you don't have the ability to freely modify any and all source included.

reply "

--

" [1] I tried Haskell and Elixir as candidates, but nested data holders with multiply circular references appear to be problematic to deal with in functional languages. Immutable data presents interesting challenges when it comes to cyclic graphs! The solutions suggested by the respective communities involved considerable boiler-plate. More importantly, the resulting code lost direct correspondence with the problem's structural elements. Eventually, I abandoned that approach.↩ "

asdasf 1 hour ago

link

His dismissal of haskell is rather misinformed. Haskell has no such problems with cyclic data structures. And this:

>Except that abstracting things into functions that take the `old' graph as input, and answer the `new' graph as output are not very convenient or easy.

Is just crazy. Yes, it is very convenient and easy: http://hackage.haskell.org/package/mtl-2.0.1.0/docs/Control-Monad-State-Lazy.html

reply

--

"

signa11 8 hours ago

link

probably this: http://research.swtch.com/generic ? go had 3 options for implementing generics:

1. leave generics out : the c way (slows programmer)

2. do it at compile time: c++ way (slows compilation)

3. box/unbox everything implicitly: java way (poor perf)

reply

"

"

SideburnsOfDoom? 7 hours ago

link

Hm. Which of these does C# use? Which does Haskell use?

reply

pjmlp 7 hours ago

link

Regarding C# it depends on which compiler you talk about.

Microsoft's current JIT/NGEN, generate a single version for reference types and a separate version for each value type. So a mix of Java/C++ approaches.

Not sure what the new JIT/NGEN compilers, RyuJIT? will use.

I also don't know what approaches are taken by Mono, Bartok and IL2CPU, but they might be similar.

reply

SideburnsOfDoom? 5 hours ago

link

Yep, now I can see why this is trilema is "incomplete at best" - c# and other use what can be described as a mixture of these three approaches.

I was going to say that the c# compiler is fast enough despite this, but then I remembered that one of go's selling points is that the go compiler is blindingly fast compared to languages such as c#. Perhaps maintaining that performance with generics is a real issue.

reply

pjmlp 3 hours ago

link

Although any language with modules is fast enough, compared with C and C++.

Many old timers like myself can remember the blazing fast compile times of Modula-2 and Turbo Pascal compilers in MS-DOS systems.

Go compilers also lack a strong optimizer, which is tends to slow down compilation.

reply

dbattaglia 7 hours ago

link

I believe C# does it at runtime for reference types, and compile time for value types, so that you don't eat the cost of boxing at runtime. It doesn't make sense to make separate compile time generic types for reference types in C# since the type is basically constrained to using system.object members anyway (unless you add type constraints to the generic type class definition).

reply

thirsteh 5 hours ago

link

Haskell: A mix of 2 and 3.

reply

arnehormann 6 hours ago

link

Also look at a list of proposed ways to add generics (and why they didn't make it): https://github.com/remyoudompheng/go-misc/blob/master/generi...

reply "

https://github.com/remyoudompheng/go-misc/blob/master/generics/generics.markdown

--

" Records

Haskell is supposed to be about declarative programming. Haskell programs should look like specifications. Haskell is supposed to be succint and Succinctness is Power, according to Paul Graham.

One area where this breaks down quickly is records.

Compare Erlang

    -record(pot, {
      profit = 0,
      amounts = []
     }).

with Haskell

    data Pot = Pot
        {
         pProfit :: !Word64,
         pAmounts :: ![Word64] -- Word16/
        } deriving (Show, Typeable)
    mkPot :: Pot
    mkPot =
        Pot
        {
         pProfit = 333,
         pAmounts = []
        }

The Haskell version requires twice as much lines of code just to initialize the structures with meaningful defaults. I have 164 record in the program, some of which are rather large. Renaming and using the Haskell accessor functions gets rather tedious after a while and there’s nothing elegant in having to explain to the customer how xyFoo is really different from zFoo when they really mean the same thing. This might seem like no big deal but, again, I have a lot of records.

I tried creating classes for each “kind” of field and I tried using HList to put these fields together into records. This seems like 1) a terrible hack compared to the Erlang version and 2) not very efficient. I did not get to measuring efficiency with a profiler but I did have GHC run out of memory trying to compile my HList code. SPJ fixed this but I decided not to take it further.

" -- http://wagerlabs.com/blog/2006/01/01/haskell-vs-erlang-reloaded/

--

" Speaking of monads… There’s not a lot of beauty in this:

type ScriptState? b = ErrorT? String (StateT? (World b) IO) type ScriptResult? b = IO (Either String (), World b) type Dispatcher b = Event -> (ScriptState? b) Status data Status = Start

Eat !(Maybe Event)
Skip
    deriving Showinstance Show (String, Dispatcher b) where show (tag, _) = show tag runScript :: World b -> (ScriptState? b) () -> ScriptResult? b runScript world = flip runStateT world . runErrorT

and then this:

withFilter :: Event -> (Event -> (ScriptState? b) ()) -> (ScriptState? b) () withFilter event fun = do w <- get let p = trace_filter w unless (p event) $ fun event

In fact, I still don’t have anywhere near in-depth knowledge of how to write my own monad.

Erlang is free of side effects (built-in function aside) just like Haskell but pattern-matching becomes far easier and the code becomes much smaller when you don’t have to deal with static typing. To wit:

%%% Matching on a tuple handshake(Bot, _, {udp, _, , ?SRV_PORT, 1}) -> bot:trace(Bot, 85, "handshake: ~w: Error: ~w~n", [?LINE, Code]), erlang:error({handshake_error, Code}); % Matching on a tuple of a different size (records are tuples in Erlang) handshake(Bot, [_, , Event], #srvhandshake{}) -> Bot1 = bot:pop(Bot), bot:post(Bot1, Event), {eat, Bot1}; % Yet another tuple handshake(Bot, Args, X = {tcp_closed, _}) -> bot:trace(Bot, 85, "Connection closed during handshake, retrying"), Bot1 = retry(Bot, Args, X), {eat, Bot1};

"

-- http://wagerlabs.com/blog/2006/01/01/haskell-vs-erlang-reloaded/

--

" Concurrency

Concurrency in Haskell deserves a praise, specially when used together with STM. Threads are lightweight (1024 bytes on the heap) and easy to launch and STM is a beautiful thing. Nothing beats being able to just send yourself a message, though. This is something that you can easily do with Erlang.

Erlang processes (327 bytes starting up, including heap) come with a message queue and you retrieve messages with “selective receive” that uses the same pattern-matching facilities as everything else.

%%% Dispatch event run(, {keepgoing, Bot}) when is_record(Bot, bot) -> receive {tcp, _, 2} -> Event = unpickle(Bot, Packet), run(Bot, handle(Bot, Event)); {script, Event} -> case Event of {tables, [H

trace(Bot, 95, "Event: {tables, [~w, ~w more]}", [H, length(T)]); _ -> trace(Bot, 95, "Event: ~p", [Event]) end, run(Bot, handle(Bot, Event)); Any -> run(Bot, handle(Bot, Any)) end;
T]} ->

This code just works. It collects network messages, events, timer events, you name it. Posting an event is also easy.

post(Bot, Event) -> self() ! {script, Event}.

I tried implementing this scheme using STM.TChan but failed. The best example of this is my logger. The most natural way to implement logging seemed to be by reading from a TChan in a loop and printing out the messages. I launched several thousand threads, all logging to the single TChan. Bummer, I think I ran out of memory.

Follow-up discussions on Haskell-Cafe narrowed the issue down to the logger thread not being able to keep up. I took this for granted and implemented a single-slot logger. This worked and reduced memory consumption drastically but I believe introduced locking delays in other places since threads could only log sequentially.

Erlang provides the disk_log module that logs to disk anything sent to the logger process. The logger can be located anywhere on a network of Erlang nodes (physical machines or VMs) but I’m using a local logger without any major problems so far.

Could the difference be due to differences in the scheduler implementation?

The Erlang version of my code has a separate socket reader process that sends incoming packets as messages to the process that opened the socket. This is the standard way of doing things in Erlang. Network packets get collected in the same message queue as everything else. It’s the natural way and the right way.

I tried to do the same with Haskell by attaching a TChan mailbox to my threads. Big bummer, I quickly ran out of memory. The socket readers were quick to post messages to the TChan but the threads reading from it apparently weren’t quick enough. This is my unscientific take on it.

Moving to single-slot mailboxes did wonders to lower memory consumption but introduced other problems since I could no longer send a message to myself from the poker bot thread. The socket reader would stick a packet into a TMVar and then the poker bot code would try to stick one in and block. This caused a deadlock since the bot code would never finish to let the thread loop empty the TMVar.

I ended up creating a bunch of single-slot mailboxes, one for the socket reader, one for messages posted from the poker bot code, one for outside messages like “quit now”, etc. Thanks to STM the code to read any available messages was elegant and probably efficient too but overall the approach looks hackish.

fetch :: (ScriptState? b) (Integer, Integer, Integer, (Event)) fetch = do w <- get liftIO $ atomically $ readQ (killbox w) `orElse` readQ (scriptbox w) `orElse` readQ (timerbox w) `orElse` readQ (netbox w)

I had to replace this code with some other hack to be able to run retainer profiling since it does not work with STM.

I also had issues with asynchronous exceptions (killThread blocking?), including crashes with a threaded runtime. "

-- http://wagerlabs.com/blog/2006/01/01/haskell-vs-erlang-reloaded/

--

" Serialization

This horse has been beaten to death by now. I would just say that thinking of Haskell binary IO and serialization makes me cringe. Binary IO is so damn easy and efficient with Erlang that I look forward to it. Specially after I wrote the Erlang version of the Pickler Combinators. Please refer to Bit Syntax for more information. I would give an arm and a leg to stick to binary IO in Erlang rather than process XML or other textual messages, just because it’s so easy.

With Haskell I tried reading network packets as a list of bytes which was elegant but not very efficient. I also tried serialization base don Ptr Word8 and IOUArray. I don’t think there’s a lot of difference between the two efficiency-wise. allocaBytes is implemented on top of byte arrays, for example.

allocaBytes :: Int -> (Ptr a -> IO b) -> IO b allocaBytes (I# size) action = IO $ \ s -> case newPinnedByteArray# size s of { (# s, mbarr# #) -> case unsafeFreezeByteArray# mbarr# s of { (# s, barr# #) -> let addr = Ptr (byteArrayContents# barr#) in case action addr of { IO action -> case action s of { (# s, r #) -> case touch# barr# s of { s ->

    s, r #)
    }}}}}

I would preferred serialization on top of byte arrays since you can inspect them and see the data. There’s no version of Storable for arrays, though. Not unless you use a Storable Array and then it can only be an array of that instance of Storable. "

-- http://wagerlabs.com/blog/2006/01/01/haskell-vs-erlang-reloaded/

--

" Inspecting the environment

Erlang has plenty of tools to inspect your environment. You can get the number of processes running, a list of process ids, state of each process, etc. This very convenient for debugging. Other libraries

I can log any Erlang term to disk, store it in a database, etc. This makes my life significantly easier. Conclusion

I was able to finish the Erlang version 10 times faster and with 1/2 the code. Even if I cut the 10-11 weeks spent on the Haskell version in half to account for the learning curve, I would still come out way ahead with Erlang.

This is due to language issues where static typing and records are working against me. This also due to the many GHC/runtime issues that I stumbled upon, specially with regards to concurrency, networking and binary IO. Last but not least, this is due to the much better library support on the Erlang side. "

-- http://wagerlabs.com/blog/2006/01/01/haskell-vs-erlang-reloaded/

--

haskell from a scala POV, toread:

https://github.com/pchiusano/fpinscala/wiki/A-brief-introduction-to-Haskell,-and-why-it-matters

--

http://www.haskellforall.com/2014/03/introductions-to-advanced-haskell-topics.html

---

http://engineering.imvu.com/2014/03/24/what-its-like-to-use-haskell/

---

haxl library:

https://code.facebook.com/projects/854888367872565/haxl/ https://news.ycombinator.com/item?id=7873933

awda 9 hours ago

link

Why Haskell?

reply

lbrandy 9 hours ago

link

Ah, my favorite question.

We previously had a custom DSL and it outgrew it's DSL-ness. The DSL was really good at one thing (implicit concurrency and scheduling io), and bad at everything else (cpu, memory, debugging, tooling). The predecessor was wildly successful and created new problems. Once all those secondary concerns became first order, we didn't want to start building all this ecosystem stuff for our homemade DSL. We needed to go from DSL to, ya know, an L. So the question is which...

If you understand the central idea of Haxl, I don't know of any other language that would let you do what Haxl in Haskell does. The built in language support for building DSLs (hijacking the operators including applicative/monadic operations) -really- shines in this case. I would -love- to see haxl-like implicit concurrency in other languages that feel as natural and concise. Consider that a challenge. I thought about trying to do it in C++ for edification/pedagogical purposes but it's an absolutely brutal mess of templates and hackery. There may be a better way, though.

reply

msie 4 hours ago

link

Did you have a problem with namespace collisions of identical field names in records? Is that much of a problem in Haskell? How did you deal with them? Thanks.

reply

thoughtpolice 4 hours ago

link

To be completely honest, the namespace/module situation with Haskell could certainly be a _ton_ better, but after 8 years of it I can't ever remember a time when it was ever at the top of my mind as game-breaking. Occasionally quite annoying? Yes, most definitely. But I'd say there are more many more annoying things day to day, and in any case, it is certainly a tradeoff I'll put up with for the returns.

That said, GHC 7.10 will ship with a new extension called `OverloadedRecordFields?` that will allow you to have identical record field names for different datatypes. Yay!

reply


Footnotes:

1. ?SRVERROR, Code:32

2. Packet/binary