JuliaLang / julia

The Julia Programming Language
https://julialang.org/
MIT License
45.64k stars 5.48k forks source link

Wouldn't it be nice if anonymous functions inside type def are considered inner constructor? #8502

Closed tonyhffong closed 10 years ago

tonyhffong commented 10 years ago

What I have in mind was:

type MyType
    a::Int
    function( x::Int )
        new(x)
    end
end

I already declare the type name above, why do I have to repeat myself inside?

StefanKarpinski commented 10 years ago

That's actually a rather nice idea.

rfourquet commented 10 years ago

+1, I never liked (since c++) this redundance, which is error prone (particularly at the REPL when types are renamed). I wouldn't mind a more explicit name though, like __init__ (one line form: __init__(x) = new(x) vs x->new(x)).

StefanKarpinski commented 10 years ago

The real advantage is that it avoids the fact that for parametric types defining methods for the type name inside the type block means something different than it does outside. This could make it less confusing.

jakebolewski commented 10 years ago

Why can't new take the type it's constructing as the first argument?

MyType(x::Int) = new(MyType, x)

Wouldn't that remove the need for inner constructors?

I guess losing the inner / outer constructor concept for types would lead to some increased verbosity (as you would have to define all constructors) but it seems conceptually simpler and would lead to more explicit code.

StefanKarpinski commented 10 years ago

Also not a bad idea.

StefanKarpinski commented 10 years ago

One big advantage of scoping new specifically to the type block is that it gives some control over how objects of a given type can be constructed since only code in the type block can do the construction.

ivarne commented 10 years ago

It will also conflict with the current approach with two default constructors (one converting) when no inner constructor is defined.

jakebolewski commented 10 years ago

That is an advantage (currently) but I feel that the concept of restricting extensibility is going to come up in the future for generic methods as well. This way you could unify the concepts of extensibility between type constructors and methods as the two would be exactly the same.

Right now type construction is special cased, it is really the only place in Julia that is not "open by default". If I want to extend or modify how a type (with an inner constructor) is constructed I have to go in and edit the source code.

@ivarne I addressed that point, although I feel the only times you should use inner constructors is when you want to opt out of the default behavior.

JeffBezanson commented 10 years ago

I personally like that there is at least one thing in the language that lets you enforce invariants.

simonster commented 10 years ago

Another reason to keep new restricted to the type block is that otherwise "identify types (especially immutable) that are always fully initialized, to avoid undefined checks on their fields" from #3440 doesn't seem so tractable.

JeffBezanson commented 10 years ago

Going back to the issue topic, using this syntax for constructors is a nice idea, I agree. The biggest problem at this point is probably that allowing both is kind of confusing.

StefanKarpinski commented 10 years ago

We could phase out the current way and make this the new hotness.

JeffBezanson commented 10 years ago

In favor of the current approach, I would say

JeffBezanson commented 10 years ago

Oh and also you can't use MyType(x) = new(x) syntax, which is kind of a problem.

simonster commented 10 years ago

I'm not really in favor of changing things given the other points, but I think the similarity between inner and outer constructors may actually be a point of confusion for parametrized types.

JeffBezanson commented 10 years ago

See #8135 for some thoughts on changes to inner constructors. I hope there is something we can do to improve the situation.

tpapastylianou commented 7 years ago

Would this kind of change still easily allow the use of secondary inner constructors? (i.e. an inner constructor that makes use of the primary inner constructor, but still needs access to the type block, such that it couldn't simply be replaced with an outer constructor)

i.e. can this:

type Foo
  x::Int
  y::Int

  function Foo(arg::SomeSpecialisedType)   # primary constructor
    // do something complicated involving x and y 
  end

  function Foo(arg::SomeOtherSpecialisedType) # secondary constructor
    // do something complicated involving x, y, and the primary constructor
  end
end

be replaced to this:

type Foo
  x::Int
  y::Int

  PrimaryConstructor = function(arg::SomeSpecialisedType)
    // do something complicated involving x and y 
  end

  SecondaryConstructor = function(arg::SomeOtherSpecialisedType)
    // do something complicated involving x, y, and the PrimaryConstructor variable
  end
end

To me these are very different. Not least because this makes SecondaryConstructor a closure which carries PrimaryConstructor along with it as a callable field (right?)