Tagged with programming languages

Dreaming of a New PL

I’ve been wanting to create a new programming language for about the last eight years or so.

Problem is, I haven’t had much clue what I would want a new programming language to look like, and even less of a clue how to actually create one. I’ve written some very simple compilers and interpreters before, so I know bits and pieces about parsing and code generation, but that’s a far cry from knowledge of programming language design.

I’d really like to change this, but I need help. There’s no way I can do this alone. So I figure I’ll write up some ideas I’ve had recently, and see if anybody who actually knows something about building languages wants to help.

I think the best way to explain what I’m thinking about right now is by comparison with existing programming languages. Specifically, there are things I like and dislike about Haskell, and things I like and dislike about Scala. So I’ll just describe those things as best I can, and see if that ends up painting a clear enough picture of what I’d hope to see in a new language.

Haskell Likes

  • Principal types. I’ve written about this before: I think explicit type annotations should be a matter of style—it’s a good idea to annotate types for an API rather than allow them to be inferred—rather than a matter of necessity. Haskell does admit some language extensions that defeat this, such as rank-N types, but for the most part it’s possible to infer a “most appropriate” type for any given expression.
  • Simple, minimal syntax. Seriously, the syntax of Haskell is not much more complex than that of the lambda calculus, and I like the use of layout to delineate blocks (see Okasaki’s post, “In praise of mandatory indentation for novice programmers”).
  • Currying and partial application for both functions and type constructors. I feel like this might be somewhat underappreciated, so maybe worth mentioning: just as it’s helpful to partially apply a function as in map (+1) [1..10], where (+) is a function of two parameters, it’s also helpful to describe a type as in instance Monad (State s), where State is a type constructor of two type parameters.
  • Pure functions. (Need I say more?)

Haskell Dislikes

  • I don’t like laziness by default. I do appreciate the elegance of composition in writing a function and only demanding a portion of its result (see Edward Kmett’s comments on the recent “Putting Haskell Down” thread), but I feel like it’s sort of a deal-breaker to require the routine use of profiling to hunt down space leaks and so on, when GHC’s strictness analysis doesn’t behave how you’d expect. I’d be very happy for somebody to change my mind about this, but so far I think optional laziness is better.
  • Monads are clunky. There, I said it. They are far from the panacea of “programmable semicolons” we’ve made them out to be. And since monads don’t compose, monad transformers are clunkier still. The syntactic noise of all the various types of lifting we do (>>=, <$> / <*>, etc.) is unsatisfying, and in some ways monads make debugging more difficult as well.
  • Related to the above, all of our “real-world” side effects in Haskell are relegated to the IO monad. This conflates the effects of reading from a mutating environment with the effects of writing to the environment, and lumps different portions of that environment (disk, network, memory, etc.) all into the same bucket. We need a better effects system. I’m very interested in Conor McBride’s work on “Frank” (described informally in comments on the same “Putting Haskell Down” thread from above), Daan Leijen’s work on “Koka” and others.
  • One could easily argue that this is a good thing, but I find it frustrating that Haskell doesn’t allow cycles in a module dependency graph. Sometimes I want mutually dependent modules, even if only to break a large file into two pieces.
  • Records are terribad, and I think we essentially need function overloading for them to work. For example, if I define a record data Foo { name :: String }, then I can’t also define data Bar { name :: String } in the same module: the two name fields collide.
  • Existential types are ugly. If I want a heterogeneous list of things that have Eq and Show type class instances, I need to put them in some sort of a “box” to homogenize them: data Box = Box (forall a. (Eq a, Show a) => a). Then I need to make Box an instance of Eq and Show, and finally I can create a list of boxes. This is straightforward in a language with subtype polymorphism, but I’m by no means saying that subtype polymorphism is what I want…
  • Scrap-your-boilerplate is bananas, and on a somewhat related note, there needs to be a better general mechanism for automatically deriving type class instances.

Scala Likes

  • Pattern matching is a big deal in functional languages, particularly when you have algebraic data types and whatnot. I really like Scala’s unapply / unapplySeq machinery for customizing pattern matching (and find it somewhat astonishing that Haskell has no equivalent).
  • I think in some sense, Scala treats type classes as more of a first-class citizen than Haskell does: they are named values that can be imported explicitly and overridden through scoping rules.
  • I like Scala’s placeholder syntax for partial function application better than Haskell’s. My Haskell example from above, map (+1) [1..10], is written in Scala as 1 to 10 map { _ + 1 }. To my eye, “something plus one” reads more intuitively than just “plus one.” This becomes a bigger deal when you have a function of multiple parameters, say three or more, and you want to partially apply some arbitrary subset of those arguments not necessarily in order. Note however that Scala currently has no such equivalent for partial type constructor application, so we resort to the horrible “type lambda trick” instead.
  • Strict evaluation by default, with optional laziness.
  • Scala targets the JVM. This is also something of a “dislike,” perhaps, since the JVM has its shortcomings with respect to functional languages—lack of tail calls most notably. But I feel like the JVM is indisputably solid: it’s fast, it’s stable, it’s mature, and it has a massive ecosystem of existing tools and open source libraries to work with.

Scala Dislikes

I hate to sound snarky, but this list is too big to put in writing. With all respect due to Odersky for his research on object calculi, I just don’t think I find objects particularly useful. I think Shields and Peyton-Jones’s “First-Class Modules for Haskell” paper is trying to change my mind (particularly since Haskell’s record system is so weak), but I don’t think I’m convinced yet. I also think Scala’s syntactic rules are generally ridiculous (I’ve bitched about this before). Pretty much all my other complaints are subsumed by these two in some way.

I really don’t want to sound like a Scala hater; hopefully my lists of Haskell dislikes and Scala likes make any complaints I have about Scala apolitical at least…

Conclusion

Reviewing my likes and dislikes here, I probably have some conflicting—or at least challenging—desires. It sounds like I want a minimalistic, strict functional language on the JVM with a novel system of effects, good type inference, easy-to-use bounded existentials and records, and flexible type classes and pattern matching. Oh, and type-safe reflection too.

So who wants to help?

Tagged