There are certain general precepts of program design that are quite general in nature, for example attempting to separate different components of a program away from each other so that if one needs to make changes to one component it only affects that component and not the whole program. A similar property that can be generally considered good design of a library is to reduce bugs in the usage of the library. The specific manner in which one implements these issues is quite specific to languages.
MissingPy is a library that aims to provide libraries missing from the Haskell standard libraries, by implementing a low level python binding and also some specific bindings on top of it. It seems to be relatively incomplete, and also implements things that are now part of Haskell – but the concept of giving an easy binding to python libraries offers general utility.
I was playing around with said bindings the other week, which caused me some frustration due to what I perceive as poor library design. The libraries themselves have a function called py_initialize, that it is necessary to call before any calls into the interpreter bindings are made. The unfortunate behaviour of this library is to segfault programs that call other methods before calling py_initialize.
Were one to implement this in an object oriented language a possible idea would be to have an interpreter class – and force initialization in the constructor. That way any execution of python code by your bound interpreter instance would have to occur after the interpreter had been initialized.
Unfortunately the translation of these concepts into Haskell was highly procedural in nature. ie one had to call the py_initialize – or be damned if they were foolish. This may be an appropriate choice in something like C – but not in a high level, strongly and statically typed language such as haskell. There are several viable solutions that I can think of – I strongly encourage readers to offer their own.
The simplest solution would be to have py_initialize return a value, that must be passed as an argument to other functions. One could export the type of this value from the module but not its constructor. This would ensure that any user of the module didn’t screw themselves (as I did 4 times in 35 minutes) by only allowing them to call the other function with this as an argument. Unfortunately – passing around immutable tokens in order to enforce security is a little bit ugly, it increases the length of the argument from every function, and is incredibly imperative. It also fails to hide the internal security of the module from the user.
An alternative, that I believe would be better, would be to introduce a new monad, that we shall call PY. Any function that involves a call to the interpreter would need to be in the PY monad. One would replace the py_initialize function with a runPY function that promotes oneself from the IO monad to the PY monad, and initialization could be performed here, thus statically ensuring that this module didn’t combine with my stupidity to cause my test code to segfault.
My point here is that idioms from different paradigms to the same basic problem can frequently be translated into alternative programming languages, but are unnatural in such a setting – resulting in errors that one wouldn’t otherwise expect. Yesterday Faux asked me if I felt programming for another 10 years would genuinely make me a better programmer. I cannot help but answer yes, there are plenty of things that I didn’t know 10 years ago, when I started programming, and plenty of things that I still haven’t figured out yet (list to be supplied on demand). The fact there will inevitably be new languages, new language features, new approaches and new idioms during that period means that I will need to understand solutions to existing problems, that I don’t yet know of. Norvig suggests I might have already reached competency – I hope that is right, but if you stop at competency you have already lost the game (as everyone reading this sentence has).