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

Syntactic sugar for typedef #4112

Open
FMorschel opened this issue Sep 30, 2024 · 8 comments
Open

Syntactic sugar for typedef #4112

FMorschel opened this issue Sep 30, 2024 · 8 comments
Labels
feature Proposed language feature that solves one or more problems

Comments

@FMorschel
Copy link

FMorschel commented Sep 30, 2024

Today if I have two libraries that export classes with the same name, we get ambiguous_import if we try to use it. The suggested workarounds are to add an alias to one (or both) imports or to use hide.

Most of the time this is just fine, I'd like to propose a syntactic sugar to make the class names clearer on use.

Today we can do the following:

a.dart

class Random {}

// Other things that our code might use
class A {}

main.dart

import 'dart:math';
import 'a.dart' as a show Random;
import 'a.dart' hide Random;

typedef MyRandom = a.Random;

A? aInstance;
MyRandom? myRandom;
Random? random;

I'd like to propose a syntactic sugar for imported elements:

import 'dart:math';
import 'a.dart' with Random as MyRandom;

I'm not sure what word to use on with, just a suggestion. Using a different word to mean that I don't want to show or hide anything, but I believe this should work on show as well.

@FMorschel FMorschel added the feature Proposed language feature that solves one or more problems label Sep 30, 2024
@lrhn
Copy link
Member

lrhn commented Oct 1, 2024

This is a renaming operator, not just a type alias. Nothing would prevent it from renaming a variable or function.

It's not a new idea, it has been known since the hide and show modifiers were added, but it's not clear that a local rename operation is worth the complexity, when you can use an import prefix to avoid name conflicts.
Allowing renames in exports might be a little too confusing.

@FMorschel
Copy link
Author

Allowing renames in exports might be a little too confusing.

I agree, but today this can be done anyway with typedef so I don't see why not.

@lrhn
Copy link
Member

lrhn commented Oct 1, 2024

My reason against that would be that a rename doesn't expose a declaration.

If you expose a name using a typedef, there is an actual declaration with that name that one can look at and document.
If you do export 'dart:core' show String as Text; and I look at Text and go to source, I'll be confused if I find class String in dart:core. If it goes to String as Text, I might understand what's going on, but I still don't know why, and there is no way to add documentation to it.

With

import 'dart:core' as core;
/// A text used by something or other.
typedef Text = core.String;

you get to say what the new name means.

The core question is which problem this feature solves.

Is it having to write a typedef to rename something inside the same library or package, just for convenience? In that case, I don't think the benefit is big enough to warrant a language feature. Just write the typedef once and for all, and import it where you need it.

To not have to do the three-step-name-bypass, two imports, one without prefix and with a hide, one with prefix, and then one typedef to give a new unprefixed name for the name that would conflict? I still think it's rare enough, and local enough, that it doesn't really need a language feature. I'd actually prefer to use the prefixed name just to make it clear what is going on, rather than have an alias for it. (And if it's intended to be local only, the new should start with an _, so it won't be much prettier than a.Name.)

Is it intended as a way to expose an existing type by another name in a public API? Then I think you should document it, so using a typedef is better.

Is it exposting the same declaration under different names (not just the same type, like a type alias)? A type alias has covered that pretty well since it started allowing static member accesses. I'm not seeing the problem that solves.

Just to be realistic, language features are not free. They need to carry their own weight, with the benefit they add outweiging the cost of adding them, including opportunity cost. This feature, even if it looks fairly simple, doesn't seem likely to be worth its own design and implementation. The problems it addresses don't happen often enough and the workaround isn't problematic enough, so I expect the net benefit to be low. If people would regularly make mistakes in the workaround, then saving them from a pitfall might increase the value, but this would only save typing. There are other places where one can save typing, more commonly hit, that I'd go for first.

Not saying it wouldn't be nice to have.

@Wdestroier
Copy link

The core question is which problem this feature solves.

I will explain why I gave this issue a thumbs up. I was creating a card game and I want to call "Card" one of the most important project classes. However, it's a Flutter project and Flutter has a class called "Card". Therefore I need to write import 'package:flutter/material.dart' hide Card;' in almost every file. Would be nice if I could alias the Card widget as FlutterCard, MaterialCard or CardWidget, because then I can use the widget somewhere. The problem this feature doesn't solve is that I still need to fix the import in other files.

@mateusfccp
Copy link
Contributor

The core question is which problem this feature solves.

I will explain why I gave this issue a thumbs up. I was creating a card game and I want to call "Card" one of the most important project classes. However, it's a Flutter project and Flutter has a class called "Card". Therefore I need to write import 'package:flutter/material.dart' hide Card;' in almost every file. Would be nice if I could alias the Card widget as FlutterCard, MaterialCard or CardWidget, because then I can use the widget somewhere. The problem this feature doesn't solve is that I still need to fix the import in other files.

In my opinion, if you are making a card came, you shouldn't be depending on material anyway.

Not saying that I disagree (or agree) with this issue.

@lrhn
Copy link
Member

lrhn commented Oct 1, 2024

One thing you can do is have your own library:

// lib/src/nocard_material.dart
import 'package:flutter/material.dart' as card;
export 'package:flutter/material.dart' hide Card;
typedef FlutterCard = card.Card;

and then import that everywhere you would otherwise import package:flutter/material.dart.

That can even be easier to use than having to do import 'package:flutter/material.dart' with Card as FlutterCard; in every library you import it in. Doing it once and for all, in your own library. And you can give it a shorter name if you prefer, so you can just write import '/src/ncm.dart';. Less typing, and a single place to make changes in the future, if you need more.

@Wdestroier
Copy link

You can have your own library and then import that everywhere.

I may have a library exporting flutter then, thanks Lasse!

In my opinion, if you are making a card came, you shouldn't be depending on material anyway.

Possibly, I can try to import widgets.dart instead of material.dart.

@TekExplorer
Copy link

and then import that everywhere you would otherwise import package:flutter/material.dart.

I think we need improved tooling for this.

When I once attempted something similar, the inconvenience of needing to select the correct import - and needing to remember it - is not great.

Perhaps we should have some kind of annotation we could use to say that "this export is preferred over importing directly"? Or perhaps that should be its own issue? I'm not sure where it would go though.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
feature Proposed language feature that solves one or more problems
Projects
None yet
Development

No branches or pull requests

5 participants