Open asterite opened 3 years ago
Another option: keep type
, but give an error if the underlying type is not Void*
. Then we can document it as just being a short syntax for a struct with a single member.
Yet another option: keep type
, but document it and make it work like a wrapper struct. Right now type
has a few special rules in the language which I can't even remember, but making it work like a wrapper struct is probably better.
Adding a macro newtype
similar to Haskell could work. IIRC newtype can have only one type argument, they are type-safe way to give a new meaning to an existing type.
newtype Temperature = Float32
newtype Length = Float32 do
def +(other : Length)
# ...
end
end
We could standardize the conversion in both directions inside the macro and it would be able to work inside and outside lib
.
Yet another option, probably my favorite: you can use type
, but you get a compilation error saying to use a wrapper struct (with example code). If you run crystal tool format
it changes type
to those wrapper structs so the fix is really simple.
Eventually for Crystal 1.1 or 2.0 we can remove it.
That way we remove it, but we still provide a really easy upgrade path.
Oh, yeah, newtype
could be another option.
That said, it seems in Haskell you have data
and newtype
, and the only difference is performance and something related to bottom. In Crystal you can already do newtype
: it's record
with just one field. So that could be another option (I wouldn't introduce newtype
if there's no difference with record
with just one field).
I don't think those problems are much of a deal.
Yes, it's another concept but you only have to learn it if you write C bindings. That's most likely a minority of developers. Most probably don't care about lib
and what goes on inside. If you do care, you need to learn some new concepts. The semantics of type
aren't hard to learn if your familiar with C types.
Sure, it could be improved and some of the ideas here sound good. But unless there was something really broken, I wouldn't make it a priority. It works and keeping it the way it is has not much cost.
The thing is, using type with a Proc type works, and if you call new on it you get a Proc type which is counter intuitive (you don't get the type type). I wanted to make Proc.new be defined in the standard library instead of the compiler, but the above breaks. I guess it's fine? It's a breaking change, but type is used the wrong way there (it should be an alias instead).
(removing type doesn't make it a breaking change because, well, type is gone)
Please, ... this will break all the bindings in the wild. We don't need that.
type Foo = Void*
as a feature is needed. The syntax is non-intrusive. struct
is a workaround. If you want to allow only Void\**
there, maybe that could work.
Sounds good. I guess I won't be making the changes I had in mind then.
@asterite Seems you've wanted to that a long long time ago: https://github.com/crystal-lang/crystal/issues/4682#issuecomment-328349647
Reading the comments there I better understand that type
as a feature is really unnecessary and eventually should be removed.
I think another alternative is to keep type but not make it special anymore: it's just a wrapper struct. I will try that some time later.
type
vs alias
confused the heck out of me for a long time, particular because I was using crystal-lib to generate bindings and it would generate all of type Foo = Void*
, alias bar : LibC::Int
, and type Baz = SomeStructDeclaredEarlier
. Of course, the generator is not perfect, so it would occasionally miss some, and in order to fix it I'd be pulling my hair out trying to understand which one was the "right" one I needed to add back in. Add to that that I was new to crystal and couldn't really understand the difference between alias
and type
. For me personally, removing type would have reduced some cognitive load.
If we really needed a dedicated keyword, I think type
is too generic and is part of the confusion... newtype
is good or perhaps even opaque
, opaque_type
, new_opaque_type
.
Inside a
lib
declaration you can usetype
similar to an alias but making its own type:The idea behind this is using
type
when the C library exposes an opaque type whose structure is not known, hidden behind a pointer, but where you still want to consider it a different type, not always equal toVoid*
.The problem with the existing
type
is that:alias
, but differenttype
with types other thanVoid*
but it doesn't work quite well. For example you can dotype Foo = Int32
but methods on it don't work, which doesn't make a lot of senseBut the most important point is that
type
can be easily done right now: using a struct.I know, it's a bit more typing, but it's just for C libraries.
Alternatively, we can keep
type
but remove its "value" part. That is, you writetype Foo
and that's it, and it's assumed that it's aVoid*
internally. Or we could allow macros to be invoked insidelib
declarations and introduce a macro for defining a wrapper struct, so you can writewrapper_struct Foo, Pointer(Void)
or similar.