1. 29

  2. 8

    This is an amazingly astute analysis.

    Posted by Neelakantan Krishnaswami

    Ah, that explains it! I’d forgotten Semantic Domain was Neil’s blog until I saw the byline.

    1. 1

      Glad you said something because skimming the post titles brought me to a few with submission potential. Ill look at them when off work. Author often partners with another favorite PLT researcher of mine whose on DeepSpec team.

    2. 5

      This post is great! This has helped me clarify my thinking about a few things:

      You just can’t do without being able to break your program into pieces.

      I’ve found that “pure” code wants to be broken up in to smaller and smaller pieces. This often comes at a performance cost for both call overhead and duplicate work, but that performance is often easy to recover with bulk pure operations, at some risk of repeating yourself.

      However, imperative code is often dramatically clearer as big linear blocks of code. More and more, I’m just inlining procedural functions at their call sites with few if any ill effects, and usually benefits. Similarly, instead of extracting procedures, I’m now frequently creating intermediate maps or parallel arrays, and then looping over them.

      The one big reason I still break up imperative pieces is for open-dispatch. However, even that can usually be separated along functional and imperative lines: the functional code operates on open union types, but return values of a closed sum type to communicate desires to a central imperative procedure.

      Functional code wants to be composed. Imperative code wants to be parameterized.

      runST operator lets you create computations that use highly aliasable state

      This is why I’m a monad detractor. Once you get in to a sufficiently complex monad, your right back where you started with imperative programming. Only now, you have the added complexity that everything is effectively “quoted” by default, so you can accidentally “metaprogram” your way in to a mess, re-ordering sub-procedures and the like. The type system won’t tell you that should have bound an intermediate result to a variable, rather than splice in a chain of first-class statements.

      I don’t doubt that reasoning about imperative programs in Haskell is easier than in say Ruby, but I think that’s more related to the amount of aliased state by default, more than anything else.

      Removing any one of the three difficulties makes things easier […snip…] a) State + procedure calls b) State + aliasing c) Aliasing + procedures

      I’m designing a language that embraces immutability, but has statements and imperative constructs. Some core design elements aim directly at spending as much time in these two-out-of-three-difficulties sweet-spots as much as possible.

      The main thing that values are deeply immutable and acyclic. They are strictly separated from variables (of various kinds) that are mutable containers of such values. Cycles must be achieved via indirecting through a symbol or ID. Stateful objects may employ internal mutability for performance, but then must perform copy-in/copy-out to preserve the value illusion.

      A) A reduction in aliasing of mutable state across procedure calls is achieved in two ways: 1) defaulting stateful constructs to be second-class. You need to use explicit syntactic constructs to reify them as objects, like address-of (&) in C or Go; quite unlike Java or Ruby, etc. 2) state has structured lifetime. First-class procedures can close over state, but they will throw if the procedure is executed outside the dynamic extent of that state. If you want to share state more widely, you need to allocate it explicitly from higher up in the process tree.

      B/C) Procedures interact with stateful aliasing less frequently with deeply immutable values and explicit mutable aliasing. While call-by-value is standard now, it’s less useful when you have pervasive aliasing in order to pass large values or values of dynamic size or type. In Go for example, you get much more milage from call-by-value behavior than Java, but still frequently create pointers in order to escape the overhead of excessive copying. Not a problem with deeply immutable values.

      1. 3

        For the curious, here is a PostScript file on specification logic with Idealized Algol, a PDF introducing separation logic, and a PDF describing and extending the Syntactic Control paper.