Live data from Hacker News

Why ML/OCaml are good for writing compilers (1998)

flint.cs.yale.edu

91–100 of 151 posts

Re: Why ML/OCaml are good for writing compilers (1998)

#91

I'll note that some of the aspects don't necessarily work out like that in practice: 1. The GC part is true, but one has to remember that this was written at a time when GC was still a bit of an unusual feature in mainstream languages. 2. Tail recursion doesn't really make much of a difference for walking trees, which is recursive, but (mostly) not tail recursive. 3. OCaml in particular uses 63/31-bit ints due to imp…

> 2. Tail recursion doesn't really make much of a difference for walking trees, which is recursive, but (mostly) not tail recursive.

Unless you, as the article notes, "know how to take advantage of it". Here's a fully tail-recursive binary tree traversal in OCaml:

    type 'a tree = Leaf of 'a | Branch of 'a tree * 'a tree

    let iter f tree =
      let rec iter_rec f worklist tree =
        match tree with
        | Leaf a ->
          (* Perform the action on this element. *)
          f a;
          (* Consult the worklist for more things to do. *)
          begin match worklist with
          | [] -> ()
          | next_tree::worklist' -> iter_rec f worklist' next_tree
          end
        | Branch (left, right) ->
          (* Visit the left subtree, save the right for visiting later. *)
          iter_rec f (right::worklist) left
      in
      iter_rec f [] tree
Usage example:

    let mytree =
      Branch (Branch (Leaf 1, Leaf 2),
              Branch (Leaf 3, Branch (Leaf 4, Leaf 5)))

    let () = iter (Printf.printf "%d\n") mytree
Yes, people do write traversals like this in OCaml, though with less verbosity than this example I whipped up.

> 3. OCaml in particular uses 63/31-bit ints due to implementation details, which isn't a good fit for 64/32-bit integers.

I think the article means here that you just use int for all the kinds of numerical identifiers that compilers give to things like instructions, basic blocks, pseudo-registers, etc., without doing the kind of micro-optimization that C++ programmers would do, guessing whether the number of blocks is safe to store in an unsigned short etc.

For representing constants from the program, which is what you seem to be referring to, the article does suggest using bignums, not OCaml's native ints.

Re: Why ML/OCaml are good for writing compilers (1998)

#92

Earlier quoted context omitted.

I'm optimistic about Reason, Facebook's new syntax "skin" on top of OCaml. I find OCaml's syntax to be quite gnarly; of the MLs, F# is probably the cleanest and most modern-feeling. Something like F# without the .NET stuff could have been amazing.

I find OCaml's syntax simple and clear. I don't get the reason for Reason, but hope it leads to more OCaml adoption.

OCaml might be simple and clear, but starting from Standard ML - there's a number of differences that feel like warts and needless complications for no discernable gain for the programmer.

I do think Reason fix up a few of these ancient and partially crumbled stone walls making the ocaml landscape easier to criss-cross for a new generation of programmers.

Re: Why ML/OCaml are good for writing compilers (1998)

#93
post #90

Earlier quoted context omitted.

I started to write a toy compiler in OCaml. I had some previous experience with Haskell, but in no way an expert. I.e. no category theory background, only shallow exposure to monads. My "problems" with OCaml started, when I wanted to "map" over a data structure I defined. I ended up having to define custom mapping functions for all container-like data structures I wrote and call them in a non-polymorphic fashion (whe…

I assume you were just not interested in passing the state around to the functions that needed it, and preferred the fact that the state monad hides that plumbing for you via bind and return. It's worth noting that there exist Ocaml libraries that provide the same operators and even similar do notation syntax that desugars to bind/return operators (via PPX). Ocaml does tend to be more verbose than Haskell - it's just…

Thanks for the thorough reply and it sounds like you're quite experienced here. Any chance going into more detail with what you do for a living? Do you maintain a compiler for something more mainstream?

Re: Why ML/OCaml are good for writing compilers (1998)

#94

I'll note that some of the aspects don't necessarily work out like that in practice: 1. The GC part is true, but one has to remember that this was written at a time when GC was still a bit of an unusual feature in mainstream languages. 2. Tail recursion doesn't really make much of a difference for walking trees, which is recursive, but (mostly) not tail recursive. 3. OCaml in particular uses 63/31-bit ints due to imp…

>2. Tail recursion doesn't really make much of a difference for walking trees, which is recursive, but (mostly) not tail recursive. Non-strictness helps here more than TCO in a strict language. >4. ADTs can be good or bad for describing ASTs. Once you enrich ASTs with semantics shared by all variants (such as source coordinates), inheritance can become a better fit than ADTs. Since this article was written we have be…

> Non-strictness helps here more than TCO in a strict language.

Can you explain? Assume I want to fold a function over a large tree and fully inspect the final result. (For example, to compile a large expression to a piece of code.) If I use non-tail recursion, my stack will be exhausted. How does non-strictness help with stack usage?

Re: Why ML/OCaml are good for writing compilers (1998)

#95
post #90

Earlier quoted context omitted.

I started to write a toy compiler in OCaml. I had some previous experience with Haskell, but in no way an expert. I.e. no category theory background, only shallow exposure to monads. My "problems" with OCaml started, when I wanted to "map" over a data structure I defined. I ended up having to define custom mapping functions for all container-like data structures I wrote and call them in a non-polymorphic fashion (whe…

I assume you were just not interested in passing the state around to the functions that needed it, and preferred the fact that the state monad hides that plumbing for you via bind and return. It's worth noting that there exist Ocaml libraries that provide the same operators and even similar do notation syntax that desugars to bind/return operators (via PPX). Ocaml does tend to be more verbose than Haskell - it's just…

I've done a lot of production Haskell and I've had a similar experience.

In our case, we dealt with it by keeping relatively bare, boring code. We avoided point-free style, crazy combinators like lenses, and complex monad transformer stacks except in the 'plumbing' part of the application that didn't need to change very much.

This paid off in spades as we had a lot of engineers who only ever had to work in the 'porcelain' parts of the application. They got a lot of great work done using abstractions that matched their intuition exactly.

Re: Why ML/OCaml are good for writing compilers (1998)

#96

Earlier quoted context omitted.

Haskell may indeed be one of the most advanced languages out there in terms of raw power, but it is very complex (how many monad tutorials does it seriously take to teach one of the most core pieces of the language) and how much category theory do you need to know to be moderately effective? Also, the ecosystem could use some work. An example is the main string library isn't used in favor of a different one. Using th…

I started to write a toy compiler in OCaml. I had some previous experience with Haskell, but in no way an expert. I.e. no category theory background, only shallow exposure to monads. My "problems" with OCaml started, when I wanted to "map" over a data structure I defined. I ended up having to define custom mapping functions for all container-like data structures I wrote and call them in a non-polymorphic fashion (whe…

Just want to note that, while not frequently seen, you can use more powerful abstractions (monad transformers, parser combinators, etc) in OCaml.

As an example consider looking at the Angstrom[1] parser combinator library and my Pure[2] functional base library.

[1] https://github.com/inhabitedtype/angstrom

[2] https://github.com/rizo/pure

Re: Why ML/OCaml are good for writing compilers (1998)

#97

I'll note that some of the aspects don't necessarily work out like that in practice: 1. The GC part is true, but one has to remember that this was written at a time when GC was still a bit of an unusual feature in mainstream languages. 2. Tail recursion doesn't really make much of a difference for walking trees, which is recursive, but (mostly) not tail recursive. 3. OCaml in particular uses 63/31-bit ints due to imp…

> 2. Tail recursion doesn't really make much of a difference for walking trees, which is recursive, but (mostly) not tail recursive. Unless you, as the article notes, "know how to take advantage of it". Here's a fully tail-recursive binary tree traversal in OCaml: type 'a tree = Leaf of 'a | Branch of 'a tree * 'a tree let iter f tree = let rec iter_rec f worklist tree = match tree with | Leaf a -> (* Perform the act…

> Unless you, as the article notes, "know how to take advantage of it". Here's a fully tail-recursive binary tree traversal in OCaml:

This is a depth-first search with an explicit stack (the stack is tree :: worklist). You can do the same in an imperative language. Tail recursion here is only an extra-complicated way of writing a simple loop, and you're adding extra complexity by having two variables to represent the stack. The same code can be written just as (if not more) compactly in an imperative language.

Re: Why ML/OCaml are good for writing compilers (1998)

#98
post #76

I'll note that some of the aspects don't necessarily work out like that in practice: 1. The GC part is true, but one has to remember that this was written at a time when GC was still a bit of an unusual feature in mainstream languages. 2. Tail recursion doesn't really make much of a difference for walking trees, which is recursive, but (mostly) not tail recursive. 3. OCaml in particular uses 63/31-bit ints due to imp…

ADTs and pattern matching are much more convenient and higher-level in practice than using OOP with inheritance. The visitor pattern, essentially just a fold, is the best one can do in an OOP language. With type-class abstractions and data type generic programming, the gap widens further. In Haskell, my current FP language of choice, I can implement a complex transform such as Lambda lifting in a few 10's of lines of…

First, inheritance provides a strict superset of standard ADT functionality. Proof: Scala does ADTs through inheritance. ADTs are basically isomorphic to a closed two-tiered inheritance hierarchy with an abstract superclass at the top tier.

Second, you're confusing inheritance with the ability to map subtypes to operations (and in statically typed languages, in a type-safe fashion). This is a function of OCaml's (or SML's, or Haskell's, or F#'s) pattern matching facilities, not of inheritance vs. ADTs. It can also be done with typecase statements, multi-methods (or actually, just external methods), or tree parsers. The tree parser approach in particular is more general and powerful than the typical pattern matchers in functional languages.

Third, if you look at actual compilers, such traversal will commonly be done in an ad-hoc fashion and can be done equally well with bog-standard methods. Where you have generalized traversal mechanisms, the visitor pattern will crop up in OCaml, too (in some guise or another). Examples are the Ast_mapper module for PPX in OCaml itself [1] and the visitor interface in CIL [2]. The reason is that if you want to perform a generalized fold, map, etc. operation over a heterogeneous data structure such as an AST (visitor is usually fold + map due to destructive updates), you need to also provide a set of operations for the various types that you can encounter during traversal.

[1] https://caml.inria.fr/pub/docs/manual-ocaml/libref/Ast_mappe...

[2] https://people.eecs.berkeley.edu/~necula/cil/api/Cil.cilVisi...

Re: Why ML/OCaml are good for writing compilers (1998)

#99

For web developers who are looking for an industrial strength functional language instead of JS, OCaml probably has the best story here. Actually it has two OCaml->JS compilers of very high quality The first one, js_of_ocaml, could bootstrap the whole compiler several years ago(probably the first one there). The recent one, https://github.com/bloomberg/bucklescript , push the JS compilation into next level, it genera…

I'm optimistic about Reason, Facebook's new syntax "skin" on top of OCaml. I find OCaml's syntax to be quite gnarly; of the MLs, F# is probably the cleanest and most modern-feeling. Something like F# without the .NET stuff could have been amazing.

>I find OCaml's syntax to be quite gnarly

What's wrong with the OCaml syntax? It's much more clean than say scala's one, it's indentation insensitive, and a' list feels more relevant than the list

Re: Why ML/OCaml are good for writing compilers (1998)

#100

Earlier quoted context omitted.

> 2. Tail recursion doesn't really make much of a difference for walking trees, which is recursive, but (mostly) not tail recursive. Unless you, as the article notes, "know how to take advantage of it". Here's a fully tail-recursive binary tree traversal in OCaml: type 'a tree = Leaf of 'a | Branch of 'a tree * 'a tree let iter f tree = let rec iter_rec f worklist tree = match tree with | Leaf a -> (* Perform the act…

> Unless you, as the article notes, "know how to take advantage of it". Here's a fully tail-recursive binary tree traversal in OCaml: This is a depth-first search with an explicit stack (the stack is tree :: worklist). You can do the same in an imperative language. Tail recursion here is only an extra-complicated way of writing a simple loop, and you're adding extra complexity by having two variables to represent the…

You are correct on all counts. Tail recursion allows you to reap the benefits of imperative programming in a purely functional setting, but only thanks to tail call optimization.
Post reply on HN