Nice post! I'm happy that the API keeps looking better and better.
I'd like that _expr to be somehow exposed only in-package, but not exposed to the public API
You can define it like protected def _expr and it will do exactly that. protected in Crystal works different than in Ruby: only the same type or types in the same namespace (module) have access to them: crystal-lang.org/reference/1.3/syn...
I'm especially unclear on type conversions if left side is an integer-like type
You will have to define overloads on Int32 and so on. In the end if you want to define + for types A and B you will need to define A#+(B) and B#+(A). It's not ideal, but it works. That said, I also noticed that there's a coerce method in Ruby but I never understood how it works. Maybe we could have a similar thing in Crystal, not sure!
chomp
Like in Ruby, when you do puts, if the string ends with a newline it won't show up in the output. And Crystal does the same thing:
Like in Ruby, when you do puts, if the string ends with a newline it won't show up in the output. And Crystal does the same thing:
OK, check out this code:
class Foo
def as_string
"A\nB\n\C\n"
end
def to_s(io)
io << as_string
end
end
a = Foo.new
puts "One:"
puts a
puts "Two:"
puts a.to_s
puts "Done"
I'd expect puts a and puts a.to_s to do the exact same thing, but they don't, puts a outputs one extra newline. Output of this script is:
One:
A
B
C
Two:
A
B
C
Done
That's definitely unexpected, and I don't think there's any way for puts a and puts a.to_s to do different thing in Ruby.
That said, I also noticed that there's a coerce method in Ruby but I never understood how it works. Maybe we could have a similar thing in Crystal, not sure!
After I tried it a bit more, I don't think Crystal needs #coerce. Ruby doesn't have methods dispatching on both argument types, and #coerce is a runtime hooks to emulate it for math-like stuff. If you do aFloat * aMatrix or whatnot, Float#* will just see that it has no idea what aMatrix is, and fallback to Matrix#coerce to figure it out (or raises exception if there's no #coerce on its argument).
In Crystal you'd just define new method Float#*(other : Matrix) without having to redefine the original Float#* (and massive performance cost that would imply).
I don't know if there are any scenarios where that's not sufficient.
Good thing to hear that coerce won't be needed! I think Julia has something similar, mainly to avoid having to do all possible combinations. But maybe not having coerce is simpler to understand.
Regarding the extra newline, the rule about not printing the last newline of a string is exclusive to printing strings. We copied that from Ruby but apparently we didn't think it well. I think Ruby does it like that because if you do puts gets it's a bit ugly to see two newlines. You'd have to call chomp, or pass chomp to gets. But in Crystal we decided that gets (and others) chomp by default o, so this exceptional newline behavior is less needed, or not needed at all. Unfortunately we decided to chomp by default after that exceptional behavior was in place, and with 1.0 it would be hard to change because it's a breaking change. But maybe it's not such a big deal! I'll open a discussion about that.
It would be best if puts worked like in Ruby, and never did extra newline, even if something to_ses with a newline at the end.
Basically the algorithm would need to be something like:
def puts(obj)
obj.to_s(io)
io << "\n" unless io.ends_with?("\n")
end
Objects to_sing to something multiline isn't that unusual, and that last \n can't always be avoided.
If that's not really doable, then that's a worse API due to this irregularity, but I guess it's not a huge issue, and every language has some irregularities.
For further actions, you may consider blocking this person and/or reporting abuse
We're a place where coders share, stay up-to-date and grow their careers.
Nice post! I'm happy that the API keeps looking better and better.
You can define it like
protected def _expr
and it will do exactly that.protected
in Crystal works different than in Ruby: only the same type or types in the same namespace (module) have access to them: crystal-lang.org/reference/1.3/syn...You will have to define overloads on
Int32
and so on. In the end if you want to define+
for typesA
andB
you will need to defineA#+(B)
andB#+(A)
. It's not ideal, but it works. That said, I also noticed that there's acoerce
method in Ruby but I never understood how it works. Maybe we could have a similar thing in Crystal, not sure!Like in Ruby, when you do
puts
, if the string ends with a newline it won't show up in the output. And Crystal does the same thing:That said, I don't know why
model_to_string
returns a newline, it sounds counterintuitive.OK, check out this code:
I'd expect
puts a
andputs a.to_s
to do the exact same thing, but they don't,puts a
outputs one extra newline. Output of this script is:That's definitely unexpected, and I don't think there's any way for
puts a
andputs a.to_s
to do different thing in Ruby.After I tried it a bit more, I don't think Crystal needs
#coerce
. Ruby doesn't have methods dispatching on both argument types, and#coerce
is a runtime hooks to emulate it for math-like stuff. If you doaFloat * aMatrix
or whatnot,Float#*
will just see that it has no idea whataMatrix
is, and fallback toMatrix#coerce
to figure it out (or raises exception if there's no#coerce
on its argument).In Crystal you'd just define new method
Float#*(other : Matrix)
without having to redefine the originalFloat#*
(and massive performance cost that would imply).I don't know if there are any scenarios where that's not sufficient.
Good thing to hear that coerce won't be needed! I think Julia has something similar, mainly to avoid having to do all possible combinations. But maybe not having coerce is simpler to understand.
Regarding the extra newline, the rule about not printing the last newline of a string is exclusive to printing strings. We copied that from Ruby but apparently we didn't think it well. I think Ruby does it like that because if you do
puts gets
it's a bit ugly to see two newlines. You'd have to call chomp, or pass chomp to gets. But in Crystal we decided that gets (and others) chomp by default o, so this exceptional newline behavior is less needed, or not needed at all. Unfortunately we decided to chomp by default after that exceptional behavior was in place, and with 1.0 it would be hard to change because it's a breaking change. But maybe it's not such a big deal! I'll open a discussion about that.It would be best if
puts
worked like in Ruby, and never did extra newline, even if somethingto_s
es with a newline at the end.Basically the algorithm would need to be something like:
Objects
to_s
ing to something multiline isn't that unusual, and that last\n
can't always be avoided.If that's not really doable, then that's a worse API due to this irregularity, but I guess it's not a huge issue, and every language has some irregularities.