A second look at pony language

I last wrote about my initial look at pony. I have since played a bit more with the language and compiled a list of interesting quirks I learned about pony.

Division by 0

Pony wont crash if you devide by 0. Division by 0 is 0 :)

fun a(i: U64): U64 => 10 / i
env.out.print(a(0).string())                  //  0

But only if the zero is not known when compiling the function

fun b(): U64 => 10 / 0                        //  main.pony:2:24: constant divide or rem by zero

Sum types

The sum types of pony are a beauty, and honestly short of Haskell I think the most pleasant to use for me so far in any language :) They are so powerful they even entierly replace the need for Option/Maybe types.

fun c(str: String): (U8 | None) =>
    match str
        | "one" => 1
        | "two" => 2
        else None
    end

env.out.print(Abc.c("one").string())        //  1
env.out.print(Abc.c("gazzillion").string()) //  None

Calling C

The C FFI of pony is fairly straight forward

// import library
use "lib:computersays"

// declare function from library above
// with the correct types
// pony will trust that you get this
// correct. in this case the function is
// called answer, its return type is USize
// and it takes no arguments
use @answer[USize]()

// now we can call the function
// and print its output
env.out.print((@answer().string())          //  42

Interface or Traits

Some languages support either traits/nominal subtyping or interfaces/structural subtyping. Pony supports both.

trait Named
  fun name(): String => "Bob"

class Bob is Named

env.out.print(Bob.name())                   //  "Bob"

Sorry I couldnt think of a better example to illustrate the “structural” part :) Interfaces also accept the keyword is which for interfaces with implemented functions is actually how you would say that this class implements interface Named without the “structural subtyping” part :)

interface Named
  fun name(): String => "Bob"

class Bob is Named

env.out.print(Bob.name())                   //  "Bob"

Behaviours

Actors can have behaviours be. These behaviours are how actors communicate. There is no “await” function like with BEAM languages so a Main actor and a Counter actor might communicate like so:

actor Counter
  var _count: U32

  new create() =>
    _count = 0

  // this is accessable from the outside because it is a `be`
  be increment() =>
    _count = _count + 1

  // a `be` can get an alias to a (in this case an actor with `tag` refcap) passed
  be get(main: Main) =>
    // we call Mains `be display`
    main.display(_count)

actor Main
  let _env: Env

  // Main is the entrypoint of an executable, Env is passed implicitly
  // as new create (the constructor) is called on execute
  new create(env: Env) =>
    _env = env

    var count: U32 = 10
    var counter = Counter

    for i in Range[U32](0, count) do
      counter.increment()
    end

    counter.get(this)

  be display(result: U32) =>
    _env.out.print(result.string())         //  "10"

Author | Thomas Bjertnes

Developer.