227 karma · joined December 29, 2012
That leaves a few other uses: macros as compiler extensions for generating fast code (see "Paradigms in Common LISP" for a lovely example of a parser generator). But heed warnings about premature optimisation.
It leaves macros for novel binding strategies. List comprehensions, do-notation, destructuring-bind, pattern matching, CL's LOOP and macros that anonymously bind a pronoun like "it".
I have no shame in using macros for really succinct control structures where delaying evaluation with a lambda would just look gross (see "or" and "and" macros), but maybe that's just a cry for lazy evaluation :P
I think the fact that strings are linked lists is great for beginners. One of their assignments is on web scraping, where they can reuse all their list based functions to solve the problem.
I am not too keen on Haskell's polymorphism for teaching beginners, and error messages are mostly useless for them. If I were teaching again, I would be recommending custom preludes with simpler types.
I'm writing this on a recumbent bike. I've averaged ninety minutes a day for four years without injury. I've also read a ton of books on my kindle.
For some reason, I've typed it out in a few files, such as my Emacs init and .Xresources.
I can even get behind runes, and I set up abbreviations in Emacs to learn their names. And generally, I like the philosophy of language where short, one syllable, old/middle English words are used in place of the less familiar Latin/Greek derived words. I don't have a problem with revising our existing vocabulary in this way.
But the attitude to type theory strikes me as anti intellectual, precisely because we get nonsense claims about Hindley Milner requiring category theory. This sort of misinformation does no good for anyone.
I compare the situation to the 90s games. Machines did hardware scrolling and were optimised for blitting sprite sets. And so, lo and behold, most games are scrolling shooters and beat em ups. I always felt gpus have put similar constraints on our imaginations, hence most games are mostly static scenes.
When doing theorem proving in Ocaml (which was both ML's and Lisp's MOs), my REPL session could last months. I sadly didn't have Lisp's great (save-lisp-and-die) in Ocaml, but I was able to snapshot the process state to get semiway there.
Common Lisp is built around the idea that your runtime is a persistent live system that you stay in and modify and introspect. Your tool-chain should consist entirely of Lisp objects that are part of your runtime, and should themselves be modifiable. Everything is programmable. The Art of the Metaobject Protocol is still one of the coolest things I've ever read.
I'm a Haskeller now, through and through, but Haskell's REPL and runtime isn't comparable to Lisp's.
So far, Incandescence is the toughest of his novels that I've read, and the main world is an extreme and beautiful conception where the ambient forces are all peculiar and the inhabitants are figuring out their cosmology as the novel goes on.
That said, I really could have used more diagrams while I was reading. I only found out after finishing that Egan has a whole bunch of Java applets on his website explaining the experiments that the main characters in the novel carry out, and why they give the results they do.
I have tried a structure editor for Haskell but I found it pretty counterintuitive. I wonder if structure editing just assumes a language with very little syntax.
The Nix language is cute, very powerful, and as you say, is capable of cool tricks. I personally find it very easy to read too.
For every project I work on, I define a .nix file describing its build environment. It is then just a matter of cloning the repo, typing nix-shell, and then all build dependencies will be downloaded and I'm ready to go. These nix files also specify which emacs I want to use and with which modes: I don't use Emacs' package manager.
I use nix-shell for pretty much everything. My basic environment contains only core utilities. I launch firefox with "nix-shell -p firefox --run firefox" (bound to a shortcut defined in my xmonad config).
Nix is easy to customize. The override infrastructure makes it easy to modify package definitions from your user config file, and I find I am much happier doing this in a programming language rather than a bespoke config file format.
Other pros: you don't need to be root to install stuff. And there isn't much more satisfying than getting a new laptop, git cloning the repo containing your intended system configuration, and being back up with your old OS with one or two commands.
As you say, if Arch turns you off, Nix may horrify. Despite the vote, I'm pretty discriminating about who I recommend it to.
Goedel's second incompleteness theorem says you can't do that. Ever. And more generally, it says that you will hit a roadblock as you try to prove what it is that your proof assistant does. This is really annoying.
But you can get pretty damn close. HOL Light has something of a self-consistency proof that is so close to the real thing that it's worth taking seriously.
But incompleteness, by itself, isn't a big deal for theorem provers. Russell himself worked as if the existence of infinities wasn't provable, which is why Goedel had little relevance to Russell. Hilbert on the other hand....
If a lookup fails, it is often a bug or a violation of a constraint that you shouldn't be handling. Throw an exception. If a lookup is expected to fail in normal execution, you can use an Optional to mash together a check and lookup in a typesafe way. Lookup failures should not be allowed to propagate as nulls only to be caught as NullPointerExceptions in disparate parts of the code.
The OP mentions "the ability to do controlled per-user changes in an ad-hoc fashion". I assume they are referring to the use of "nix-env", which some members of the community frown upon. I've adopted the policy of not using it. My system is defined entirely by various ".nix" files, and for launching simple applications, such as firefox, I just have my xmonad configuration bind "C-SHIFT-F" to "nix-shell -p firefox --run firefox".
For programs where I have more complicated environments, I have a "shell.nix" file in $HOME which contains a bunch of derivations that I commonly use, and I use "nix-shell -A" to enter them.
For development, all of my projects now include a "default.nix" file. Typing "nix-shell" in the project directory then brings in my project's dependencies as well as the tools I want when developing on that project, such as a suitable Emacs with the appropriate packages already installed.
I find I move these move ".nix" files frequently between different machines, and I really appreciate that I am put into reproducible development environments wherever they are.
To reiterate what others have said below: the Nix package manager makes sure to share build inputs. If I start a new project and include a bunch of stuff in my "default.nix" that I use elsewhere, it costs nothing to enter the new environment.
Many of our crossings do nothing if you don't press the button. And if the light has been green for road traffic for a while, pressing the button /immediately/ sets the traffic sequence towards red. I often let the odd car pass so the traffic looks clear before pressing, rather than force a poor driver to a stop.
I am happy to see these ideas work their way into any language. I just question the utility here. So there are potentially abstract things going on in my code? Great. So what? I can maybe notice it and get back to work.
Haskell gives me more. It says I can define some of the abstractions and write code against them. More realistically, it allows someone smarter than me to define the abstractions which I can then code against. For better or worse, this is expected now of even beginner Haskell programmers, where "monad" rears its head as an inbuilt type class, in inbuilt functions and some of the earliest type errors.
In Haskell, a monad isn't just an abstract concept that you can see if you squint hard enough at your code. A monad is code. It has an implementation as a typeclass, in Ocaml as a module type. How is it implemented in JS?
I am also sceptical that these abstractions are useful outside statically typed languages. Haskellers habitually ride towers of these constructs and I for one couldn't do it without a type inferencer. I already find scalaz too painful for me personally.
The upshot is that to teach OOP in Java, say, you need to talk about significant parts of the inheritance machinery just to get dynamic dispatch, perhaps later cautioning against implementation inheritance and even interface inheritance.
In Smalltalk, you can talk dynamic dispatch without messing around with inheritance at all.
For a statically typed language where dynamic dispatch is free from inheritance graphs (sometimes described by saying that subtyping is not inheritance) see Ocaml's structural subtyping via row polymorphic records (bit of a mouthful --- but I need to differentiate from Ocaml's module system which is structurally subtyped and supports inheritance!)
A standard use-case of dynamic dispatch is for representing different types of term in a parse tree, where we want to dispatch on nodes according to type. This sort of dispatch cannot be monomorphised, since the the exact callee depends on the shape of the trees being traversed and this almost certainly cannot be figured out statically.
In Haskell, you do not do this sort of dispatch with typeclasses, but instead use ADTs.
When it comes to the expression problem, the tradeoff is that ADTs allow you to easily write new sorts of traversal but do not allow you to extend the tree type in a modular way. By contrast, single dispatch OO allows you to easily extend the tree type by providing new classes to implement the traversal methods, at the cost that you cannot easily define new traversals.
The idea is at least as old as SLIME, which has been the defacto common lisp mode for Emacs for ages. When I was using it nearly fifteen years back it was easily the most comfortable development environment I had used in any language.
For me, babel is the killer feature. I can write code snippets in the buffer, highlighted and indented according to the corresponding emacs mode, with paredit for lisp, and beautifully exported via pygments. A single key combo lets me evaluate my code in persistent repls associated with my buffer, and I can choose what combo of code/result I want to export. I had some of my snippets generate raw LaTeX that I could include in the document.
Org links make section references trivial, and I have it hooked up to ebib so my citations are pure org. In the emacs buffer, clicking a citation link takes me straight to the bibtex entry.
I don't much use the organiser, but I do use org heavily for writing cross referenced notes about other people's code.
https://github.com/Chattered/ocaml-monad
and use it regularly when writing Ocaml. I'd like to get back to it and try to implement some MTL style interfaces (MonadState etc...). Edward Kmett says this is not likely to work, but that sounded like a wager to me.