Live data from Hacker News

How to Implement a Programming Language in JavaScript

lisperator.net

31–36 of 36 posts

Re: How to Implement a Programming Language in JavaScript

#31
post #7

I've written multiple parsers/interpreters in both JS and Python -- Python being my favorite language. From that experience, I've come around to the fact that they're both the wrong language for writing languages -- lexers, parsers, interpreter loops, compilers. I have a good analogy to explain this. Take this OCaml program. let sum_file filename = let file = In_channel.create filename in let numbers = List.map ~f:In…

While the result is probably correct, your argument is, odd. :)

Hammers in nails with an orange, and this folks, is how Black and Decker makes better drills than Makita.

The single reason that OCaml excels at compilers is due to pattern matching. It _removes_ all those explicit conditionals by making them implicit.

http://andrej.com/plzoo/

Python has excellent affordances for writing compilers, they just have to be used.

https://github.com/mrocklin/multipledispatch/

https://github.com/Suor/patterns

Re: How to Implement a Programming Language in JavaScript

#32
post #31
post #7

I've written multiple parsers/interpreters in both JS and Python -- Python being my favorite language. From that experience, I've come around to the fact that they're both the wrong language for writing languages -- lexers, parsers, interpreter loops, compilers. I have a good analogy to explain this. Take this OCaml program. let sum_file filename = let file = In_channel.create filename in let numbers = List.map ~f:In…

While the result is probably correct, your argument is, odd. :) Hammers in nails with an orange, and this folks, is how Black and Decker makes better drills than Makita. The single reason that OCaml excels at compilers is due to pattern matching. It _removes_ all those explicit conditionals by making them implicit. http://andrej.com/plzoo/ Python has excellent affordances for writing compilers, they just have to be u…

It's not just pattern matching -- it's also ADTs and the functional paradigm. (e.g. in Python, functools.partial is uglier than OCaml's application, just as working with dictionaries is uglier in OCaml than Python)

Do those Python libraries offer type safety? I don't think there is any way they can. If not, you might as well use OCaml.

As I said, Python is my favorite language, but there is no reason to write OCaml in Python or even shell scripts in Python. Why not use the real thing? The Python code still won't be shorter than OCaml, even if you use these fancy libraries.

Re: How to Implement a Programming Language in JavaScript

#33
post #15

Earlier quoted context omitted.

Right, I was referring to all ML-based languages -- so SML, OCaml, Haskell, F#, and possibly even Rust. Do you know how this would look in Haskell? # Return K most common lines in a file def top_k(f, k): counts = collections.defaultdict(int) for line in f: counts[line] += 1 return sorted(counts.items(), key=lambda x: x[1], reverse=True))[:k] I was actually looking for the OCaml example which does this. I think it was…

> Do you know how this would look in Haskell? I'm curious, how did you think it would look in haskell? Can you give a code example of what was in your head?

I don't know Haskell, so there wasn't anything in my head. I basically know the ML subset of OCaml and I guess Haskell, and that doesn't really apply at all to the top K problem.

I was saying that OCaml was awkward for this problem, and the reply was that OCaml is but Haskell isn't. I'm not convinced after seeing the Haskell code. Python is both semantically and syntactically a better fit for that problem.

Re: How to Implement a Programming Language in JavaScript

#34
post #33

Earlier quoted context omitted.

> Do you know how this would look in Haskell? I'm curious, how did you think it would look in haskell? Can you give a code example of what was in your head?

I don't know Haskell, so there wasn't anything in my head. I basically know the ML subset of OCaml and I guess Haskell, and that doesn't really apply at all to the top K problem. I was saying that OCaml was awkward for this problem, and the reply was that OCaml is but Haskell isn't. I'm not convinced after seeing the Haskell code. Python is both semantically and syntactically a better fit for that problem.

> I don't know Haskell, so there wasn't anything in my head.

Then what were you impling by saying:

> Do you know how this would look in Haskell?

Because that sentence seems to imply it would be very convoluted in Haskell. How can you imply it would look convoluted in Haskell if as you said:

> I don't know Haskell, so there wasn't anything in my head.

Anyway, onwards...

> I'm not convinced after seeing the Haskell code. Python is both semantically and syntactically a better fit for that problem.

You aren't convinced of what after seeing the Haskell code?

How can you come to the conclusion that Python is both semantically and syntactically a better fit for that problem if you don't know both Python and Haskell?

Can you give me the criteria you are using to come to that conclusion?

Re: How to Implement a Programming Language in JavaScript

#35
post #15

Earlier quoted context omitted.

I 100% agree that JS and Python are the wrong language for writing languages. Haskell, however gets you the best of both worlds. Your sum_file function in Haskell would look like this: sumFile :: FilePath -> IO Int sumFile file = sum . map read . lines readFile file I don't think many people would dispute that Haskell is at least as good as OCaml for writing languages. And strangely enough, the most advanced Perl 6 i…

Right, I was referring to all ML-based languages -- so SML, OCaml, Haskell, F#, and possibly even Rust. Do you know how this would look in Haskell? # Return K most common lines in a file def top_k(f, k): counts = collections.defaultdict(int) for line in f: counts[line] += 1 return sorted(counts.items(), key=lambda x: x[1], reverse=True))[:k] I was actually looking for the OCaml example which does this. I think it was…

Sorry, but I can't agree with this.

Here is a (more or less) identical implementation in Rust:

    fn top_k(f: Lines, k: usize) -> Vec {
        let mut counts = HashMap::new();
        for line in f {
            *counts.entry(line).get().unwrap_or_else( |v| v.insert(0) ) += 1;
        }
    
        let mut counts = Vec::from_iter(counts);
        counts.sort_by( |x, y| y.1.cmp(&x.1) );
        counts.truncate(k);
        counts
    }
These are the things that make it more verbose than the Python that are not related to ML:

* Curly brace syntax. Not a property of ML languages--Rust and Swift are pretty much alone in the ML family in having curly brace syntax.

* Having to write & explicitly. Not a property of ML languages at all--it's a C++ism. In the type signature, the & is because Rust has first-class references--it deliberately does not abstract over them, unlike nearly every other ML (and most other languages, for that matter). The second case is a stylistic choice: Rust is (somewhat inconsistently) explicit about when you are taking a reference. There have been some proposals to remove this annotation burden.

* Rust doesn't have a find_or_insert method in the standard library (or a "HashMap with Default" a la defaultdict). Not a property of ML languages; in fact, Rust used to have this (see http://web.mit.edu/rust-lang_v0.11/doc/std/collections/hashm...). It was removed because the Entry API is seen as more general. It would be quite easy to add such methods or types back as sugar in a library (if someone hasn't already).

* By convention, Rust APIs that mutate things don't return the original. Not a property of ML languages, just a Rust thing (in Rust, you can get sugar for this a chain! macro, as demonstrated at http://www.reddit.com/r/rust/comments/2wssch/function_chaini...).

* Having to write out ".take(k).truncate" instead of just being able to use slice syntax like [:k] (in Rust, [..k]). This is not an ML thing; the reason you can't do this is because slices in Rust do not create copies, but references into the original vector. If Rust were garbage collected, this would be fine, but in Rust the counts vector would be deallocated at the end of the function, leaving the references dangling. We could clone it, but it wouldn't be any shorter and it would be wasteful compared to just calling truncate.

* Having to specify that the counts be represented as a `Vec` to turn the counts item list into a vector. This is not an ML thing; it is because in Rust, vectors are not privileged over other types of containers. Most MLs have plenty of syntactic sugar for lists; this is again something more C++ish.

* Using '::' instead of '.' as the path separator. Most MLs just use ., this is a C++ism. I believe it is related to Rust's desire to remain LL(1), but I can't remember the particular reason for this one.

* A big one that might seem like an MLism, but isn't: explicitly writing out the types. While MLs vary in how much type inference they actually support, most will infer them. Rust is somewhat unusual in requiring function-level type annotations. In particular, a language with global inference should be able to infer this signature for the function (given in Rust syntax):

    fn top_k(f: I, k: usize) -> Vec where I::Item: Eq + Hash
As far as I can tell, these are the things that make it verbose than the Python that are related to ML:

* Having to write "let" and "let mut". I don't think there is any ML language that doesn't differentiate between variable creation and variable assignment. Similarly, I can't think of a ML-family language that doesn't differentiate between mutable and immutable bindings (however, there was a quite serious proposal by one of the core Rust developers to do this: http://smallcultfollowing.com/babysteps/blog/2014/05/13/focu..., and it could be made less boiler-platey for the common case, as Swift does, by having a separate var keyword, at the cost of losing power while pattern matching).

Thus, here is a hypothetical implementation in an MLish language that switches the non ML-ish Rust-isms to quite reasonable ML-ish alternatives (and has a garbage collector, presumably, but if you don't like that just pretend slice is a copying operation in this language):

    fn top_k(f, k):
        var counts = HashMap.new()
        for line in f:
            counts.find_or_insert(0) += 1

        from_iter(counts).sort_by( |x, y| y.1.cmp(x.1) )[..k]
I guess you could argue that all I've done here is cherry-picked the most Python-like features from all the ML languages (not the shortest, obviously, as the Haskell example demonstrates :)), but my point is that most of the syntactic concerns people associate with ML have little to do with ML itself, but unrelated decisions of the languages in question. Personally, I would love to use a language like the above for similar tasks to what I use Python for, and maybe someday someone will create one :) (perhaps someone already has!)

Re: How to Implement a Programming Language in JavaScript

#36
post #15

Earlier quoted context omitted.

Right, I was referring to all ML-based languages -- so SML, OCaml, Haskell, F#, and possibly even Rust. Do you know how this would look in Haskell? # Return K most common lines in a file def top_k(f, k): counts = collections.defaultdict(int) for line in f: counts[line] += 1 return sorted(counts.items(), key=lambda x: x[1], reverse=True))[:k] I was actually looking for the OCaml example which does this. I think it was…

Sorry, but I can't agree with this. Here is a (more or less) identical implementation in Rust: fn top_k(f: Lines, k: usize) -> Vec { let mut counts = HashMap::new(); for line in f { *counts.entry(line).get().unwrap_or_else( |v| v.insert(0) ) += 1; } let mut counts = Vec::from_iter(counts); counts.sort_by( |x, y| y.1.cmp(&x.1) ); counts.truncate(k); counts } These are the things that make it more verbose than the Pyth…

Sorry, line four of the hypothetical function should be counts.find_or_insert(line, 0) += 1. Would have edited, but HN won't let you edit after the procrastination settings kick in (but curiously, does let you reply).
Post reply on HN