Should module function type annotations always be required? #1339
Replies: 17 comments
-
I think breaking the need for ordering of function and type declarations in a file would be one big advantage of requiring those annotations. That's a kind of strange thing to encounter in a compiled language (in my opinion). However, in terms of adoption, it seems that many people really desire type inference these days, so this change might turn some potentially interested folks off. As much as I'm generally against such things, what about a middle ground where you can turn on a sort of |
Beta Was this translation helpful? Give feedback.
-
Gleam's type system is very much a imitation of OCaml's so it inherits the ordering requirement from there. The ordering requirement will be removed in Gleam, but it'll likely come a lot sooner if we have mandatory function arguments. RE ease of adoption, while I think that annotating functions may be initially off-putting it can be advantageous for newcomers for a couple reasons. First it encourages the programmer to think about types as a core part of their programming model, and second with this level of type information the compiler can provide much clearer error messages when a mistake has been made (and a beginners are likely going to be making a lot). I believe this second reason is why Elm encourages type annotations so aggressively they may as well be required. I think optional type annotations are most useful for the experienced static types user, for brevity when writing. |
Beta Was this translation helpful? Give feedback.
-
What would actually help people vs. what people actually want are often really different things. It's not exactly apples to oranges, but Typescript is getting way more adoption than Elm, and I think the fact that Typescript is a very small step past regular Javascript is a big part of that. Elm is the more correct language, with far better error messages, but because it's more of a departure from what most users know, it's scarier, so they're less likely to use it. And, yeah, there's also the Microsoft thing, so they're kind of cheating, but I do think the way Typescript was designed does make it particularly easy to get into the language. If you're trying to make it easy for folks to go from Elixir/Erlang to Gleam, then making it as similar as possible to those languages is going to help that. That's why I like having the simple on-ramp, and then a lot more features that allow them to get even more powerful stuff incrementally but without making that powerful stuff mandatory right at the beginning. On the other hand, if you're designing this language for power users to get a whole lot out of it, then just focus on as many of the benefits to stuff like user experience as possible. Frankly, typed languages skew towards this group already, and I can imagine that a typed language on the BEAM might be even more in this direction. Otherwise, if you want a lot of folks to use Gleam, then (unfortunately) you'll want to make compromises on features if it means a more familiar/simple syntax. The thing that I've learned over the last few years is that syntax matters way more to most developers than it should, and for some reason they're really biased towards writing as few words on the screen as humanly possible. |
Beta Was this translation helpful? Give feedback.
-
I think that there are some compromises that are worth making to make this language more familiar and accessible to Elixir/Erlang developers in particular, especially as it is in at least some sense written to exist within the Erlang ecosystem, but I personally don't think that introducing the complexity of optionally strict type annotations, or allowing for optional type annotating if that means giving up on fundamentally useful language features, is worth the tradeoff. I think that if you take the "give people what they're familiar with" mentality to the extreme, you just end up cloning the original language(s) that you're trying to improve upon, or deviate in exactly one (1) way so as to make it as close as possible, while still technically "better". I think that doing something like referring to type variants as "records" is an example of a good compromise that is semantically useful for Erlang developers and doesn't affect the fundamental usefulness of any of Gleam's unique language features. Elm takes the approach of assuming that anyone is capable of learning and appreciating a fully-fledged, Hindley-Milner-typed, immutable-data language, by trying to make it as friendly and easy-to-learn as possible without giving up on any of the qualities of the language that it thinks are the most valuable. Education is consequently a deeply-ingrained characteristic of that language's community. This is of course not my language, but if anyone were to ask me, those are some of the guiding principles that I would like to see Gleam have, too :) I also think that an important difference between the ecosystems of JavaScript/TypeScript/Elm and Erlang/Elixir/Gleam is that the Erlang ecosystem has Dialyzer, which has pretty wide adoption and value recognition for what it does, and at least strongly encourages (does it not require?) type annotations! That plus the fact that all big/serious libraries in the two languages seem to use it, to the noticeable benefit of those libraries' users, makes me think that Erlang/Elixir developers might have a different baseline when it comes to their comfort with type annotations than JavaScript developers might. Still, I think those are well-observed points @devonestes—but personally (and more to the specific point of this discussion) I would definitely like to see module function type annotations be required! I always annotate them myself, appreciate it when reading others' code when they do, and even personally like annotating anonymous functions (e.g. |
Beta Was this translation helpful? Give feedback.
-
Thank you for your input @rjdellecese , @devonestes ! Helpful and very much appreciated |
Beta Was this translation helpful? Give feedback.
-
I greatly admire Elm's philosophy here and I'd like Gleam to be as good as Elm in this respect, with some differences. If you have some ideas around this please open an issue to share your thoughts :) |
Beta Was this translation helpful? Give feedback.
-
Removing the 'ordering' would prevent the ability to have OCaml style function override and imports/extensions, so careful with considering that. And ordered function declarations are super common in compiled static languages. :-) |
Beta Was this translation helpful? Give feedback.
-
There are two main advantages to always requiring module function annotations that I have not seen other people mention in this discussion yet:
|
Beta Was this translation helpful? Give feedback.
-
With Gleam's type inference algorithm compilation would not be any faster (it means inserting a concrete type rather than a type variable for each argument) but errors may be a little more precise. :) |
Beta Was this translation helpful? Give feedback.
-
Something I haven't seen mentioned here... With a well built, fast language server running in your IDE to give you mouse-over info on types (something everyone expects out of an IDE) it heavily removes the need for actually typing out the type in the source. If gleam is able to infer them, that seems to me like the middle ground everyone's searching for here. |
Beta Was this translation helpful? Give feedback.
-
@Qqwy I'm unsure if you mean it like "user gave us this type, let's skip inferring the actual body to see if they're right" - that shouldn't be done IMHO: your type annotation, if given, should still be checked against the real type, and error raised if you were wrong. |
Beta Was this translation helpful? Give feedback.
-
That's right, languages that have mandatory type annotations still perform full type inference to ensure the annotations are correct. |
Beta Was this translation helpful? Give feedback.
-
I'm going to close this as I believe the main issues have been resolved in other ways now. Thanks all! |
Beta Was this translation helpful? Give feedback.
-
For posterity, I meant that when the user's type annotation already fills in certain type variables in the signature, then this narrows down the unification that is required to test whether the real type and the provided type match.
…On September 12, 2020 11:14:33 PM UTC, Martin Janiczek ***@***.***> wrote:
> There are two main advantages to always requiring module function
annotations that I have not seen other people mention in this
discussion yet:
>
> Compilation will be faster because there is less to infer.
@Qqwy I'm unsure if you mean it like "user gave us this type, let's
skip inferring the actual body to see if they're right" - that
shouldn't be done IMHO: your type annotation, if given, should still be
checked against the real type, and error raised if you were wrong.
--
You are receiving this because you were mentioned.
Reply to this email directly or view it on GitHub:
https://github.com/gleam-lang/suggestions/issues/48#issuecomment-691569963
--
Met vriendelijke groet / Sincerely,
~W-M
|
Beta Was this translation helpful? Give feedback.
-
New motivationPeople are currently frequently surprised by the way that Gleam will generalise functions differently if there are no type annotations and if the function is used before it was defined. Always having annotations would avoid this issue, and it would make the type checker simpler. |
Beta Was this translation helpful? Give feedback.
-
eg in #1096! |
Beta Was this translation helpful? Give feedback.
-
I want to resurface a viewpoint similar to the one brought up in #1218 (comment), specifically this part:
I'll begin with context for my stylistic preference, but keep in mind it's not advocating for either side, rather it's just some context for the main point at the end of this post. As aforementioned, TypeScript does require explicit function signatures even in strict mode. That being said you can totally enforce this for your project using linter rules: I personally do not use either of those rules for my projects. In strict mode (which I use), however, the following signature is not allowed:
because
I enjoy the freedom of explicit function signatures being opt-in through linter rules rather than being imposed by the language. It definitely is useful to have parameters' types be annotated so that the reader doesn't assume "Any" for everything (an unpleasant experience for exploring complicated functions and complex modules in dynamic languages), but personally, stylistically, I don't feel like inclined to type out the return type for brevity's sake; annotating the input parameters is definitely enough for the compiler to figure out the return type by itself. It also feels like a drag for short functions (which come up very often):
Anyways, the point of this post is not to advocate for imposing (or not) having complete signatures, but rather show that the freedom for varying degrees of verbosity is great. Basically, in TypeScript you can go with either:
In Gleam, for simplicity's sake in the compiler, while you may want to go in the direction of requiring complete signatures rather than having to support different degrees of verbosity, it definitely is worthwhile to at least consider this "stylistic flexibility" aspect. People come from different backgrounds and have different opinions about how they want to write their code, so I'd say having an opt-in Again, I'm not interested in the merits of having vs. not having complete signatures, but instead trying to emphasize that the flexibility matters for some. It would not be a deterrent for me if Gleam would go the "fully verbose" route (and I doubt it would for others), although it definitely is not my preference. |
Beta Was this translation helpful? Give feedback.
-
Currently we are like Elm, Haskell, and OCaml in that type annotations are not required, but they can be given.
In real-world code written in these languages type annotations are almost always given for top level functions, and in Elm the compiler will complain if you do not supply them.
Other popular languages with static type systems always require type annotations for top level functions. i.e. Rust, Scala, Kotlin, Go.
With annotated function arguments we can provide easier to understand messages when there is a type error.
It would also make it trivial to introduce mutual recusion, remove the need for functions to be defined in the order they are used, and enable some fancier type features in addition.This is done now.Lastly I think having more certainty in the types when being inferred would open the door to some overloading or implicit typing features that would otherwise introduce ambiguity.
The cost of requiring these annotations is that there is more boilerplate for the programmer, though not an unusual amount for a statically typed language.
I'm interested in hearing what other people think about this question.
Beta Was this translation helpful? Give feedback.
All reactions