New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Value types are not cast to Cell<T> when passed to functions that take Object parameter #803
Comments
This is an interesting idea. We've always had problems with class vs cover in ooc, because of the fact that everything inherits from Object, even value types. However, I don't think Object is intended to be used that way in ooc, when generics can do the job fine. |
If it is not intended to be used this way then the last line in the example should create a compile error, not a runtime error. You are right that a generic function works in this case. |
What I mean by this is that Object is not intended to be used for boxing/unboxing. Perhaps the fact that covers inherit from Object is a mistake but the fact of the matter is that they are mostly designed and used to cover C types and many C libraries take an OO approach, which makes their types fit into the ooc typesystem really well, thanks to this. Compound covers are quite unstable as of right now, I will try to figure something out to make them fit into the system more (perhaps passing a pointer implicitly when Object is expected, although this sounds like a bad idea) or at least implement plenty of warnings. |
So, after reading this: http://fasterthanlime.com/blog/2015/ooc-generics-and-flawed-designs/ I tried write: func <T> (data: T) {
(match (data) {
case value: Int =>
value toString()
case cell: Cell<Int> =>
cell get() toString()
case =>
"FAIL"
}) println()
}
// This works
write(Cell new(2))
// This also works
write(2) I'm pretty sure this solves my problem, as in, it lets me do what I want to do, I just didn't think of doing it that way. I'm still not sure I think the fact the first version builds, but crashes when run, isn't an error. |
@davidhesselbom you might want to have a suffixed function for the Int case, so that dispatch happens at compile time |
Are you suggesting I avoid generic parameters altogether? I can have a suffixed function for every imaginable case, but then, what's the point of having generics? (I always found them to be confusing and strange, myself, heh.) |
@davidhesselbom If the type is known at compile-time then suffixes functions can be used for dispatch and it's more efficient. If it isn't know (e.g. you're dealing with a List or another generic collection) then you can use a generic function to do the dispatching at runtime yourself. Of course if for example you have a class hierarchy like:
And then |
I know it's confusing sometimes, but really, generics were introduced mainly for collections. If you think of them like that, then it makes a lot more sense. |
True, it's more efficient. For some reason I thought you were saying I should avoid generic parameters, period :) |
Generics are just a means to an end, for ooc it's mostly collections, for other languages it might solve a lot more. It's okay. It's not a silver bullet :) |
As long as the collections don't should contain value-types, or? (With the exception of when the length of the value type coincides with the length of a pointer.)---- On må, 19 jan 2015 06:12:15 -0800 Amos Wengernotifications@github.com wrote ----Generics are just a means to an end, for ooc it's mostly collections, for other languages it might solve a lot more. It's okay. It's not a silver bullet :)—Reply to this email directly or view it on GitHub. |
@simonmika I'm having trouble reading that sentence, but if you're wondering whether generics only works on types which size are exactly a pointer size, you're mistaken. Here's an example: import structs/ArrayList
ABigStruct: cover {
a, b, c, d: Double
toString: func -> String {
[a, b, c, d] as ArrayList<Double> map(|x| x toString()) join(", ")
}
}
main: func {
"ABS size : #{ABigStruct size}" println()
"ABS instanceSize : #{ABigStruct instanceSize}" println()
list := ArrayList<ABigStruct> new()
list add((1, 2, 3, 4) as ABigStruct)
list add((2, 4, 6, 8) as ABigStruct)
list add((9, 7, 5, 3) as ABigStruct)
list each(|el|
el toString() println()
)
} Output:
That's 32 bytes, so 256 bits. Much wider than a pointer anywhere that I know of :) |
For comparison's sake here's the same example with a class: import structs/ArrayList
ABigClass: class {
a, b, c, d: Double
init: func (=a, =b, =c, =d) {}
toString: func -> String {
[a, b, c, d] as ArrayList<Double> map(|x| x toString()) join(", ")
}
}
main: func {
"ABC size : #{ABigClass size}" println()
"ABC instanceSize : #{ABigClass instanceSize}" println()
list := ArrayList<ABigClass> new()
list add(ABigClass new(1, 2, 3, 4))
list add(ABigClass new(2, 4, 6, 8))
list add(ABigClass new(9, 7, 5, 3))
list each(|el|
el toString() println()
)
} Output:
Because classes are by-reference, Everything clear? |
Sure, it was a bug. Fixed now |
Boxing and unboxing
shouldcould be transparent to the programmer so that any value type can be implicitly casted to an object and explicitly cast from an object.The Cell class should be very well suited for this.
The last call should either not compile (if it is decided that value types cannot be passed as objects) or the
2
should be cast to, in this case,Cell<Int>
.This is called boxing and unboxing in e.g. C#: http://msdn.microsoft.com/en-us/library/yz2be5wk.aspx
The text was updated successfully, but these errors were encountered: