Strange typeof behaviour

Hi All,

Can anyone explain this behaviour?

struct Foo
  function Foo(f)
    foo = new()
    (foo)(x) = f(x)
    return foo
  end
end
foo = Foo(x->5)
@show typeof(foo)

struct Bar
  function Bar(f)
    bar = new()
    return bar
  end
end
bar = Bar(x->5)
@show typeof(bar)

I get, paraphrasing:

typeof(foo) = getfield(Main, Symbol("#foo#3")){getfield(Main, Symbol("##4#5"))}
typeof(bar) = Bar

I’d have thought that foo would be of type Foo.

Running Julia 1.0

Your Foo constructor doesn’t seem to construct a Foo object.

Sometimes looking at @code_lowered can help you understand what is going on -

julia> @code_lowered Foo(x->5)
CodeInfo(
1 ─      foo = %new(Main.Foo)
│   %2 = Main.:(#foo#7)
│   %3 = (Core.typeof)(f)
│   %4 = (Core.apply_type)(%2, %3)
│        foo = %new(%4, f)
└──      return foo
)

Note the presence of two calls to new; the first one constructs a Foo struct and the second one constructs something else (I have to admit, I’ve no idea what).

It looks to me that you are trying to achieve something like

julia> struct Foo
           f
       end

julia> (foo::Foo)(x...) = foo.f(x...)

julia> foo = Foo(x -> 5x)
Foo(getfield(Main, Symbol("##7#8"))())

julia> foo(3)
15

though it is probably better to parameterise on the function type if you are more concerned about runtime speed over compile times, i.e.,

julia> struct Foo{F}
           f::F
       end

That is how I’m currently implementing my working version but was thinking about how to do away with holding f on the struct. Hence my inner constructor on Foo.

How is your struct going to know what function to call if it doesn’t hold onto it? It kinda looks like you’re meaning to write (::typeof(foo))(x) = 5x — but that’s not what you want here. That’ll mean that calling every instance of foo will result in 5x, so you definitely don’t want to do that in a constructor.

Is it not? I thought I’d be constructing a Foo object with new. I must be missing something basic.

I was hoping that the function would be bound to the object via magic, to be honest.

1 Like

Your Foo constructor is effectively saying:

  function Foo(f)
    foo = new()
    foo = x -> f(x)
    return foo
  end

It’s rebinding foo such that it’s an anonymous function.

1 Like

I see!

:smiley:

Just remember that Julia dispatches to functions based on their types, not their values. So you can’t define a function (even a call overload) to dispatch on a particular value like you had hoped.

Understood :+1:

In global scope defining a function foo or any const binding when foo already had a value would be an error, but we don’t have const in local scope so it’s allowed.