5 ms·
> Specifying what a function returns in Gleam is optional; due to type inference, Gleam will understand it anyway. Oh yuck, I don't like that. Function signat
by kstenerud 2y ago
> Specifying what a function returns in Gleam is optional; due to type inference, Gleam will understand it anyway.
Oh yuck, I don't like that.
Function signatures are for HUMANS to read. Sure, the compiler can reach into a function implementation to glean what it returns, but that's no reason to force a human reader waste time on it as well.
"I'll accept this and this from you and then return that kind of thing to you."
vs
"I'll accept this and this from you and then return something to you that your supervisor will tell you if you're using correctly or not - or you could watch me as I work to see what it is I've made for you."
- PartiallyTyped 2y agoDoesn’t gleam come with a linter? If so, it’s likely that that can be enforced.
- widdershins 2y agoThe designer of gleam is trying to make a linter unnecessary, by not having extraneous language features that need to be discouraged. Guess it's not working 100% if someone feels this way! However, I would say that since the compiler/LSP knows the full type of every function it should be possible to have a code action that says 'annotate the type of this function for me'.
- kstenerud 2y agoYup that would be fine, so long as it's required to be done before you release your code. Putting return types on functions is signaling intent, much like left and right signaling on a car. I want to know what your function COULD produce, not what it happens to produce now. Type inference can only show that in every current path you return a TYPE_X, and that works fine until one day you return an instance of its supertype TYPE_A, and now my code blows up because I misunderstood the contract. You may have always intended for it to return TYPE_A and its subtypes, but you never signaled that intent to others. This is why enforcing explicit contracts is necessary.
- widdershins 2y agoWell Gleam doesn't have supertypes, so the function signature (even when inferred) does describe everything your function could possibly produce. If the author changes that, your code will stop compiling, not blow up at runtime. I agree that packages authors really should annotate their types though, and possibly that they ought to be forced to do so, just to make reading the code easier.
- lpil 2y agoGleam doesn't have subtyping, so the drawback described here is not possible.
- k_bx 2y agoVery often in Elm/Haskell would I write the code first, then let my IDE insert the type annotation. Not always perfect, but often the case.
- __jonas 2y agoAs a user of a package, you see the signature including the return type in the generated documenation and in your editor through the LSP which is part of the core language, so it's not really an issue. But I agree that specifying the returns explicitly is a bit nicer when reading the source, don't think it's a big issue that it's optional though.
- kstenerud 2y agoBut what if the inference generates a contract based on the narrowest scope of the types returned, and then later you add a wider type (because that's what you'd originally intended but didn't have any actual use cases at the time) and now everyone's code blows up because they assumed the narrower type and don't handle the wider one? If you were forced to specify the return type, your original intent becomes clear for all (rather than leaving it to a machine that can only assume the narrowest scope).
- __jonas 2y agoIt won't, because there is no type narrowing in Gleam currently, the type system is also very different to something like Typescript, I just don't see what you are describing as something that could realistically happen, but if you have an example I would be curious. If the return type changes, the code using the function should definitely blow up until it's changed, don't think that's a bad thing.
- scns 2y ago> If the return type changes, the code using the function should definitely blow up until it's changed, don't think that's a bad thing. This. The big upside of algebraic data types. The function won't blow up though, the compiler shows you every piece of code where the added type needs to be handled and therefore ensures the program runs correctly.
- lpil 2y agoGleam doesn't have subtyping, so this drawback is impossible. Its type system is similar to OCaml, Elm, F#, etc.
- widdershins 2y agoGenerally the advice is that public functions should have type annotations, and private functions needn't if you don't want to. But I could see arguments for either actually. Sometimes it's a bit of an annoying overhead if it's obvious what the function returns, and it certainly helps make prototyping code feel nice, especially since everything's typesafe anyway. As I said in another comment, as the compiler knows the type of everything, it would be easy to have the LSP overlay the type (or annotate it into the text via code action for you).
- benrutter 2y agoHonestly, on this one, I see both sides. I think functions should be written so that there type signatures are readable in the code, but I'm not sure I want a language making something semantically incorrect just to discourage bad style. Either way, I use (and honestly love) python on a daily basis and I wish my gripes with it were on this small a level. Yes, I'm looking at you, passed by assignment and late binding closure.
- danybittel 2y agoI usually also like type annotations for return types. The problem I'm having with my PL is with generics: If the return type depends on T but is not T. If I infer the return type it's obvious.
- viraptor 2y agoIt's honestly nice when it's optional but with tooling understanding the inferred type. (Both LSP and docs) Sometimes you have a function with a single line implementation and an obvious return (bool or string). Having to add explicit types to everything becomes a bit silly.
- ThinkBeat 2y agoA seriously important point you make. The little bit of time it takes to annotate the type that the function returns will save a lot of time when other people take over the codebase. Humans are not great compilers and spending time try to figure out what the hell is returned from a function
- mrkeen 2y ago> Humans are not great compilers and spending time try to figure out what the hell is returned from a function That's why I leave it to the compiler and don't try to do it as a human.
- thunky 2y agoIf you aren't able to figure out the return type then something has gone wrong: maybe the language or code is too complex, or maybe you don't understand the code very well.
- mrkeen 2y agoAs was said above, humans aren't great at it. If I want to know the type of an expression, I ask the compiler.
- thunky 2y ago> humans aren't great at it Yeah that's fine, although my point is that being unable to figure out the return type is a symptom of a bigger problem imo. It means we're outsmarting ourselves.
- lpil 2y agoI think if I were making Gleam again from scratch it'd be required, them being optional was somewhat inherited as Gleam is an ML family language. Luckily Gleam programmers do write return annotations in practice.
- valenterry 2y agoWith the right tooling, this is a non-issue. Compare with Scala, a language that also allows you to omit return types (except for recursive functions). IntelliJ (the best Scala IDE) now has something called X-Ray mode, see the videos here: https://blog.jetbrains.com/scala/2023/12/21/the-x-ray-mode/ https://blog.jetbrains.com/scala/2023/12/21/the-x-ray-mode/ Alternatively, it also allows you to configure it to always show the inferred types on functions. Before, I annotated things a lot, but after having this I've never felt the need to annotate functions ever again. I still do it for certain things though but for other reasons than readability (e.g. for type-driven development).
- Barrin92 2y ago> Function signatures are for HUMANS to read Nope, exactly the other way around. Function signatures are to help machines optimize your code. What humans should care about are names. It's just that we've normalized an extremely misguided practice of abusing type systems as a crutch for non-descriptive variable and function names.
- itishappy 2y agoHard disagree. It's a contract between compiler and coder, and it needs to be exposed to both. Type information does not belong in names. // For example, this: fn get_driving_requirements(vehicle: Vehicle) -> String { ... } // Is just so much nicer than this: fn get_driving_requirement_description_from_vehicle(vehicle) { ... } This is nicer for me (a human) to read, even if the compiler can infer the types of both.
- mrkeen 2y ago> It's just that we've normalized an extremely misguided practice of abusing type systems as a crutch for non-descriptive variable and function names. And maybe we just abuse formal logic because we've forgotten how to write good prose.
- myaccountonhn 2y agoI thought so too, but then I used OCaml and found myself very productive without explicitly annotating everything compared to Haskell. It gives you the speed of dynamic languages with the correctness of a static language. The LSP should be able to annotate for you. The cases where I think it is necessary to annotate, I can do so.
- tome 2y agoI'm curious about what you mean by 'compared to Haskell'. You don't have to annotate everything in Haskell either, do you?
- myaccountonhn 2y agoYou don't, but the convention in Haskell is that you do. The linters I used complain for example if you don't annotate.
- dllthomas 2y ago> cases where I think it is necessary to annotate Worth noting that that can be reactive, too. If an type checking error message is confusing or insufficiently localized, pinning down a few things around it can clarify. As tome says, though, that can be a thing in Haskell too. There exist situations in Haskell where the compiler cannot figure out the types for you, but they're not most code.