-
Notifications
You must be signed in to change notification settings - Fork 35
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
Type assertion operator #85
Comments
I think the string representation is the easiest and most intuitive of these. Then we can accept types in the usual type notation such as |
We can add a macro that converts the second argument to its string representation. (We already use this technique in a test suite, the macro -define(as_type(E, T), '::'(E, ??T).
foo(A) -> ?as_type(A, pos_integer()). |
Some thoughts: Regarding the semantics, there are two reasonable alternatives that we can go with. I'll be referring to them as type annotation and type assertion.
Both of these are useful. But I'd argue that type annotations are more useful than type assertions. We don't have to choose between one or the other though. We can have both. I makes sense to use the As for the syntax of types, why can't we use |
Thanks for the semantics! It like it.
Well, the expression |
Right. I'm imagining that we would transform them since you suggested we use a parse transform anyway. Using the internal representation as a syntax seems to me like very poor ergonomics. |
Agree. I suggest we use the string form then for the Nothing prevents us from also supporting a parse transform in the future which would transform the 2nd argument to a string unless it already is a string. However, there is some type syntax that is not valid expression syntax, e.g. ranges and types like |
@josefs there's a third valid semantic (used in TS):
This means you can walk along the subtyping latice in a way that is useful and fairly safe. I don't know how it interacts with blame calculus but, assuming that it doesn't clash, it's the semantics I would greatly prefer for assertions since you can always get the unrestricted semantics by manually taking the step via any. Supporting both annotations and assertions seems like the best way to go about it. '::' should be an annotation; any ideas for the assertion operator? |
@Zalastax, I can't say I'm a fan of the Typescript semantics. In order to understand whether you're doing something potentially unsafe you have know the exact type of the expression you're annotating and how it relates to the type you're annotating it with. If you're upcasting, that's totally safe, but if you're downcasting you're doing something potentially dangerous. From a blame calculus perspective it gets very weird because the blame is different depending on whether you're downcasting or upcasting. It's probably possible to fit it into the blame calculus somehow but it's going to get very complicated. I'd rather we didn't have this semantics if we're ever going to have a shot at proving anything about this type system. |
@josefs what if upcasting is done via type annotations and downcasting via type assertions? That would correspond to how I use assertions in TS, i.e. I annotate potentially wider types for my variables Since upcasting is performed automatically (in most cases or always?) what I've been missing is safe downcasts. Downcasting is inherently unsafe |
@Zalastax, what you're saying about "safe" downcasts makes perfect sense (although I think words like "sensible" or "consistent" would be better adjectives to use rather than "safe" in this context). I like your suggestion of having upcasting be done by type annotations and "consistent" downcasting done by type assertions. In particular, I really like the idea of forcing people to convert back and forth to Just to be clear, type annotation wouldn't only be about upcasting, since, as you say, that happens automatically. It would also convert from |
Awesome @josefs! Thanks for challenging my idea - it turned out a lot better this way! |
Thanks for adding the wiki page Peti! |
One of the discussions in #83 regards type assertions. A good idea that spawned out of that (thanks @zuiderkwast!) was that adding an
id
-function that accepts an extra dummy parameter representing a type would allow us to implement type assertions in an efficient and general way without the need to extend the language. To exemplify:To provide nicer syntax for type assertions @zuiderkwast suggests a parse transform:
Discussion points:
any()
to representany()
does not fly. Some ideas: the internal representation of types ('::'([42], {type, 0, nonempty_list, [{type, 0, pos_integer, []}]}
), structured data that needs to be massaged (if we add some helper functions, e.g.type(T) -> {type, T}
, this might turn out OK), or as a string (which we'll have to parse) e.g.'::'(42, "pos_integer()")
.The text was updated successfully, but these errors were encountered: