crystal-lang / crystal

The Crystal Programming Language
https://crystal-lang.org
Apache License 2.0
19.37k stars 1.62k forks source link

[idea] Implicit constructor #5347

Closed anykeyh closed 6 years ago

anykeyh commented 6 years ago

Hello Crystal team !

I'm having some time to spend, and I want to put my hands dirty in the Crystal compiler - for fun, knowledge and community help.

While I've started to give a look, I ask myself about what is frustrating me on the current Crystal language and I think this example comes in my top 3:

x : Int64 = 1 # Meh
y : Float64 = 1 #Meh

Yep. Both of them doesn't compile, and in my opinion it's bad. It's bad because Crystal is made to make the developer happy ( it stole it to Ruby 😄 ), and this small annoying casting make me and probably others developers sad (I know I'm very emotional).

I know, before you tell me, than 1_i64 and 1.0 will works. But I still think this is really verbose.

So I'm just curious to know if this kind of structure sounds interesting or if not, why:

class Float64
  # Implicitly cast any time of integer on assignment
  implicit def self.new(x : Int)
    x.to_f64
  end
end

Adding implicit keyword, usable on initialize and self.new only, which allows the compiler to pick from implicit flagged methods when a wrong type assignment is found during compile time.

This is something currently existing in C++, implicit casting operators. But the control is reversed (the assigned-type take the lead, not the value-type)

This will offer also other (probably dangerous) fun way of writing your code:

# eq of x = StringBuffer.new("helloworld")
x : StringBuffer = "helloworld" # I'm not going to make a lot of friends on this one :D

y = [] of Int64
y << 1
y << 2

I'm just curious about your thought about it.

Cheers,

straight-shoota commented 6 years ago

Your immediate usecase doesn't require any additional language feature. It "just" needs the compiler to defer assigning number literal types depending on their use. This is already tracked in #2995

And I think this should be limited to literals, not arbitrary values. These should need be casted explicitly.

anykeyh commented 6 years ago

I understand, however some simple case cannot be solved just by deferring number assignment:

x : UInt8 = 1
y : UInt16 = x #We still have the problem here?

This can happens a lot in boxing/unboxing algorithms, for example on characters (UTF-8/UTF-16), or pixels colors (e.g. non-floating color mixing ((a * b) >> 8) )

The approach to treat literal as special type looks in my opinion even more complicated to implements.

straight-shoota commented 6 years ago

What about the opposite case:

x : UInt16 = 1
y : UInt8 = x

If x holds a value larger than UInt8::MAX, an implicit cast would cause problems not noticed by the developer. Therefore this should be explicitly casted through .to_u8 to make it clear that the type changes.

RX14 commented 6 years ago

To me this is a duplicate of #2995. I vote to close as a duplicate.

konovod commented 6 years ago

There are use cases for a non-trivial casting\assignment operators. For example, it will make easier to do efficient matrix calculations - right now a = b*c+d means that there will be a lot of allocations (for b*c, then for b*c+d). With implicit conversions, b*c and b*c+d could create a "proxies" so actual evaluation will happen only when such proxy is assigned to a matrix variable. Of course it is possible to do a = (b*c+d).eval or a.assign(b*c+d) , but that doesn't look so cool. That said, the non-trivial casting\assignment operators can create many errors and lead to a cryptic code, i don't think i want to see them in Crystal.

anykeyh commented 6 years ago

If x holds a value larger than UInt8::MAX, an implicit cast would cause problems not noticed by the developer. Therefore this should be explicitly casted through .to_u8 to make it clear that the type changes.

But we would allow implicit casting from 8bits to 16/32/64 but not the inverse.

To me this is a duplicate of #2995. I vote to close as a duplicate.

Well, if my main example cover the same subject, the solution is not the same and the implication extend over the literal assignment system.

@konovod

Indeed that's an example of what's possible with implicit casting. We can also extend this to usage with a vectorization library, with openCL or cuda under the hood, but still writing natural code, and without need of any wrappers.

My opinion on the subject is that Crystal, thanks to its speed, clear syntax and garbage collection, has potential to be used in mathematic-heavy subject, and my experience with numpy or c++ in video game related works tell me it's a language which can be easier to read and allow a more "natural" syntax to write down things than the more common C/C++.

I'm not sure implicit constructor would lead to cryptic code. Monkey patching and the crystal macro system seems to be way more dangerous already 😄.

larubujo commented 6 years ago

implicit means hard to understand and debug. and in crystal not always types in all places, so feature contradictory with whole language. using type anotation like x : UInt8 = ... is not the crystal way. issue should be closed.

straight-shoota commented 6 years ago

@anykeyh

But we would allow implicit casting from 8bits to 16/32/64 but not the inverse.

Having it only one way is possible (and the only thing that would work) but it creates inconsistency. You'd still have to write explicit type conversions for downcasting. So it's still a thing a developer needs to care about.

ysbaddaden commented 6 years ago

Duplicate of #2995 which has been accepted —thought restricted to casting untyped number literals only.

straight-shoota commented 6 years ago

I am a bit disappointed how issues like this are handled.

Here, we have a user who want's to dedicate work to Crystal development. He proposed an idea he'd like to see put forward. This issue proposes a language feature that's suitable as an alternative to #2995 but also has other applications. Treating this as a duplicate seems not very accurate. It ignores a central aspect of the idea - TS has already expressed this. Closing this shortspoken as a duplicate to me feels ignorant and neglects the effort that was put into this.

As I have expressed above, I am not a supporter of the idea and I'm totally ok with closing it, but I don't like to see it dressed down like this. It wouldn't hurt to make the reasoning more comprehensible and see not to scare off potential new contributers.

Wouldn't it sound much friendlier if the issue was closed with something like this?

Thank you for your proposal, but it does not feel like Crystal should go this way. The use case for literals will be fixed by #2995 which has been accepted (in this regard it could be considered a duplicate) and the implicit casting of arbitrary values is not considered good practice in Crystal (for example). It would be most welcome if you could dedicate work to Crystal and please keep coming with new ideas to improve the language.

This would give more explanation, appreciate the effort that was put into this and express encouragement to keep working on Crystal and new ideas. I'm sure it is not intended to sound rude, but it could easily percieved as such, especially for newcomers who may not familiar with Crystal's goals, previous discussions, community procedures etc. Let's try to have a more welcoming atmosphere.