Reasons to use Haskell as a Mathematician (2006)
blog.sigfpe.com
blog.sigfpe.com
I wrote about it in more detail here: http://slepnev.blogspot.ch/2014/06/is-laziness-wrong-for-equ...
data Natural = Zero | Successor Natural
must include this member as well: omega :: Natural
omega = Successor omega
bool1 = (omega == Zero) -- returns False
bool2 = (omega == Successor Zero) -- returns False
bool3 = (omega == omega) -- whoops, infinite loop
The moral is that some parts of mathematics may be lazy, but mathematical induction has gotta be strict. Since many functional programs rely on induction for proofs of termination, correctness and resource usage, that means they should be strict as well.Am I incorrect in thinking that any strict program proven to be correct and terminating is also correct as a lazy program?
In general, strict languages allow you to say more about termination and time/space complexity of functions than lazy languages. For example, the statement "computing the length of a list takes O(1) space" is true in a strict language, but in a lazy language it's difficult to say what the statement even means, and in most practical cases it's false. Computing length with either foldr and foldl uses at least O(n) space, and the usual advice is to use foldl', which has a strictness annotation. I think this should be alarming to anyone who recommends lazy evaluation as the right default.
In slightly more complicated cases, like sorting a list, there's no sensible way to assign a time or space complexity at all, because it depends on how the function is called and how much of the result is used by the caller. People sometimes claim that's an advantage of lazy languages, e.g. implementing quickselect in terms of quicksort and claiming that quickselect will only evaluate as much of quicksort as needed. I think that's a hack. We need to be able to reason about the time and space complexity of a program in terms of its parts, without relying on implementation details of the parts.
http://arstechnica.com/science/2014/05/scientific-computings...
I have written real time 3D reconstruction and SLAM algorithms in Haskell, so I have had to face this head on.
As languages go, Haskell out paces (not just in elegance but pragmatism too) most of the mainstream imperative languages.
The tooling for scientific computing does need to catch up though.
Remember that scientists don't care about the quality of their code. In many cases, once the code is complete, it will only run once and then be thrown away. There is no maintenance. Things like technical debt don't exist.
And pragmatism? Haskell doesn't even have loops. Loops with mutable variables inside are a very intuitive way of thinking about things and often a better fit to a problem than recursion. Sure, you can use tail recursion (which is just ugly looping) instead, but you're adding unwanted cognitive overhead.
What I would like is a functional language with enclosed, sealed off loops. Syntactic sugar for tail recursion, basically, but Haskell isn't built with programmers in mind; it's built for CS researchers. Computer language researchers, more specifically.
Yes. In fact this is one of the earliest examples [0] of what you can do with the ever more numerous extensions to Haskell's type system.
There are BLAS bindings [1] and several native linear-algebra libraries in Hackage [2] that enlist the type-checker to enforce shape constraints at compile time.
0. See McBride (2001), "Faking It": http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.22.2...
1. http://hackage.haskell.org/package/blas
2. Repa, for instance: http://hackage.haskell.org/package/repa, http://hackage.haskell.org/package/repa-algorithms
The code is usually used once, but it’s modified and “improved” for the next paper. So you add a few new routines here and a few files to read or write there, ... And one day you have a 95 pages fortran 77 program (it’s does’t even follow the fortran 90 standard).
In any dependently typed language, absolutely yes.
I can do it in C# for small matrices, by including the dimensions as type bindings (http://bling.codeplex.com), but that is not practical for larger matrices, nor would it work for matrices loaded from IO.
To the downvoters: are you claiming Haskell is dependently typed? If so, why isn't it used in http://hackage.haskell.org/package/matrix-0.2.1/docs/Data-Ma...? Or just one matrix data type that checks dimension lengths via the type system?
Repa for instance does provide type errors based on dimensionality of matrices. This is a package on Hackage.
See http://www.haskell.org/haskellwiki/Numeric_Haskell:_A_Repa_T...
Looking at the linked page, extent isn't a part of a matrix's type signature, so it would be checked dynamically, correct?
This is exactly how Repa works, it uses a Peano encoding of the extent of dimensions to make invalid array operations inexpressible.
Deleted comment
It's very simple to build a function which doesn't have to understand the possible dimension conflicts and lift it to work on this new type, returning an either (or a maybe, if there's only one failure mode) in place of a definite value.
It's also very simple to propagate such errors forward, so they'll short circuit a computation when you have non-matching matrices used in a calculation that's multiple steps.
In Haskell, I don't have to remember to write special functions which guard against this: I write functions that operate on the matrices and add the guarding at the very end. I can ensure that all my calls use the guarding functions, because they have a different type signature.
Trying to do this same thing in Python require that I remember to always use the guarded calls, and doesn't have as clean of an interface to create the guarded functions from standard functions.
Yes they can, it's known as 'type providers'. http://blogs.msdn.com/b/dsyme/archive/2013/01/30/twelve-type...
It seems so
http://stackoverflow.com/questions/8332392/type-safe-matrix-...
Not when you're doing mathematical research. You can write x86 machine code and the time spent programming is still completely dwarfed by the time spent doing math. Mathematics is full of renowned professors who write F77 by hunting and pecking with one finger. There's no reason for them to use anything "more productive" because they can already code far, far faster than they can think of what to code.
There are extreme outliers for whom this doesn't hold, but they are few and far between.
Productive in what sense and at what cost (in time, especially, but generally)? What benefit is there to learning Haskell when the existing "tooling" in FORTRAN77, C, or C++ is quite adequate for the purpose of research? I mean for the specific purpose of applying it in a research context; not the general sense (i.e. this isn't meant to question the worth learning something new in general).
Some people also use Fortran 95/2003/2008.
That's the funny thing about researchers, some seem extremely obstinate (I suppose in many ways that's a required attribute otherwise they might give up). I wonder if this is why we still have Fortran code around...
If you need any more input from me to convince you to learn something new, then it's not worth my time or yours.
Like I said the tooling isn't as mature but the language enables me to produce results faster with fewer bugs that is much easier to maintain down the road.
The only numerical computing I've ever really programmed for was manipulation of time series data and some standard statistical methods.
I did it at first in Python with Pandas and numpy. Then moved to Haskell. The language was better but the tooling I had to shore up in places.
Take that if it's useful to you :)
Look more like the actual equations? Can you give me a concrete example of Haskell code that solves a simple PDE?
mmMult :: Monad m
=> Array U DIM2 Double
-> Array U DIM2 Double
-> m (Array U DIM2 Double)
mmMult a b = sumP (Repa.zipWith (*) aRepl bRepl)
where
t = transpose2D b
aRepl = extend (Z :.All :.colsB :.All) a
bRepl = extend (Z :.rowsA :.All :.All) t
(Z :.colsA :.rowsA) = extent a
(Z :.colsB :.rowsB) = extent b
http://www.haskell.org/haskellwiki/Numeric_Haskell:_A_Repa_T... readIOArray m (i,j)
But that is misleading because there are two kinds of arrays in Haskell, and the kind (namely, mutable) I used in my literal answer is the less common kind. In fact, it is so uncommon and so contrary to the spirit of Haskell that the language implementations do not take the trouble to make it fast. Last time I played with the Glasgow Haskell Compiler, for example, a related operation (readIORef) was over 100 times slower than its counterpart in C.Parenthetically, because you need to use "a monad" to perform any sort of mutable operation in Haskell, the translation of
m[i,j] := m[j,i]
into Haskell is not writeIOArray m (i,j) (readIOArray m (j,i))
like someone unfamiliar with monadic style might think, but rather readIOArray m (j,i) >>= writeIOArray m (i,j)
or equivalently do
element <- readIOArray m (j,i)
writeIOArray m (i,j) element
But that is a side issue. The central fact is that even though it is technically possible, it is considered bad style in Haskell to access or update individual array elements.And another reason why Haskell will not fair well. A lot of numerical mathematics is linear algebra which involves, almost exclusively, updating array elements.
I'm sure Haskell is good at some things but for many of the types of problems that numerical analysts face Haskell's paradigm just doesn't make sense. Especially when we have languages like Fortran/Matlab which were explicitly designed for computational mathematics.
> with Haskell the code would look more like the actual equations you're working from
so at least in matrix algorithms, and finite difference methods for partial differential equations, the textbooks usually explain the algorithms using a notation that refers to matrix elements. So in these cases, an imperative implementation would be close to the math textbook representation, and the Haskell code would not.
The state of the art in numerical computing really isn't to the point we can work from equations because things like sparsity and which solver you use (Algebraic Multi Grid? Conjugate Gradient Descent? ...) matter a lot and the established algorithms and implementations are really fast and well tested.
Basically without something like Haskell, or at least something like Haskell-or-ML (I didn't need typeclasses and I don't know how much I needed laziness), it seems like that would have been a complete mess.
Of course, the above example is perhaps not the best as it meant I had to [either look up a library, or, what I actually did] implement row-reduction in Haskell, obviously not a great language for it! But ultimately it wasn't too terrible. And the rest as I've said would have been terrible in another language.
(1) Haskell is Functional
(2) Haskell Insulates You
(3) Haskell Performs Well
[...]
(3) Haskell is Declarative
Not being ironic, by the way. From experience, often I can tell pure vs applied mathematicians by how hilariously bad/absent-minded they can be at basic arithmetic/algebraic manipulations.
Same for theoretical- vs systems-oriented CS people, I've found --almost as if being a horrible programmer somehow gives you academic hipster cred.
(EDIT - To clarify: I mean CS academics in grad school, not CS professionals in industry. Also it's just a silly anecdote only marginally correlated with reality: no offense intended to anyone.)
http://www.knowthecosmos.com/destember/destember-experiment-...
He also won an Oscar this year, his second total:
Don't think the last point is particularly true either: being a good programmer is valuable even for theoretical CS. Point in case, recently saw the work leading to a paper on SDD solvers: there was a ton of programming and exploration (albeit in Mathematica/Matlab) that led to the final paper. Also, people working on parallel stuff these days (even the theory guys) usually publish CILK/MPI along with the paper. It's great when you can do both :)
And what I meant is that, anecdotally, academics working on CS theory (eg theory of computation, computational learning theory) are more likely than not to unmistakeably be what I think everyone on HN would agree to call point-blank "horrible programmers": no use of version control, no tests, little concern for organization/modularity/maintainability, etc. And in part it makes sense since they don't have to hack nearly as much as systems-type CS academics --in many ways their work is closer to pure mathematics than engineering/applied math. That's all.
In all just a silly anecdote, although with a kernel of truth. See for instance Scott Aaronson's joke comments about his programming abilities [1]:
On the spectrum of computer science, I'm about as theoretical as you can get. One way to put it is that I got through CS grad school at Berkeley without really learning any programming language other than QBASIC (!).
Second -- and this is purely based upon my personal experience (perhaps we have worked with people in radically different departments or research areas) -- what you say is simply not true in most cases.
I've worked with a lot of theory people, and did not encounter any really bad programmers.
Your specific criticisms:
1. version control. Everyone uses it for everything. Even and especially paper writing. If your experience is that theory phds don't use version control, then I would say your department is/was abnormal (or else we've observed people in very different parts of theory, I suppose).
But also, in many cases, using VCS is more religious than practical. Academic projects are often <10kloc single dev projects. It's more of a useful additional backup mechanism than actually necessary.
2. Testing. If the paper contains a full spec and correctness proof for the implementation (e.g. inference rules, automata, algorithm in terms of some mathematical object) then the point of the implementation is really just transliteration. The space for potential bugs is still there, but the bugs are very different from typical bugs (which, imho, are most often due to poor/vague or even completely absent specification). You simply need fewer unit tests when you know that precisely the thing you are implementing is correct, and just want to make sure you copied things down correctly.
3. The code is often well-written. When it is not, two problems are most common:
a. no comments! Okay, but of course there is a 20 page paper explaining the core idea. And also it's a protoype.
b. Bad structure/poor abstractions. Again, most prototypes don't need to be maintainable. If the core idea gets big and important, it probably makes sense to reimplement as part of a new research agenda anyways (e.g. for performance reasons, or because of unforeseen extensions due to scientific advances, etc). Also, in some cases, the correct way to organize a piece of academic software is highly non-obvious. Organizing your code is much easier when it's a completely solved technical problem in search for new social applications (yet another web app) than when it's a completely new technical idea.
>It's not a sufficient method to describe a person's strengths and weaknesses.
So, there should be if your classifications are correct. You are assuming that 'CS Professor' is equatable to 'good CS People'. But, my classification may not be so rigid.
EDIT: You also still have not defined what it means to be a 'Good Programmer' vs. a 'Horrible' one.