HNHacker News
TopNewBestAskShowJobs

jpolitz

452 karma · joined July 29, 2012

https://jpolitz.github.io
submissionscomments
jpolitz··on Pyret: A new programming language from the creators of Racket
Thanks for the feedback. I think I responded to another comment that gave very similar feedback as you, so I'm just going to link to it here:

https://news.ycombinator.com/item?id=6708420

jpolitz··on Extending JavaScript with inline unit tests
A few things that Pyret gets out of "where" blocks that it's not obvious are easy with just assertions after the function.

- Reporting failures can more easily report context about the function being tested

- Since "where" blocks are actual blocks, we define helper functions and variables inside them that don't need to clutter up the namespace of the current scope

- It's easy to turn the assertions on and off; for example when importing a Pyret module we skip running the checks of that module by default.

- The localized information also plays into our story for type inference (which is work in progress), but starts from the unit tests in the where: block to figure out what the programmer intended for input/output types.

jpolitz··on Pyret: A new programming language from the creators of Racket
Thanks for the feedback.

Re: scope, you're right that using "def" the way I did isn't very idiomatic Ruby, but I always run afoul of it because it looks so similar to what I'd write in another language. I took that example down for the moment; I still have a personal gripe with it, but my "fundamentally broken" language was a bit strong. If I think of a more illustrative example, I'll put something back up.

The currying example I still think is weird in Ruby, and it's because it interacts bizarrely with the syntactic choice about optional argument lists. The thing that is wrong with JavaScript and Ruby is that they both have dot expressions and application expressions; o.m and f(x). It looks like

o.m(x)

should be a composition of dot lookup followed by an application, since both of those raw expressions make sense on their own. But in neither does the decomposition actually work:

m = o.m m(x)

There's no state or funny mutation going on here, but a simple kind of substitutability isn't working. JavaScript does it especially poorly, and Ruby has this awkward inability to decompose because of its choices about application syntax. Now, in Ruby I'm aware that with or without parens are actually both method calls, so it's not like there's a field access and an application in the underlying language model. But Ruby then adds the syntactic convenience of no arguments to make it look like access is possible, but that syntactic convenience is a bit of a leaky abstraction.

I should note that Python actually gets this nicely right IMO, and dot lookup curries self so this works out.

The underlying thing that irks me and I'm calling out here is the non-compositionality of what looks like two expressions that should compose. This is something we felt like figuring out and getting consistent for Pyret.

jpolitz··on Pyret: A new programming language from the creators of Racket
I've done a fair amount of programming in Ruby (two reasonably large Rails applications, spanning about two years of development time). When switching to Ruby from just about anything else that I program in from JavaScript to Python to Racket to ML, I get tripped up on exactly this issue because it doesn't match my expectations at all.

My gripe is that the simplest, cleanest construct in the language for defining named functional abstractions---"def"---doesn't support closing over variables, which other languages do, so I have to warp my thinking a bit to program in Ruby. The analogous program in the four languages I mentioned above (and Pyret, and more) works and creates a closure. Maybe I just don't think well in Ruby, because I end up feeling frustrated that Ruby seems to not like functions.

All that said, "in fundamental ways" is a little harsh. At least in this example, Ruby hasn't done anything to violate the integrity of static scoping the way, say, JavaScript's "with" does. I'll try to see if I can come up with a better example that's a clearer problem and less a personal dislike.

jpolitz··on Pyret: A new programming language from the creators of Racket
There's another response to this that I didn't mention. Shriram is writing a textbook that includes all the material for our advanced intro course and our programming languages course. There's a lot of good examples here:

http://papl.cs.brown.edu/2013/

jpolitz··on Pyret: A new programming language from the creators of Racket
> Do the type annotations result in performance/compiler optimization, as in Julia?

We've been designing the static system with exactly this in mind. The current implementation doesn't do anything fancy yet, but that's just because we've been getting off the ground and focusing on ergonomics and curriculum first. Getting performance in return for types is absolutely where we're going.

jpolitz··on Pyret: A new programming language from the creators of Racket
That's a useful comparison, thanks. While we were certainly inspired by Pythonic syntax, we have ended up somewhere slightly different.

The note about local variables is particularly important; we actively don't want Python's model of variables and scope.

jpolitz··on Pyret: A new programming language from the creators of Racket
You can actually use ";" as an alias for "end" in Pyret, so you could write:

    data BinTree: | leaf | node(value, left, right);
We use "end" or ";" in order to have unambiguous delimiters for the ends of syntactic forms and avoid needing to depend on whitespace (we added some discussion on pyret.org about our philosophy on indentation and why we don't want to depend on whitespace).

Making that leading pipe optional is a good idea. I made an issue for it, we'll think about if it'll break or confuse anything and add it if it doesn't:

    https://github.com/brownplt/pyret-lang/issues/106
jpolitz··on Pyret: A new programming language from the creators of Racket
Syntax is tricky and contentious, and our team members do love our Schemes. Members of the Pyret team have actually done some research on the issue of parenthetical expressions in introductory programming, and seen that parens aren't necessarily the best:

http://cs.brown.edu/~sk/Publications/Papers/Published/mfk-va...

One issue is that it's too regular: since open-paren means so many different things (start of a "defun", start of a function application, start of an argument list in a "defun", start of a syntactic form like "cond" or "if", start of a clause of "code", the list goes on), a typo can easily and drastically change the kind of error message you get.

More closely matching syntactic forms to the type of behavior the expression has will hopefully let us improve error messages and grokkability of the difference between concepts. We're collecting data about common syntax errors and actively asking what we can do better in syntax design based on what we observe about Pyret's use.

jpolitz··on Pyret: A new programming language from the creators of Racket
Thanks for your comments!

I have two responses:

1. You're right that exposing too much, too soon is a recipe for disaster. People can only fit so many new concepts in their head at once, and blasting them with refinement types from day one isn't a great idea. However, there's two things at work here: the role of the curriculum and the role of the language. Pyret is careful to allow a gradual transition to more and more complex features. Annotations are not necessary on day 1, and neither are iterators. We write untyped, recursive-descent functions on lists in the beginning, and build up to types and the pleasant terseness of "for" loops. If Pyret required introducing these concepts to write any programs, that would indeed be a problem, but we've put thought into the dependencies between curricular concepts and the needed language constructs to mitigate exactly this concern.

2. I think that some of the features you listed are actually a huge necessity in early programming, particularly special purpose syntax for data structures. Teaching the definition and implementation of a balanced binary tree to folks without language experience in Python or Java requires a huge amount of boilerplate explanation. You need to describe classes, fields, and constructors just to do the equivalent of Pyret's "data". The alternative (at least in Python) is to encode everything in lists and dictionaries, but hopefully in 2013 we've moved beyond writing all our data structures that way :-)

jpolitz··on Pyret: A new programming language from the creators of Racket
Thanks for the questions!

As to why we're advertising it as a teaching language: Our group has a design sense of how to do this, and Pyret's success in the classroom is what we're able to study, measure, and focus on improving. That doesn't mean we won't be thinking about managing million-line codebases or getting great performance out of the runtime, but those concerns won't necessarily be the main guide of our design decisions. We may very well end up with something that's a superb general-purpose language, but I'm not yet willing to give folks that expectation, or do so at the expense of the learning experience. Make sense?

EDIT to add: One thing that the Racket community has very successfully done is separate teaching languages from the main Racket language. A probable future for Pyret is splitting it into teaching and professional versions. I'd love to hear feedback about what more you'd like to see in Pyret, with this split in mind.

The getting started guide (http://www.pyret.org/getting-started/) and tour (http://www.pyret.org/tour/) is the best reference for getting started right now. If you didn't see those then they probably need more prominent placement.

jpolitz··on Magic: The Gathering API
This is absolutely awesome.

I have a few thoughts right off the bat.

1.

It would be nice to put a Gatherer link in every response, just for browsing purposes.

2.

Split cards are weirdly reported, as if they are two separate cards. In Gatherer these are reported with an id and a "part" field:

  http://gatherer.wizards.com/Pages/Card/Details.aspx?multiverseid=249394&part=Malice
It looks like you're only getting the first part. See that Spite//Malice is missing from

http://mtgapi.com/api/v1/fetch/search/malice

But I can search for "spite" and I find:

http://mtgapi.com/api/v1/fetch/id/249394

3.

It looks like you're using the Oracle text for every version (for example, http://mtgapi.com/api/v1/fetch/id/198 and http://mtgapi.com/api/v1/fetch/id/226588 have the same rules text, though they appeared differently when printed). Is there any way to get that information? That could be really cool for mining information about how terminology has changed over time.

jpolitz··on Establishing secure connection
Here's one related study, "examining the extent to which individuals will tolerate delays when told that such delays are for security purposes..."

http://cs.brown.edu/~sk/Publications/Papers/Published/emcahk...

Please Continue to Hold: An Empirical Study of User Tolerance of Security Delays

jpolitz··on JavaScript: Clarifying The Keyword ‘this’
Indeed --- Python goes to great lengths to work this way, automatically binding the self argument on method extraction.

    o.f()
Can be decomposed in Python:

    func = o.f
    func()
JavaScript fails this test, and it's a shame.
jpolitz··on JavaScript: Clarifying The Keyword ‘this’
Thanks, not sure what I was smoking there.

I can't seem to go back and edit, though...

jpolitz··on JavaScript: Clarifying The Keyword ‘this’
My comment was wrong, I was mis-remembering a bug that I'd seen before. You can get everything correct with the prototypes, but you cannot perfectly emulate it.

The only thing you can't do with JS emulation of .bind() is the ability to create a varargs constructor (https://bugs.webkit.org/show_bug.cgi?id=26382#c29). But at the same time, I don't totally grok that example yet. The relevant bit of the spec is http://es5.github.com/#x15.3.4.5.2, which describes the [[Construct]] behavior of .bind().

jpolitz··on JavaScript: Clarifying The Keyword ‘this’
Yeah, those were mentioned in the post as exception 2, but maybe I should make a note.
jpolitz··on JavaScript: Clarifying The Keyword ‘this’
I'm confused... doesn't that just reduce the question to "what is the current binding of self"?
jpolitz··on JavaScript: Clarifying The Keyword ‘this’
That's a reasonable way to think about bind().

On a pedantic note, due to complications with the new() operator, you can't actually implement .bind() in JS using only .call() and .apply() and get the prototype of the resulting objects right.

jpolitz··on JavaScript: Clarifying The Keyword ‘this’
The this keyword is confusing, it's good to see posts clarifying it. However, while the rule in the post will be right in many cases, this list is incomplete, even for some common usages of this, and might lull folks into a false sense of security:

1. "To the left of the dot" should be "to the left of the dot or bracket". E.g. o["foo"]() also passes o as this.

2. When you pass callbacks to built-ins like forEach, you can supply a thisArg, and if you don't, this will be bound to undefined (http://es5.github.com/#x15.4.4.18). (EDIT): Copying from cwmma below. Some callbacks, like setInterval and setTimeout, pass the global object rather than undefined. I suspect that DOM callbacks tend to use the global object and JS callbacks tend to use undefined, but that's not a blanket statement by any means.

3. This isn't purely JS, but when interacting with the DOM, this is also implicitly bound as a DOM element in event listeners. E.g. document.addEventListener('click', function() { console.log(this); }) will print the document object when you click on the page. This is quite relevant for any JS web development; if it makes it easier to conceptualize, imagine that the browser is calling a method with the DOM element to the left of the dot.

4. When the caller is strict mode code and the function is called without method-call syntax, undefined is passed as the this argument rather than the global object. You should always use strict mode to avoid accidentally handing the global object around (in strict mode, the "set to global" option is skipped in http://es5.github.com/#x10.4.3).

5. When using this at the toplevel (which is allowed), it is bound to the global object even in strict mode (http://es5.github.com/#x10.4.1.1).

And a few more esoteric ones:

6. In addition to call and apply, Function.prototype.bind() can also change the this parameter of a function, and violate the "left of the dot or bracket" rule (http://es5.github.com/#x15.3.4.5, see boundThis).

7. Inside a with() {} block (which you should never use, but we're trying to cover our bases here), this is bound to the object passed to with in the parentheses.

8. If a property is a getter or a setter, this is bound to the object to the left of the dot or bracket in the field access or assignment expression. This actually matches the rule in the post except for the fact that the "call time" is implicit; there are no () in the expression o.x, but it may call a getter function for x that passes o as this (http://es5.github.com/#x8.12.3).

(EDIT): One more doozy:

9. The dot and bracket operators implicitly convert primitives like numbers, strings, and booleans to objects, so it's not exactly what's to the left of the dot:

    Number.prototype.identity = function() { return this; }
    var x = 5
    var x2 = x.identity()
    typeof x2 === 'object' (true!)
    typeof x2 === 'number' (false!)
You can get the raw primitive if you make the function strict:

    Number.prototype.identity2 = function() { "use strict"; return this; }
    var x = 5;
    var maybe_five = x.identity2();
    typeof maybe_five === 'object' (false)
    typeof maybe_five === 'number' (true)

Please correct me if I've forgotten any...
jpolitz··on If Hemingway wrote JavaScript
Like this?

    function fibs(size) {
      return f();
      function f() {
        ... implementation here ...
      }
    }
jpolitz··on Show HN: Matches.js -- Powerful Pattern Matching for Javascript
Cool! Sucks that it has to be so stringly typed, but I can't see another alternative.

One concern:

"Each pattern will be tried in order until a match is found."

As far as I know, ECMAScript doesn't specify any enumerations on objects that obey an order... browsers do syntactic order, but it's by no means a guarantee. (e.g. http://stackoverflow.com/a/280861)

jpolitz··on Mechanized LambdaJS
It looks like the λJS has an issue parsing some of the escape sequences in underscore.js; it's a bug in WebBits parser that version uses.

Another implementation of the core semantics + desugaring approach, dubbed S5 (https://github.com/brownplt/lambdas5), is better engineered and actually uses SpiderMonkey's parser to handle more programs.

S5 runs underscore.js without any issues. Do give it a shot if you're still interested in trying things out.

Also, it's worth mentioning that S5 handles features of the new ECMAScript 5 specification, like strict mode, getters and setters, and new APIs, that the original λJS simply doesn't handle, so it's a much more complete account of JavaScript.

← PreviousPage 4 of 4