Tag Archives: haskell

On the novelty factor of compile-time duck typing

Or structural type systems for the pendantic, but I think most people know what I mean when I say “compile-time duck typing”.

For one reason or another I’ve read quite a few blog posts about how great the Go programming language is recently. A common refrain is that Go’s interfaces are amazing because you don’t have to declare that a type has to satisfy an interface; it just does if its structure matches (hence structural typing). I’m not sold on how great this actually is – more on that later.

What I don’t understand is how this is presented as novel and never done before. I present to you a language from 1990:

template <typename T>
void fun(const T& animal) {
    cout << "It says: " << animal.say() << endl;
}

struct Dog {
    std::string say() const { return "woof"; }
};

struct Cat {
    std::string say() const { return "meow"; }
};

int main() {
    fun(Dog());
    fun(Cat());
}

Most people would recognise that as being C++. If you didn’t, well… it’s C++. I stayed away from post-C++11 on purpose (i.e. Dog{} instead of Dog()). Look ma, compile-time duck typing in the 1990s! Who’d’ve thunk it?

Is it nicer in Go? In my opinion, yes. Defining an interface and saying a function only takes objects that conform to that interface is a good thing, and a lot better than the situation in C++ (even with std::enable_if and std::void_t). But it’s easy enough to do that in D (template contraints), Haskell (typeclasses), and Rust (traits), to name the languages that do something similar that I’m more familiar with.

But in D and C++, there’s currently no way to state that your type satisfies what you need it to due to an algorithm function requiring it (such as having a member function called “say” in the silly example above) and get compiler errors telling you why it didn’t satisfy it (such as  mispelling “say” as “sey”). C++, at some point in the future, will get concepts exactly to alleviate this. In D, I wrote a library to do it. Traits and typeclasses are definitely better, but in my point of view it’s good to be able to state that a type does indeed “look like” what it needs to do to be used by certain functions. At least in D you can say static assert(isAnimal!MyType); – you just don’t know why that assertion fails when it does. I guess in C++17 one could do something similar using std::void_t. Is there an equivalent for Go? I hope a gopher enlightens me.

All in all I don’t get why this gets touted as something only Go has. It’s a similar story to “you can link statically”. I can do that in other languages as well. Even ones from the 90s.

Advertisements
Tagged , , ,

Haskell actually does change the way you think

Last year I started trying to learn Haskell. There have been many ups and downs, but my only Haskell project so far is on hold while I work on other things. I’m not sure yet if I’d choose to use Haskell in production. The problems I had (and the time it’s taken so far) writing a simple server make me think twice, but that’s a story for another blog post.

The thing is, the whole reason I decided to learn Haskell were the many reports that it made me you think differently. As much as I like D, learning it was easy and essentially I’m using it as a better C++. There are things I routinely do in D that I wouldn’t have thought of or bother in C++ because they’re easier. But it’s not really changed my brain.

I didn’t think Haskell had either, until I started thinking of solutions to problems I was having in D in Haskell ways. I’m currently working on a build system, and since the configuration language is D, it has to be compiled. So I have interesting problems to solve with regards to what runs when: compile-time or run-time. Next thing I know I’m thinking of lazy evaluation, thunks, and the IO monad. Some things aren’t possible to be evaluated at compile-time in D. So I replaced a value with a function that when run (i.e. at run-time) would produce that value. And (modulo current CTFE limitations)… it works! I’m even thinking of making a wrapper type that composes nicely… (sound familiar?)

So, thanks Haskell. You made my head hurt more than anything I’ve tried learning since Physics, but apparently you’ve made me a better programmer.

Tagged , , , , , ,

Haskell monads for C++ programmers

I’m not going to get into the monad tutorial fallacy. Also, I think this blog about another monad fallacy sums it up nicely: the problem isn’t understanding what monads are, but rather understanding how they can be used. Understanding the monad laws isn’t hard. Understanding how to use the Maybe monad isn’t hard either. But things get tricky pretty fast and there’ s a kind of monads that are similar to each other that took me a while to understand how to use. That is, until I recognised what they actually were: C++ template metaprogramming. I guess it’s the opposite realisation that Bartoz Milewski had.

The analogy is only valid for a few monads. The ones I’ve seen that this applies to are IO, State, and Get from Data.Binary. These are the monads that are referred to as computations, which sounds really abstract, but really functions that return these monads return mini-programs. These mini-programs don’t immediately do anything, they need to be executed first. In IO’s case that’s done by the runtime system, for State the runState does that for you (I’m stretching here – only IO really does anything, even runState is pure).

It’s similar to template metaprogramming in C++: at compile-time the programmer has access to a functional language with no side-effects that returns a function that at runtime (i.e. when executed) actually does something. After that realisation I got a lot better at understanding how and why to use them.

The monad issue doesn’t end there, unfortunately. There are many other monads that aren’t like C++ templates at all. But the ones that are – well, at least you’ll be able to recognise them now.

Tagged , , ,

Unit-tested, acceptance-tested, type-checked and yet still buggy

So I wrote an MQTT broker once (twice really, but I never really finished the second version). It’s now my go-to way of learning a new computer language. Once Rust finally makes it to version 1.0, I’ll write an MQTT broker in it as well. It’s a problem I know well now, it tackles the hairy problems of networking and concurrency, and it’s small enough to not become a huge time sink. So that’s how I decided to try and learn Haskell.

With the immense paradigm shift that came with learning Haskell came a much longer development time. I’m ok with that, especially given that the code is so much shorter. This time I decided to BDD the whole thing, using Cucumber to drive the acceptance tests and writing unit tests for the rest.

After much toiling and trying to wrap my head around monads (I finally understand them! It only took months of reading multiple different blog posts and using them…), I got a version that compiled and passed all tests. A working MQTT broker! So let’s run Jeff’s benchmark on it to see how it compares with the implementations in other languages and… oops.

The program hangs. I try again with 10 messages and 2 connections. It still hangs. How can this be? I did my due dilligence, didn’t I? Aren’t Haskell programs just supposed to work if they compile? I mean, I had to jump around hoops to write a function to convert character literals into Word8 values so write my tests! After a lot of “printf” debugging (the Haskell debugging experience is not ideal) I find what’s causing the bug, and as always, it seems obvious in hindsight.

Networking is dirty, ugly and variable. Not the kind of thing that lends itself well to being unit-tested. So I cheated a bit. The part that dealt with client connections and whether or not the server should disconnect went into the main loop and wasn’t unit tested at all. I felt bad about it at the time but let it go on one of those “it’s ok, I know what I’m doing” feelings. And as usual, I break my own rules at my own peril.

I had an acceptance test for it, and it passed. It just wasn’t comprehensive enough. The TCP traffic has to be just so to trigger the bug, and it’s actually hard to craft packets that look like real-world usage scenarios. Who wants to have a list of bytes 512 bytes long in their test code, much less several of them?

My conclusion of the error of my ways? Not enough unit-testing. Too much code outside the nice, warm and fuzzy pure core. The feeling I shook off about the dirty networking code not being unit tested? I’m never doing that again. The fix is going to be relatively simple: purify as much of the code as I can so it’s trivially unit-testable and have the “real” code be a thin wrapper over the pure code.

The worst is that was already my belief of how to develop robust software. I just didn’t follow my own advice.

Tagged , , ,

Computer languages: ordering my favourites

This isn’t even remotely supposed to be based on facts, evidence, benchmarks or anything like that. You could even disagree with what are “scripting languages” or not. All of the below just reflect my personal preferences. In any case, here’s my list of favourite computer languages, divided into two categories: scripting and, err… I guess “not scripting”.

 

My favourite scripting languages, in order:

  1. Python
  2. Ruby
  3. Emacs Lisp
  4. Lua
  5. Powershell
  6. Perl
  7. bash/zsh
  8. m4
  9. Microsoft batch files
  10. Tcl

 

I haven’t written enough Ruby yet to really know. I suspect I’d like it more than Python but at the moment I just don’t have enough experience with it to know its warts. Even I’m surprised there’s something below Perl here but Tcl really is that bad. If you’re wondering where PHP is, well I don’t know because I’ve never written any but from what I’ve seen and heard I’d expect it to be (in my opinion of course) better than Tcl and worse than Perl. I’m surprised how high Perl is given my extreme dislike for it. When I started thinking about it I realised there’s far far worse.

 

My favourite non-scripting languages, in order:

  1. D
  2. C++
  3. Haskell
  4. Common Lisp
  5. Rust
  6. Java
  7. Go
  8. Objective C
  9. C
  10. Pascal
  11. Fortran
  12. Basic / Visual Basic

I’ve never used Scheme, if that explains where Common Lisp is. I’m still learning Haskell so not too sure there. As for Rust, I’ve never written a line of code in it and yet I think I can confidently place it in the list, especially with respect to Go. It might place higher than C++ but I don’t know yet.

 

Tagged , , , , , , , , , , , , ,