Before I get into the technical details at the heart of this blog post, I want to take a quick tangent to talk about failure and community. One universal truth to which I cling at all times is that humans learn through failure. Nowhere is this more apparent in my life than in learning programming languages (as well as spoken languages).

In order to grow, improve, and gain wisdom about how to apply our new knowledge, we need to fail. We need to fail often. This is where community comes in. A community around any new thing that requires difficult learning must be aware of this and embrace it. A community needs to know that new learners are going to fail, and fail often. This community needs to help these new learners through the failure process. If a community is insulting, disparaging, or otherwise unhelpful during these early iterations of stumbling, getting up, and stumbling again… new learners will simply walk away from the community and take their enthusiasm and support elsewhere.

People in the Pony community like Joe Eli McIlvain (jemc) have been remarkably patient with my newbie questions and failure. Every budding community needs knowledgeable, patient people like him.

Now to the technical stuff: I have been working on a Pony port of a library a friend of mine and I wrote called d20. This is a library that converts expressions well-known to dungeon masters like “3d10” or “2D6” into random rolls of the dice and simple addition/subtraction. You feed the roller with your expression and you can get back a result:

let roller = Roller("3D10+5")

let result = roller.roll()

Debug.out(result.total().string())

I didn’t do this because I thought there was any urgent need for such a library, but it presented enough of a technical challenge that I felt it would be a great learning exercise to port d20 from its original Rust implementation.

To do this, I had to create my own iterator that runs through regular expression matches in a loop. I won’t bore you with all the code, but you can find it in my github repo. As my match iterator went through a list of regex matches pulling what I call “terms” out of the “die roll expression”, it parses individual pieces. At some point, I end up converting “3D10” into a class that has a multiplier (3) and a number of sides (10). For the D&D impaired, this basically means “roll a 10-sided die 3 times and add the results”.

Probably the most illustrative class in my project is the Roll class:

class Roll

let _expression: String

let _values: Array[EvaluatedTerm]

let _sum: I64 new create(expression': String, terms: Array[RollTerm] box) =>

let rand = MT(Time.micros())

_expression = expression'

_values = Array[EvaluatedTerm]

for term in terms.values() do

_values.push( ( term, _DiceEvaluator(rand, term) ) )

end

_sum =

try

Iter[EvaluatedTerm](_values.values()).fold[I64]( {(sum: I64, x: EvaluatedTerm):I64 => sum + x._2.i64() }, 0)

else

0

end fun rollterms() : Array[EvaluatedTerm] box =>

_values fun values() : Iterator[EvaluatedTerm] =>

_values.values()



fun total() : I64 val =>

_sum

I really wanted to embrace immutability here and I truly love the idea of a “set once and never again” class field (the 3 let fields). There’s something fairly subtle going on here to make this work that I’ll talk about in a minute. But then there’s this:

_sum =

try

Iter[EvaluatedTerm](_values.values()).fold[I64]( {(sum: I64, x: EvaluatedTerm):I64 => sum + x._2.i64() }, 0)

else

0

end

Pony won’t let me get out of my class constructor without assigning a value to _sum, but this use of fold has a couple method calls that could fail. So here I’ve actually used the result of the try expression as the value to be put in _sum, and that satisfied the compiler.

Having to convert _values into an iterator, and then use Iter[EvaluatedTerm](…) to convert it into an Iter just so I can gain access to the fold method felt a little more awkward than a more functional-friendly language might… but it works. Every once in a blue moon I miss Scala’s implicit conversions which take code that would ordinarily look like the above and hide the complexity. Then I remember debugging Scala… and I stop missing it.