#910 Another callback question

fury Thu 14 Jan 2010

Hi guys!

If I have this code:

class HFan
{
  Void xecho(Int i)
  {
    echo(i)
  }

  Void main()
  {
    g(xecho)
  }

  Void g(Func m) 
  {
    l := [1,2,3,4,5,6]
    x := l.findAll |Int i->Bool| { i % 2 == 0 }
    x.each |Int i| { m.call(i) }
   }
}

How do I send the xecho method as a callback? I'm getting x.fan(10,11): Unknown slot sys::Void.func ERROR: cannot compile script . I'm not really sure if declaring m as Func is the right way to go here. Not sure how to get the script to compile. Halp! :)

katox Thu 14 Jan 2010

Hey!

You have to write your code like this

class HFan
{
  Void xecho(Int i)
  {
    echo(i)
  }

  static Void main()
  {
    hfan := HFan()
    hfan.g(#xecho.func)
  }

  Void g(|HFan implied, Int i -> Void| m) 
  {
    l := [1,2,3,4,5,6]
    x := l.findAll |Int i->Bool| { i % 2 == 0 }
    x.each |Int i| { m(this, i) }
   }
}

Explanation:

  • you probably wanted main method static, i.e. a starting method for the class,
  • g is an instance method so it must be called on an existing object (added hfan instance),
  • xecho is also an instance method, so this parameter is implied even though it is not present it the signature explicitly (like in Python) - see methods as functions,
  • #xecho gives you xecho slot of the current class and .func provides it as a Func,
  • in g method call you need a proper signature - the first parameter is a function so all its parameters must be explicit (including this),
  • in g in call of m you need an extra parameter which is the object on which g is called, i.e. this.

fury Thu 14 Jan 2010

Thank you very much for your reply! What if both g and xecho were declared as static? How would main have changed then?

katox Thu 14 Jan 2010

Then you can drop implied this object (which is ignored anyway).

class HFan
{
  static Void xecho(Int i)
  {
    echo(i)
  }

  static Void main()
  {
    g(#xecho.func)
  }

  static Void g(|Int i -> Void| m) 
  {
    l := [1,2,3,4,5,6]
    x := l.findAll |Int i->Bool| { i % 2 == 0 }
    x.each |Int i| { m(i) }
   }
}

with the same result

$ fan hfan.fan 
2
4
6

fury Thu 14 Jan 2010

This is amazing! Thank you very much! I think I just found my statically-typed Ruby :)

tompalmer Thu 14 Jan 2010

I would write it more like this:

class HFan {
  Void xecho(Int i) {
    echo(i)
  }

  Void main() {
    g {xecho(it)}
  }

  Void g(|Int i -> Void| m) {
    l := [1,2,3,4,5,6]
    x := l.findAll {it % 2 == 0}
    x.each {m(it)}
  }
}

Or something like that. I just got rid of statics, used it where possible, and avoided explicit method references for syntactic convenience.

brian Thu 14 Jan 2010

I've found the best technique for simple callbacks is to have a function that takes one parameter, then you can use it-blocks for the simple cases. For example:

verifyErr(ArgErr#) |->| { somethingBad }
verifyErr(ArgErr#) { somethingBad }

helium Thu 14 Jan 2010

Passing methods is IMO still a week point of Fantom.

C#:

class Program
{
    static void xecho(int i)
    {
        Console.WriteLine(i);
    }

    static void Main(string[] args)
    {
        g(xecho);
    }

    static void g(Action<int> m)
    {
        var l = new List<int> { 1, 2, 3, 4, 5, 6 };
        var x = l.FindAll(i => i % 2 == 0);
        foreach (var i in x) { m(i); }
    }
}

You can just call g(xecho) in C# (or this.xecho if it's an instance method).

brian Thu 14 Jan 2010

Passing methods is IMO still a week point of Fantom.

I agree. The fundamental problem is that we don't require the () to call a method. If didn't have that then method would be unambiguous as pass method as function.

But I think b/w the two I'd rather have () be optional.

Something I agree we still need to make easier.

fury Thu 14 Jan 2010

I'm still new to Fantom, but I think that () would be nice to be necessary for function calls. If you just do object.method in Python, you'll get the method object. In my opinion, it looks more readable.

Login or Signup to reply.