Hacker News new | comments | ask | show | jobs | submit login

Pyret looks like a there's a lot going on, based on the examples. It has implicit returns, special purpose syntax for data structures, iterators, assertions, refinements, etc. Having support for more stuff makes it less suitable as a teaching language, not more. Pyret looks like a has the good bits of Python plus a whole bunch of other cool stuff. But the language is pretty complex as a result.

Cool? Yup! Good for teaching? Probably not.

Take a look at the grammar and judge for yourself: https://github.com/brownplt/pyret-lang/blob/master/src/lang/...




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 :-)


Joe, any plans to push this out in Kathi's intro course at WPI?


Not our decision! Tell the WPI folks about it and why (if) they should consider using it.


Language complexity may not matter much.

"He speculated that the size of programming languages might confuse many students trying to learn to program. Java, the teacher’s programming language of choice at the end of the 20th century and at the beginning of the 21st, is defined loosely but at great length in several hundred pages.4 Natural Deduction, on the other hand, can be defined precisely on a single side of a single A4 sheet, in a normal font size. It is small and conceptually simple: surely it can be taught! Well, up to a point it can, but the results were depressingly familiar. Figure 14 shows the results from an in-course exam on formal logical proof. Unlike the examinations of section 3, students were strongly discouraged from guessing, with negative marks for wrong answers in multiple-choice questions and extensive unseen proof problems. As a consequence, two humps are clearly visible, with one at 33-40 (about 50%) and another at 65-72 (about 85%). (This data was collected long before Dehnadi’s test was developed, so we can’t analyse it further)."

Saeed Dehnadi - The camel has two humps (working title): http://mrss.dokoda.jp/r/http://www.eis.mdx.ac.uk/research/Ph...


Actually Brown teaches Intro to Programming Languages using Pyret. http://cs.brown.edu/courses/cs173/2013/software.html


By looks of the course number, this is a third or fourth year elective for undergrads. It seems that what is being taught here is the design and engineering of programming languages, not programming itself.


That's correct - 173 is definitely not an intro course. But, we also are teaching an intro course with it:

http://cs.brown.edu/courses/csci0190/2013/software.html


They also teach the Accelerated Intro to Computer Science (the first cs course some students take) using Pyret. http://cs.brown.edu/courses/cs019/2013/


The grammar can be described with a single page in actual BNF. Thats AMAZING. The only languages have shorter grammars are core lisp/scheme. Ruby is fundamentally impossible to describe with a BNF.


> Thats AMAZING. The only languages have shorter grammars are core lisp/scheme.

It's nice, but I'm not sure I'd go with amazing. Most or all of Niklaus Wirth's languages have shorter grammars, for example. I'd imagine quite a few others do as well.

E.g. Oberon-2 has 33 EBNF productions: http://en.wikipedia.org/wiki/Oberon-2_(programming_language) - that fits fully on screen for me with my default font size...

Regarding Ruby, I agree. I'm working on a Ruby compiler, and "decoding" the MRI parser into something closer to optimal will still leave an awful mess no matter what you do. I love programming Ruby, but implementing it is hell (and one of the reasons I'm trying it...)


If you think parsing is the hard part, wait for the semantics. See, for instance, the Ruby examples on the Pyret home page.


I don't see any difficult semantics in those examples.

The hard part of parsing Ruby is mostly trying to conform to MRI rather than to a formal grammar. In comparison the semantics are reasonably simple.

It's hard to compile Ruby efficiently, though, but for reasons unrelated to those examples.

(Incidentally I don't know if that scoping example is intentionally ignoring the fact that Ruby does support lexical scoping (EDIT: for blocks), or if it's lack of understanding of what what "def" is.

The Ruby way of achieving what that example is trying to do is:

    def f(x)
      g = proc do |y|
        x + y
      end
      g[2]
    end
    f(2)
(g[2] is shorthand for g.call(2))

"def" explicitly introduced a method-level scope of the innermost wrapping class scope. Nesting 'def's is not in any way idiomatic Ruby. It's not necessarily very useful to nest them given those semantics. But "def" is really just syntactic sugar for #define_method, and so it is logical for it to work where #define_method does.

I might be inclined to agree that the different Ruby ways of defining blocks, methods and closures could do with some simplification. Allowing "def" to bind surrounding variables would remove a lot of the need for class instance variable or class variables, for example. )

EDIT:

Also, the currying example would look like this in Ruby:

    o = Object.new
    def o.my_method(x)
      self.y + x
    end
    def o.y
      10
    end
    method_as_fun = o.method(:my_method)
    method_as_fun[5]
You can argue for implicit currying if you want, but to me that's far more confusing. That said, it is extremely rare to find currying used in Ruby code.


Yes, I spoke too loosely. What I meant to say is that you'll have hard time making Ruby fast. I assumed implicitly that the point of writing a compiler was to optimize performance, though maybe not.


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.


> Incidentally I don't know if that scoping example is intentionally ignoring the fact that Ruby does support lexical scoping (EDIT: for blocks), or if it's lack of understanding of what what "def" is

I also don't think it is fair to say that ruby "failed to nail lexical scope in fundamental ways".

I like how it enables lexical scoping with blocks, instead of enabling it in, maybe, more common way with embedded functions. To me, the ruby way is more intuitive.

Also, 'breaking scope' with block (that acts as anonymous function) instead of defining new (embedded) function feels more explicit wrt real scoping intentions.


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.


agreed, having decent semantics is key. :)


> The only languages have shorter grammars are core lisp/scheme.

Smalltalk would like to have a word with you: http://chronos-st.blogspot.be/2007/12/smalltalk-in-one-page....



DrRacket pioneered the notion of "language levels" that grow with the student's needs. Pyret will end up taking some variant of this route. It's just too early to design those levels yet. Once we have more experimental data about what kinds of inadvertent mistakes students make, we'll be in a position to say, "Oh, yeah, that was a bad thing to release on day 1", etc.


The language is in use in introductory courses, with a gradual introduction of these topics. It's no different from using any other modern programming language in education.

Our goal is to eventually create language levels, pioneered by DrRacket, based on what we learn from observation. This is a research project in addition to a development one, where the research is into human factors.


Why replace Racket with Pyret? Is this purely to stop students from being able to complain about syntax or is there a deeper strategic reason behind this?

Refinement types are nice. I really wish those were more common.


Your second paragraph answered your first paragraph (-:.

1. We have a different view of static typing than Racket.

2. We have a different view of the type language than Racket.

3. Long term, we are working on smoothly integrating testing, types, and specification [as a spectrum -- perhaps even as a cycle -- rather than as three different things]. Refinements are an instance of this. Another is our plan for how to do type inference.

4. I am also convinced that parenthetical syntax has real problems that I can't completely ignore. Of course, it doesn't hurt if it can get people to stop complaining. (It's not only students: the bigger opposition often comes from teachers. Unfortunately the teachers control the path to the students, so their views _really_ matter!)


I am also convinced that parenthetical syntax has real problems that I can't completely ignore.

What brought you to this conclusion? Experience from Bootstrap (in which I found arithmetic and parenthesis counting to be the biggest stumbling blocks) or just personal reflection?


All of the above, plus the research that Guillaume Marceau, Kathi Fisler, and I did.


My understanding is that it's a relatively simple place to learn about how to manipulate data (as in algebraic data type style, FP data) and has all of the conveniences of syntax that allows someone to think fluently about that concept.

So it's syntactic complexity might be high, but it's conceptual complexity is rather low.


Edit: deleting snark.

Oz is one of the best languages ever for learning CS.


By that logic, assembly is the best language to learn programming.

Presumably it is easier to learn about concepts with a language that has them.


Actually, there's a (devil's advocate) argument for teaching assembly first. After all, TAOCP is all assembly for good reason. Many programmers who never learn any assembly language lack a kind of procedural literacy, as well as a mechanical sympathy, which are a big part of the craft of programming.

But, as you state, there are a lot of higher-level programming concepts you can't effectively learn in assembler (one imagines a bizarrely circuitous route around learning to build compilers in assembly (via Forth perhaps) in order to teach lexical scoping or similar).


Obviously I'm not suggesting that assembler is the best teaching language. I could also take your logic and use it to conclude that C++ is the best programming language to learn first because with C++ you can learn about the implications of heap corruption when improperly handling exceptions in constructors. C++ allows functional, procedural and OO and many more programming styles. It supports manual and automatic memory management. It can be used to teach high level and low level programming constructs. But I think we're all in agreement that C++ is not a good first language to learn despite (or because of) its versatility.

I think Python is a very accessible programming language that still has enough depth to go into a lot of interesting CS subjects. That Python doesn't have manual memory management or support for true multiple inheritance means that you can't teach those things effectively with Python, and I'm completely OK with that trade-off.


Defining basic data structures is a pain. See the example on the Pyret homepage.

I am already seeing a trend in Python tutorials where they just stay away from complex data, and stick to what is easy to encode in the data structures that Python does provide (lists, dictionaries, arrays).

We've seen this movie before: it's what happened when languages like Fortran, Pascal, and C dominated programming education, and the fact that they made some data (particularly of the linear kind) really easy and others a nuisance meant that curricula diverted towards the path of least resistance.

It's harder now to spot the pattern because it's better hidden. But it's still there.


I don't see why a complex language can't be good for teaching if its designed in such a way that the complexity is optional, and you can use a focussed subset for pedagogical purposes (especially when the language supports using different focussed subsets depending on the specific focus of pedagogy.)

I mean, even if you look at how language like Scheme/Racket, or Python, or pretty much any other language is used in teaching, you don't usually use the whole thing in any one teaching context.


This is true of all educational settings -- except that the language subset then becomes an unenforced (and hence very leaky) abstraction. Students inadvertently step outside the subset and get either bizarre errors or, worse, their program runs and produces strange errors.

DrRacket pioneered the notion of "language levels" that grow with the student's needs. Pyret will end up taking some variant of this route. It's just too early to design those levels yet.




Guidelines | FAQ | Support | API | Security | Lists | Bookmarklet | Legal | Apply to YC | Contact

Search: