Strange typeof behaviour

#1

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

0 Likes

#2

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

0 Likes

#3

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
0 Likes

#4

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.

0 Likes

#5

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.

0 Likes

#6

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

0 Likes

#7

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

1 Like

#8

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

#9

I see!

0 Likes

#10

: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.

0 Likes

#11

Understood :+1:

0 Likes

#12

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.

0 Likes