import init.lean.name
open Lean

abbrev ConstantTable (α : Type) := HashMap Name α

def mkSimpleFnTable : IO (IO.Ref (ConstantTable (Nat → Nat))) :=
IO.mkRef {}

@[init mkSimpleFnTable]
constant simpleFnTable : IO.Ref (ConstantTable (Nat → Nat)) := default _

def registerSimpleFn (n : Name) (fn : Nat → Nat) : IO Unit :=
do ini ← IO.initializing,
   unless ini (throw "we should only register functions during initialization"),
   simpleFnTable.modify (λ m, m.insert n fn)

def add10 (n : Nat) := n+10
def mul10 (n : Nat) := n*10
def inc (n : Nat) := n+1

@[init] def regAdd10 : IO Unit := registerSimpleFn `add10 add10
@[init] def regMul10 : IO Unit := registerSimpleFn `mul10 mul10
@[init] def regInc   : IO Unit := registerSimpleFn `inc inc

def eval (fName : Name) (n : Nat) : IO Nat :=
do m ← simpleFnTable.get,
   match m.find fName with
   | some f := pure $ f n
   | none   := throw (IO.userError "unknow function")

def main (xs : List String) : IO Unit :=
do [f, x] ← pure xs | throw "invalid number of arguments",
   r ← eval (mkSimpleName f) x.toNat,
   IO.println r,
   pure ()
