Hacker News new | past | comments | ask | show | jobs | submit login
Introduction to the Pony programming language (opensource.com)
70 points by spooneybarger on May 31, 2018 | hide | past | favorite | 72 comments

>"A programming language is just another tool. It's not about syntax. It's not about expressiveness. It's not about paradigms or models. It's about managing hard problems." —Sylvan Clebsch, creator of Pony

Well, isn't that a zero content statement?

Sure, if you can't help in managing problems (and hard problems) the language is not that good.

But I'd argue a language succeeds or fails in helping you manage problems through its syntax, expressiveness, paradigms, models, and other attributes (like community size, lib ecosystem, tooling, speed, etc).

My hunch is Sylvan was just trying to moving conversations about Pony language past some of the "shallower" topics that swirl around new languages.

The amount of times new language conversations nitpick c-style semicolons and curly brace blocks, lisp's parentheses, prolog ending statements with a ., or Python's significant whitespace ... It's exhausting. If I never have to read the words "visual line noise" causing "cognitive load" again I'll be happy. It's not that those aren't occasionally interesting but they are over trodden topics.

I've found myself steering conversations like this: "It's not the language it's the runtime!" Whats interesting about erlang, go, and pony are the runtimes. The languages play a role in how they expose those goodies but that's a conversation well beyond whitespace and semicolons.

I think what the author is trying to say is that expressiveness and "managing hard problems" are a level of abstraction above syntax and paradigms. I concede that it's a bit hyperbolic to say it's "not about" the latter, but I'd argue that they're less important than their intent, i.e. what the syntax is designed to express. For example, Golang's channel syntax would be a lot less useful if it were just a weird synchronous assignment operator, instead of an abstraction over a concurrent operation.

Context is here: https://www.infoq.com/presentations/pony about 5 minutes in.

Hi all,

In addition to being the submitter, I'm also the author and a member of the Pony core team. I'll check the comments here from time to time and answer what I can.

The website does not show any code in the first or second page. Only a cheatsheet after 3 clicks - which is a pdf.

You'd be looking for the tutorial: https://tutorial.ponylang.org/

It would be good to show off the language more prominently, on the home page.

You could consider some runnable demos like python and rust's. [1], [2]

[1]: https://www.python.org/

[2]: https://www.rust-lang.org/en-US/

Not every language presents itself as an object to be marketed.

Also, pony is still an early product. It's secure, but it still needs more work before extremely important work happens using pony.

In that regard, it's better to attract only those who propel themselves then to attract a bunch of users who wont/cant contribute and will impose their demands on the core team.

It's just not time to market yet.

I'm on mobile and I don't see any code or links to code. Is it different on desktop?

interesting. do you see code on mobile when you visit:


Yep! I suppose I didn't notice the hamburger navigation at the top left.

The tutorial is hosted on Gitbook. I'm finding that it updates very slowly on mobile for me. Even if it isn't broken, it feels broken. Hadn't noticed that as an issue previously.

This is an issue I've noticed with GitBook in general. I think I began to notice it in 5.0

that's good to know and somewhat depressing.

I understand that its actor model pursues very different ideas from Erlang and is therefore very limited and bare bones, basically allowing only per actor event handlers (behaviors in Pony terms). How does it work in practice, do you use higher order functions to construct basic flows? Like doing something, then entering receive loop for certain messages and making sure no other handler gets called to mess with the state until we are in receive loop for this type of message, then doing something else and entering another receive loop and so on.

Sorry, I don't understand your question but I'm going to answer it as best as I understand. Apologies if it ends up being a non-answer.

There's a good talk by Scott Fritchie on the differences between Pony and Erlang that I would suggest.

talk: https://www.youtube.com/watch?v=uv-3ptTD8hg&feature=youtu.be slides: https://github.com/slfritchie/wide-world-of-actors

Like Erlang, each Pony actor has a mailbox that is processed in a serial fashion so that only one message is being processed.

In Erlang, you have selective receive that allows you to receive messages out of order that they arrived. Pony has causal messaging where each message has to be handled in the order it was received. Otherwise they are same in terms of how received messages are handled.

There's no `receive` in Pony as the behaviors are called directly.

I would not call that "very limited and bare bones". Its different.

Pony's model is different than Erlang's, so trying to think of it in terms of Erlang's model ("entering receive loop") may not be helpful when trying to understand it.

The Pony runtime takes care of scheduling an actor to run when there is a message for that actor. The actor runs through the behavior and then waits to be scheduled again by the runtime when another message is available. If you want to filter messages you need to arrange a way to do that in your code. There's no way to inspect the message queue.

I get it, it's like event driven programming with static event handlers per actor. Where you can't express a wait for a specific message directly in the body of the function. But you still have to do it somehow, which is what my question was about, how do people do it in Pony. Typically in event driven systems to do this either nested callbacks are used or higher order programming (futures/promises, etc).

You can use callbacks, state machines, promises. All are available as options to use.

So kind of like the RabbitMQ worker/message/job dynamic?

Pony uses a queue for storing messages that have been sent to an actor, so that's probably a reasonable way to start thinking about it. I'm not sure how far I would stretch this comparison, though. :)

This might be a little hard to converse on over HN comment. If you stop by the #ponylang channel on freenode, it would be easier to discuss as there would probably be a lot of back and forth.

Hi spooneybarger. I'm curious: why is 1/0 == 0? Is it because there is no way to panic/kill the current actor with an error?

That's covered in the Pony tutorial.


I can elaborate a bit on what is in the tutorial. With the current semantics, you as the application programmer can check for division by zero and throw `error` if you want. Otherwise, every division operation would be partial which given that pony forces you to handle all partial functions, can get to be very painful.

We are working on adding value dependent types and will be revisiting the divide by 0 question. Some work was done on that for a thesis a couple years back but hasn't been integrated into Pony by the author as they have had time constraints since leaving school. You can check out a video about the work here: https://vimeo.com/175746403

It's definitely something that folks bring up. We aren't thrilled with the current functionality but feel its better than the previous functionality and are looking to slow improve the situation over time.

It can definitely bite the uninitiated if they aren't careful.

It might be worth clarifying in that tutorial that this behavior is different for floating point division by zero (correct?) I believe that numerical tools like MATLAB give the same result as Pony for integer divide by zero, if that gives you or others more comfort... Although they return a 'divide by zero warning' at the same time which can be user-disabled.

It's better to be wrong and functional than correct and complicated?

PS: This is the approach PHP took in the early days too.

BTW, this is the same approach taken by the Coq, Isabelle and Lean theorem provers. In all of them, 1/0 = 0. It is certainly unconventional, but whether this is wrong is a matter of philosophy. Basically, division by zero is undefined, but you can define it to be zero and so extend the definition. This is consistent with the rest of arithmetic. You get more theorems (like for all a, b and c, a/c = b/c), but no inconsistencies.

You'd think that in these kinds of dependent type systems you would have to use the type system to prove that when doing a/b, b is never equal to 0.

Well, designing a language (even a mathematical one) is an art, and you need to trade off measures of convenience. The mathematical axioms that define division by zero to be equal to zero are consistent and so "correct" (if unconventional), and it turns out to be more convenient to define things in this way in those particular formalisms (rather than having to prove each time that you're not zero). You can define different theories in those systems that leave zero outside the domain of division or define it to be some value that means "undefined", but that's how it's done in the default arithmetical theories in those languages (and, BTW, Isabelle/HOL is not based on dependent types; Coq and Lean are). Other proof systems, like TLA+, do it differently.

For a recent discussion about this on the Isabelle mailing list, see here https://lists.cam.ac.uk/pipermail/cl-isabelle-users/2018-Mar... (search for 1/0)

The users of Coq and Isabelle are generally people with mathematics degrees who check their work. Not your average programmer.

You are correct that the requirements are different, but not exactly for the reasons you mention. Users of those languages use them to prove theorems, and the theorems you get are correct with respect to the arithmetical theory used. Programmers may want some error condition when dividing by zero, because that's what they believe their users expect (say, the program drives some actuator, and bad things may happen if division by zero is not detected). So yeah, the justification for this behavior needs to be different.

It's an intersting philosophical conversation. Take floating point for example. I just fired up a Python interpreter and did:

>>> 0.3 * 3.0


There's are a lot of areas of computer science that touch on these sorts of problems. And people love to do Wat! talks for all those fun edge cases where how computers work and how programming languages work run up against our expectations of what we expect to be correct.

It’s not wrong. It’s just a definition issue. The result is undefined in mathematics.

Throwing an error is actually “wrong” as the result should actually be undefined/null but most languages throw for practical reasons.

So in fact it’s most languages that are being “wrong but functional”. Pony is just functioning differently.

You are right, but the question is what most programmers want. It is certainly reasonable to believe that most want some special treatment of division by zero. Assuming the program is connected to some actuator (even a screen) -- as most programs are -- while not wrong mathematically, this results in something that is wrong "physically," and doing so silently, without a mechanism to later recover, from the result alone, whether division by zero occurred.

Right, though there’s nothing intrinsically better about throwing an error - it’s just the idiom programmers are more familiar with.

If it’s raised and documented well there’s no reason it wouldn’t work as undefined (could throw type errors on usage after that for example).

> Right, though there’s nothing intrinsically better about throwing an error - it’s just the idiom programmers are more familiar with.

Well, it can be argued that physics dictates an error is intrinsically better than 0, and that programs care about physics at least as much as about mathematics.

Any plans to make the compiler on Windows work with clang/msys rather than Visual Studio? I played with the flags and somehow managed to get an Hello world compiled to .exe, but it would crash upon start.

I know it's kind of stupid, but relying on such a huge behemoth was my biggest turnoff.

There's an issue for that: https://github.com/ponylang/ponyc/issues/2079

Also you can use the Visual C++ Build Tools, which is just the compiler without the IDE.

https://github.com/ponylang/ponyc#windows-using-zip-via-bint... http://landinghub.visualstudio.com/visual-cpp-build-tools

Since there are no exceptions, in a try-else how can you tell what the error was?

If you need to distinguish between different errors than a union type like (Result | ErrorA | ErrorB) should be used.

I mean like

    if not x() then error end
    if not y() then error end
    // which error happened?

If you need to distinguish between error then that would inappropriate. Without a real example, I can't really say what the right approach would be.

Looking at your example, I would say, if you care about which error happened, you should be using separate try blocks.

Errors aren't meant for flow control like that so really, you shouldn't be indicating error inside a try like that.

One option that Pony could have gone with is to use union types to always indicate errors:

(My Result | MyError)

There's overhead to that though. You always have to match on the type. For situation where an error is unlikely, partial functions are nice. Partial functions are not exceptions though, and aren't designed to do flow control so trying to use like some might use exceptions (to do flow control) is going to be painful (as your example points out) because they weren't designed for that use case.

How about things like opening a file, where there could be a variety of reasons it could fail?

This might be a better conversation for IRC (you can find me on Freenode in #ponylang as SeanTAllen) as we are replying to each other fast enough to trigger the flamewar detector. Now that it is letting me reply...

You should use a union type for that situation. Note, the current File API in the Pony standard library doesn't do that and it's something that we intend to fix before version 1.0. At this point, now one has gotten frustrated enough with it to open an RFC to propose an updated API. Someone will at some point though. But... volunteer project, limited time, itch to scratch etc.

Please consider putting some code snippets in the home page, I needed I don't know how many clicks to find a random code snippet to look at in the docs.

Exactly. This is common on many programming language sites. You shouldn’t have to dig deep before finding code examples.

Fortunately Rust doesn’t do this and has a big example right at the top of its front page.

Looks like JS crossed with Python (not a bad thing at all)

We've heard the Python comment before, never the JS one. That's a new one to me. What brings JS to mind?

Nothing specific! Just a general impression.

I’m primarily working in JS these days and it felt very familiar and approachable so...

> Pony's reference capabilities and Rust's borrow checker both provide data safety; they just approach it in different ways and have different tradeoffs.

Would like to see a summary of what those tradeoffs are. Very curious!

This won't be an exhaustive answer, but I can asnwer a little. Also note, this is purely based on my own experiences with Rust (and Pony) and from what I've heard from other folks as well.

Both are difficult for folks just learn the languages to grasp. When I tried doing async programming in Rust, I found the borrow checker especially hard to deal with. Pony's reference capabilities, once you understand them, apply quite naturally to async code. This is important as most actions in Pony involve sending asynchronous messages between actors. There are certain patterns of passing around isolated regions of memory that are a pain in the ass to do in Pony that are very easy to do in Rust (once you know how to work with the borrow checker). It's something we are aware of on the Pony and are working to address.

Reference capabilities are part of the type of variables. This can make writing your own generics really hard unto you have a really good grasp of the type system and reference capabilities. It's the steepest learning curve in all of Pony.

Reference capabilities exist only at compile time but, something about how people think about them leads people to wanting to be able to match on them at runtime. I suspect this is something about how we teach them. It's a bit of a hurdle for some folks and can lead to some confusion early on.

Someone who has spent more time writing Rust and helping people learn Rust could come up with a similiar list. From time to time, Steve Klabnik and I have talked about similiarities in how problems folks have in learning the two languages.

I prefer reference capabilities to the borrow checker because reference capabilities more closely align with how I think about data safety.

Being the dual of Sean, I know Rust super well, but don't have as much experience with Pony, especially newer versions. This basically corresponds to my understanding as well. I also say "Pony is like Rust with Erlang" too.

I'd like to do a comprehensive comparison someday, but just don't have the time...

Same (on the just dont have the time).

Sounds very interesting. Very much like to see other languages starting to take this stuff seriously. Do you know where Pony is used in industry?

There's a company in the UK that does consulting work for the NHS that recently announced in the Pony IRC that they were looking to hire. You'd have to check the IRC logs to find the name (search is by day so it could take a while): https://irclog.whitequark.org/ponylang/2018-04-30

An early version of the Pony runtime is in use at one of the large banks in the UK.

There's a couple other uses that I've heard of that I'm not at liberty to talk about.

Publicly, Wallaroo Labs (where I work) is the only company talking about their usage.

It's still early days for Pony. Pre-1.0 and all that. Plenty of improvements that we want to make to the language, the standard library and ecosystem before it makes it to 1.0.

So, as someone who has only dabbled in Pony, and Rust, I'll try to give an answer. With Rust, you have to fight the borrow checker, and either work out lifetimes manually, or just copy stuff around 'by hand' -- In reality, the whole set of traits to automagically copy stuff around doesn't work. In addition, the mental overhead of Cells, mutable cells, Rcs, and ARCs, is a lot.

Pony normally works out of the box, because quite a bit can just be passed by value. You can pass by reference using reference capabilities, but there's a lot less cognitive overhead there, and a lot less fighting the compiler. I think, in the future, Pony could even derive the capabilities required automagically in many cases.

I'd say the biggest issue is less around safety, but around the I/O, and FFI models.

I think one of the core differences that informs a lot of the other trade-offs is Rust has minimal runtime (e.g. no garbage collector, works in bare metal environments), whereas Pony (I think) is willing to have a heavier-weight runtime with garbage collection.

Lots of discussion from three years ago:


It appears in the last three years they haven’t made good on this admission to show language examples on the website:

> Yes you're right, we will put some code samples in earlier on.


I was surprised to see such a long intro to a new language without any code samples. Did I miss something?

I, the author, don't find code samples particularly interesting. When I want to consider a language, I start by asking what the hard problems it solves are. Why would I want to learn this language? What does it give him, what is it better at than the languages I already know. Given that is my bias and I am the author, that is what I wrote about.

Was the article for you or an audience, what outcome are you trying to achieve, does your decision still hold true?

This is not a great intro to Pony. The most interesting thing about Pony is how data permissions work and how that allows for safe shared memory (a place where Erlang can fall down for certain applications as data is always copied between actors with the exception of large binaries)

Unfortunately for me the language felt a bit big and like it was doing too much (I guess you could say Elixir is in that same boat and I eventually got over that). It has the same "kitchen sink" problem as C++ and Scala. The use case for safe shared memory is interesting though and I'm keeping an ear to the project in passing

> it's compiled to efficient native code

When people say this, do they mean that it's compiled to assembly? So what's native about it is the instruction set, yes?

"native code" insofar as i've ever seen it, means something "natively understood" by the cpu. the typical alternative is "byte code" which must be interpreted (or jit'd, etc).

I meant as compared to being interpreted in some fashion.

Pony is a slang word in UK, not great choice for name.

Yes it does mean £25, but rarely used any more however the alternative is

Meaning: Rubbish; nonsense, or 'of poor quality'. Often shortened just to 'pony'.

What's the origin of the phrase 'Pony and trap'? This is Cockney rhyming slang - pony and trap -> crap.

Snark: Pony Express will be a library or framework to help resolve current issues.

Zero examples.

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