Fun with the RPN calculator in Ioke

In this article, Cédric Beust explains how to port a RPN calculator from Haskell to Fantom. It could have made me want to code more in Haskell or go discover Fantom. Not at all. Reading the article, I felt the urge to code the same algorithm with Ioke. You know, this language I discovered through a MasterMind Kata a while ago.

Here is the Fantom code:

foldingFunction := | Int[] n, Str p -> Int[] | {
  echo("n:" + n)
  switch(p) {
    case "*" : return n.push(n.pop * n.pop)
    case "+" : return n.push(n.pop + n.pop)
    case "-" : return n.push(n.pop - n.pop)
    case "/" : return n.push(n.pop / n.pop)
    default: return n.push(p.toInt)
  }
};
"8 1 2 + 5 * +".split.reduce([,], foldingFunction)

Here is my first try with Ioke:

rpn=method(sum, x,
  case(x,
    "+", [sum pop! + sum pop!],
    "*", [sum pop! * sum pop!],
    "-", [sum pop! - sum pop!],
    "/", [sum pop! / sum pop!],
    [x toRational]
  ) + sum
)
"8 1 2 + 5 * +" split fold([], sum, x, rpn(sum, x)) println

Quite neat. The code looks a lot like the Fantom code or even the Haskell code/
Now let’s use the power of Ioke and remove duplication by defining rpn method on List itself :

List rpn = method(x, 
  append!(case(x,
    "+", pop! + pop!,
    "*", pop! * pop!,
    "-", pop! - pop!,
    "/", pop! / pop!,
    x toRational
    )
  )
)
"8 1 2 + 5 * +" split prepend!([]) fold(rpn) println

I like that one better. We could remove some more duplication at the expense of readability, because, you know, there must be another way to convert “+” to , “*” to * and “-” to -:

List rpn = method(x, 
  append!(case(x,
    or("+","*","-","/"), Message fromText(x) appendArgument(pop!) sendTo(pop!),
    x toRational
  ))
)

Here we used reflection, but we could use eval method and regexp:

List rpn = method(x, 
  append!(if(x !~(#/[-+*/]/),x toRational,Origin eval("#{s pop!}#{x}#{s pop!}))
)
"8 1 2 + 5 * +" split prepend!([]) fold(rpn) println

Ok, now let’s try to recall those days I was an expert in Perl, and compress this to the max:

r=fn(e,e split fold([],s,x,s append!(if(x !~(#/[-+*/]/), x toRational,
Origin eval("#{s pop!}#{x}#{s pop!}")))))
r("8 1 2 + 5 * +") println

This one, I’m not really proud of. Should I?