Skip to content
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

typedef for simple type aliases #2626

Closed
rakudrama opened this issue Apr 18, 2012 · 47 comments
Closed

typedef for simple type aliases #2626

rakudrama opened this issue Apr 18, 2012 · 47 comments

Comments

@rakudrama
Copy link
Member

@rakudrama rakudrama commented Apr 18, 2012

In the dom library there are several types which are sum (union) types.

IDBKey and SerializedScriptValue are two examples - both include numbers, strings and JavaScript arrays (Lists) among other things, and neither includes functions or arbitrary user defined types.

We are forced to use Dynamic for these types.
If typedef could do a simple renaming we could generate more readable interfaces.

e.g.

interface IDBFactory {
   int cmp(Dynamic first, Dynamic second);
}

could become

/** Documentation about IDBKey in one place. */
typedef IDBKey = Dynamic;

interface IDBFactory {
   int cmp(IDBKey first, IDBKey second);   // Tools can navigate to the definition.
}
@gbracha

This comment has been minimized.

Copy link
Contributor

@gbracha gbracha commented Apr 18, 2012

Clearly typedefs should allow type aliasing for all types. I hope we can fix this soon.


Set owner to @gbracha.
Added Accepted label.

@gbracha

This comment has been minimized.

Copy link
Contributor

@gbracha gbracha commented Apr 18, 2012

See also bug 84.

@gbracha

This comment has been minimized.

Copy link
Contributor

@gbracha gbracha commented May 24, 2012

Added this to the Later milestone.

@rakudrama

This comment has been minimized.

Copy link
Member Author

@rakudrama rakudrama commented Oct 29, 2012

See Issue #6292 for another example.
Lack of simple typedef continues to be an impediment.


cc @larsbak.

@DartBot

This comment has been minimized.

Copy link

@DartBot DartBot commented May 4, 2013

This comment was originally written by george.moscho...@gmail.com


Any ETA wrt type aliasing for all types? Will this make 1.0?

@kasperl

This comment has been minimized.

Copy link
Contributor

@kasperl kasperl commented Jul 10, 2014

Removed this from the Later milestone.
Added Oldschool-Milestone-Later label.

@kasperl

This comment has been minimized.

Copy link
Contributor

@kasperl kasperl commented Aug 4, 2014

Removed Oldschool-Milestone-Later label.

@rkj

This comment has been minimized.

Copy link

@rkj rkj commented Oct 29, 2015

Any updates? Will it be ever possible? We have some long classes names that are really awkward to use (to the point where it's impossible to fit the case statement on single line), and aliasing them to an abbreviation would greatly help.

@korsvanloon

This comment has been minimized.

Copy link

@korsvanloon korsvanloon commented Feb 16, 2016

I could really use typedefs for aliasing my long generic classes like so:

class Something<A,B,C,D,E> { }

typedef Something<Foo, Bar, Baz, Qux, Quux> SomethingFoo;
@RSSchermer

This comment has been minimized.

Copy link

@RSSchermer RSSchermer commented Feb 16, 2016

Also perhaps for future consideration: if the Dart team/committee ever decides to add union types to the language (#4938) I think arbitrary type aliasing would be very useful for dealing with the unwieldy types this might produce.

My current use case concerns vertex attribute types (for WebGL rendering). If Dart were to allow something like this I'd be a very happy Dartisan:

typedef VertexAttribute = double | Vector2 | Vector3 | Vector4 | Matrix2 | Matrix3 | Matrix4;
@eernstg

This comment has been minimized.

Copy link
Member

@eernstg eernstg commented Feb 16, 2016

Thanks for the input! I'm on the Dart team, and I think we'll have union types and maybe the generalized typedefs as well (and I also want them), but it's always a much more involved process to change the language than you'd think. Even though I can't promise you that they will be added anytime soon, your input helps keeping the priority of these features in place.

@kevmoo kevmoo added P2 type-enhancement and removed accepted labels Feb 29, 2016
@tmst

This comment has been minimized.

Copy link

@tmst tmst commented Oct 22, 2016

Just wondering, would this enhancement allow the following construct?

class Foo {}

void main() {
var FooClass = Foo;
foo = new FooClass();
}

@eernstg

This comment has been minimized.

Copy link
Member

@eernstg eernstg commented Oct 24, 2016

No, being able to access constructors via a value of type Type is a reflective operation ('dart:mirrors' and package reflectable will allow you to do such a thing via reflectType and ClassMirror.newInstance), and the feature discussed in this issue does not provide a way to allow a typedef to provide similar functionality. However, depending on the context, you might be able to pass around a factory function rather than the Type itself, and the factory function could carry some type information:

class Foo {}
class Foo2 extends Foo {}

typedef Foo FooFactory();

void main() {
  FooFactory myFooFactory = () => new Foo2();
  Foo foo = myFooFactory();
}
@eseidelGoogle

This comment has been minimized.

Copy link

@eseidelGoogle eseidelGoogle commented Dec 2, 2016

I suspect the importance of this bug is going to rise with strong_mode (@leafpetersen) since longer types are going to be more common, and folks are going to want to shorten them (as per the ask in #2626 (comment)).

@eseidelGoogle

This comment has been minimized.

Copy link

@eseidelGoogle eseidelGoogle commented Dec 13, 2016

I'm told that #27527 is viewed as a precursor to this more general issue and is under evaluation currently.

@aemino

This comment has been minimized.

Copy link

@aemino aemino commented Aug 30, 2017

Is this still being considered? Personally, I view union types and typedefs that are used for more than just functions to be almost imperative to Dart. I find myself in so many situations where dynamic could be used, but union typedefs would be a more concrete and specific way to define a type.

@leafpetersen

This comment has been minimized.

Copy link
Member

@leafpetersen leafpetersen commented Aug 30, 2017

This is not planned for Dart 2.0. It is still on the list of things that we would like to consider for future releases.

@lrhn lrhn added core-a and removed P2 labels Dec 6, 2018
@stt106

This comment has been minimized.

Copy link

@stt106 stt106 commented Dec 9, 2018

Just to add, adding type alias is going to make dart compile time even safer, just because two values of the same type doesn't mean they should be compared or assigned to each other. This is one of the primary reasons why many languages like Go, F# allow type alias. A naive example would be a function that does conversion between Celsius and Fahrenheit. Naturally people want to define both of them as double but then it's very easy to unintentionally mix up the two since they're both just doubles.

If allowing type alias, one can easily do
type Celsius double // or a similar syntax
type Fahrenheit double
Fahrenheit CToF(Cesius c) => Fahrenheit(c*9/5 + 32);

@lrhn lrhn added this to Non-Breaking And Simple in Language Enhancement Categories Dec 14, 2018
@zhengxiaoyao0716

This comment has been minimized.

Copy link
Contributor

@zhengxiaoyao0716 zhengxiaoyao0716 commented Feb 16, 2019

Maybe we don't need to use type or typedef for type alias?
In my opinion, since Dart support the syntax like class =, it is a better way to implements type alias.

For the example that @stt106 gived, maybe we can use class = to implements it like this:

class Celsius = double;
class Fahrenheit = double;
Fahrenheit cToF(Celsius c) => Fahrenheit((c as double) * 9 / 5 + 32);

I thinks that the above syntax is easier to understand in Dart, because of we can simply simulate it now!
Here my "polyfill":

import 'package:test_api/test_api.dart';

class Celsius = Alias<double> with Type;
class Fahrenheit = Alias<double> with Type;
Fahrenheit cToF(Celsius c) => Fahrenheit(c.as(double) * 9 / 5 + 32);

class Alias<T> {
  final T _value;

  const Alias(T value) : _value = value;
  Alias.of(Alias<T> other) : this(other._value);

  T /*operator*/ as(Type type) {
    if (type == _value.runtimeType) return _value;
    throw _CastError(runtimeType, type);
  }

  @override
  String toString() {
    return _value.toString();
  }

  @override
  bool operator ==(other) =>
      other.runtimeType == this.runtimeType && other._value == _value;
  @override
  int get hashCode => _value.hashCode;
}

class _CastError extends CastError {
  final Object message;
  _CastError(Type alias, Type expected)
      : message = "alias '$alias' is not type '$expected' in type cast";
  @override
  String toString() => message;
}

void main() {
  test('playground', () async {
    var celsius = Celsius(30.0);
    print('Celsius: $celsius');

    var fahrenheit = cToF(celsius);
    print('Fahrenheit: $fahrenheit');

    expect(fahrenheit.as(double), 86.0);
  });
}
@rrousselGit

This comment has been minimized.

Copy link

@rrousselGit rrousselGit commented Feb 16, 2019

I disagree.

class Foo = Bar with Mixin syntax is used to create a new type. Such that Foo != Bar

But i think that a class Foo = Bar can be interesting.

This especially matters when we take the extension methods proposal in consideration.

@eernstg

This comment has been minimized.

Copy link
Member

@eernstg eernstg commented Feb 18, 2019

This is now happening. As of 02bb437 (Jan 11, 2019) it is part of the language specification that you can write type aliases like typedef C = D<int>; and typedef D<X extends num> = Map<X, List<X>>;, etc.

There's an implementation plan. Right now some other things are being pushed harder than this feature, but it's accepted and in the pipeline.

Note that you can not use this feature to create new types (similar to Haskell's newtype or Pascal's type Temperature = Integer;), it creates an additional notation for an existing type. So if you declare typedef D = C; then it is type correct to have things like D d = C();.

It is for abbreviation and consistency, not for "branding" of types (that is, creating a "copy" of a type that has the same underlying properties and representation, but which is considered during type checking to be a different type). Type aliases wouldn't fit very well for type branding, anyway, because they have always been specified to create a new way to write an existing type rather than creating a new type.

We did have some discussions about type branding (e.g., I wrote dart-lang/linter#1264 at some point in response to such discussions), but that discussion should be taken separately.

So I think we can close this issue now (before it turns 7 ;-). @rakudrama, do you agree?

@atreeon

This comment has been minimized.

Copy link

@atreeon atreeon commented Feb 20, 2019

Is this functionality in a version of dart in which we can use? I'm using 2.2.0-dev.1.1
'Error: Can't create typedef from non-function type.'
typedef X = List<int>;

@eernstg

This comment has been minimized.

Copy link
Member

@eernstg eernstg commented Feb 20, 2019

It is not yet implemented, and it won't make it into Dart 2.2, but it is coming. Also, there is no bleeding-edge version of the tools that have it yet, so you have to find a tiny extra bit of patience somewhere. ;-)

I suggested closing this issue because it's about the language—the implementation process would be associated with the implementation plan and its associated issues.

@atreeon

This comment has been minimized.

Copy link

@atreeon atreeon commented Feb 20, 2019

ok thanks, I'll keep an eye on the changelog in future! (I'm terrible during the buildup to christmas too ;-) The feature looks great!
btw, will you be able to alias just a simple type like int. I thought this might help with documenting return types from functions
typedef userId = int;

@eernstg

This comment has been minimized.

Copy link
Member

@eernstg eernstg commented Feb 21, 2019

@twistedinferno wrote:

will you be able to alias just a simple type like int. I thought this might
help with documenting return types

A type alias like typedef userId = int; is certainly possible with this feature. I guess there will be a need to develop good software engineering practices around such things in order to ensure that it is actually helpful, but that's of course true for many language features.

@rayfarias56

This comment has been minimized.

Copy link

@rayfarias56 rayfarias56 commented Jun 26, 2019

So I believe this issue recently turned 7. Is there any update on the implementation progress? It seemed that this was so close to being implemented about 4 months ago.

@eernstg

This comment has been minimized.

Copy link
Member

@eernstg eernstg commented Jun 27, 2019

Right, it's still very close. ;-)

There is some work on it too, e.g., here's a test: https://github.com/dart-lang/co19/blob/master/Language/Types/Type_Aliases/syntax_t02.dart.

Sorry about the delay, but other features (e.g., NNBD) have a very high priority, and this one is in the pipeline now.

@DanielBiegler

This comment has been minimized.

Copy link

@DanielBiegler DanielBiegler commented Sep 8, 2019

Just found this issue since I also was looking for this feature. I see the last commit from here is over 3 months old.

But I still see some issues about NNBDs, so I guess it might take some time still. Is there something new that could be said about this? 😄 Cheers!

@eernstg

This comment has been minimized.

Copy link
Member

@eernstg eernstg commented Sep 9, 2019

Yes, NNBD and static extension methods are taking up the resources now. So, unfortunately, we're close, but we aren't actively getting any closer at this point. Thanks for pushing the issue, however: The only way to get it into the implementation pipeline is that there is a perceived need.

@DoubleHub

This comment has been minimized.

Copy link

@DoubleHub DoubleHub commented Sep 13, 2019

I need this. I wonder why it wasn't included in the language from the start... I often use type aliases to simplify complicated variable types while still maintaining 100% safety! It's a practice that carried over from C++

@antekone

This comment has been minimized.

Copy link

@antekone antekone commented Sep 15, 2019

Any chances of implementing this feature?

@leafpetersen

This comment has been minimized.

Copy link
Member

@leafpetersen leafpetersen commented Sep 24, 2019

This is still planned to be implemented, but it's lower on the priority list right now. There's a strong feeling on the team that we need to focus on landing any breaking features (like NNBD) now, since they will just get harder to land as time goes on, and also a desire to prioritize the higher impact features (like extension methods) above smaller features. We still think this is a great feature and look forward to landing it when we can find a good slot for it!

@GAM3RG33K

This comment has been minimized.

Copy link

@GAM3RG33K GAM3RG33K commented Oct 7, 2019

is this feature still in the development pipeline? I need this really bad. I want to use it to map 'Map<String, dynamic>' to 'JsonObject'.

@DoubleHub

This comment has been minimized.

Copy link

@DoubleHub DoubleHub commented Oct 8, 2019

is this feature still in the development pipeline? I need this really bad. I want to use it to map 'Map<String, dynamic>' to 'JsonObject'.

Tbh they should just give us an alias like that built-in. In my projects I mainly use Map<String, dynamic> for JSON objects, rarely for other stuff

@vaind vaind mentioned this issue Oct 9, 2019
@mindplay-dk

This comment has been minimized.

Copy link

@mindplay-dk mindplay-dk commented Nov 10, 2019

Just wondering, is it really a good idea to overload the typedef keyword with a different feature?

EDIT: it's probably fine, see notes below.

As far as my understanding, type aliases are quite a different thing from type definitions - since a type-definition declares a new type, whereas a type alias merely declares an alias for an existing type, right?

So if typedef worked consistently with the function types you can declare today, which are new types, I would expect e.g. typedef UserId = int to also generate a new type, e.g. UserId would be assignable to int, but int would not be assignable to UserId without an explicit type-cast or assertion of some sort. (This is useful for things like IDs which aren't just any numbers, or e.g. typedef HexColorCode = string, which is a string that requires a validating constructor, so you can statically guarantee that a HexColorCode is not just any string, but a string with a specific length and format.)

As opposed to type aliases, which type-check more like interfaces - e.g. in TypeScript, a declaration such as type HexColorCode = string doesn't declare a new type, but just an alias for an existing type. So a HexColorCode is assignable to a string, and vice-versa. (This has it's own set of use-cases - often just to avoid repeating the same complex type-expression over and over, which can make a codebase more difficult to refactor.)

I think both of these features are useful - but if these new typedef = declarations are just for declaring type aliases, I would suggest introducing a dedicated keyword like type for that, to avoid any confusion about a given typedef generating a new type or just a type alias. This would also make it possible to overload typedef in the future with a similar feature that does generate unique types.

Thoughts?

@GAM3RG33K

This comment has been minimized.

Copy link

@GAM3RG33K GAM3RG33K commented Nov 10, 2019

Just wondering, is it really a good idea to overload the typedef keyword with a different feature?

As far as my understanding, type aliases are quite a different thing from type definitions - since a type-definition declares a new type, whereas a type alias merely declares an alias for an existing type, right?

So if typedef worked consistently with the function types you can declare today, which are new types, I would expect e.g. typedef UserId = int to also generate a new type, e.g. UserId would be assignable to int, but int would not be assignable to UserId without an explicit type-cast or assertion of some sort. (This is useful for things like IDs which aren't just any numbers, or e.g. typedef HexColorCode = string, which is a string that requires a validating constructor, so you can statically guarantee that a HexColorCode is not just any string, but a string with a specific length and format.)

As opposed to type aliases, which type-check more like interfaces - e.g. in TypeScript, a declaration such as type HexColorCode = string doesn't declare a new type, but just an alias for an existing type. So a HexColorCode is assignable to a string, and vice-versa. (This has it's own set of use-cases - often just to avoid repeating the same complex type-expression over and over, which can make a codebase more difficult to refactor.)

I think both of these features are useful - but if these new typedef = declarations are just for declaring type aliases, I would suggest introducing a dedicated keyword like type for that, to avoid any confusion about a given typedef generating a new type or just a type alias. This would also make it possible to overload typedef in the future with a similar feature that does generate unique types.

Thoughts?

I would prefer a separate keyword for declaring alias.
Maybe define or declare can be used for it.

for ex.
define JSON as Map<String, dynamic>

So that after this I can use JSON.containsKey()

@eernstg

This comment has been minimized.

Copy link
Member

@eernstg eernstg commented Nov 11, 2019

A type alias (that is, a declaration starting with typedef) declares an alias for an existing type, and that will also be the case for the generalized type aliases. In particular, if e is an expression of type T and TAlias is an alias for the type T, TAlias x = e; is allowed. The only difference is that a type alias will currently only create an alias for a function type, and the generalized kind will allow for creating an alias for any type.

@mindplay-dk

This comment has been minimized.

Copy link

@mindplay-dk mindplay-dk commented Nov 11, 2019

@eernstg oh, so I have this backwards? The typedef keyword in Dart doesn't define new types - they're only aliases, like the type keyword in TypeScript?

In that case, the proposal is consistent with existing behavior, and my whole argument is moot, so please feel free to mark it as irrelevant to save other readers some time! 😊

@eernstg

This comment has been minimized.

Copy link
Member

@eernstg eernstg commented Nov 11, 2019

That's correct: A typedef in Dart does not define a new type, just a new syntax to denote an existing one.

It does come up, and we do recognize that the ability to define a new type associated with an existing representation is a useful tool (e.g., in order to enforce that a given representation is only used in ways that are suitable for a given interpretation of that representation, e.g., such that we don't add a weight and and height which are both represented as a double). But that's a different topic.

PS: In case I change my mind about a comment on an issue I usually go back and edit it, with an [Edit: <some-explanation>] at the top in case it isn't a mere typo fix. 😄

@mit-mit

This comment has been minimized.

Copy link
Member

@mit-mit mit-mit commented Nov 20, 2019

Closing this as we're now tracking this in dart-lang/language#65

@mit-mit mit-mit closed this Nov 20, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
Language Enhancement Categories
Non-Breaking And Simple
You can’t perform that action at this time.