4 ms·
I really like the syntax used to declare generic type parameters to functions in ML-type languages like F#. Writing the below in other languages (pseudocode) f
by rspeele 6y ago
I really like the syntax used to declare generic type parameters to functions in ML-type languages like F#.
Writing the below in other languages (pseudocode) feels repetitive in the same way that `Thing thing = new Thing()` did back before `var` or `auto` types became popular:
list<Y> Map<X, Y>(list<X> inputs, X->Y mapping)
Why do I need to put <X, Y> in brackets after Map? To tell the compiler that these are generics, not attempts to reference actual specific types that just happen to have crappy, nondescript names like X and Y.
In F# and friends, there is no need to put a bracketed block declaring which types are generic parameters. I would simply write:
let map (inputs: list<'x>) (mapping: 'x -> 'y) : list<'y> = ...
The little apostrophe or tick mark before the type name tells the compiler it's a generic type parameter. It's not legal to name a concrete type with a leading tick mark, so there can be no mix-up. At first it looks ugly, but once you are used to it, it is natural and less cluttered than having to have these separate bracketed declarations for "uh-oh, here comes a generic".
But I suspect Go will always want more explicit syntax for generics. In part out of backwards compatibility, but largely because Go's designers see generics as an advanced, occasionally useful feature. Correspondingly they'll want the presence of a generic function to have ugly syntactical warning signs: here be dragons.
Functional languages usually take the opposite approach and see a fully generic function as less complex than one for a specific type, because just by looking at its signature, you know it doesn't require anything special about the input objects it's going to work on. It is not going to be calling methods on them or messing with their guts. It's just treating them as black-box, could-be-anything cards to be shuffled around from point A to point B.
- emmelaich 6y agoIndeed! You could use other chars if the single quote is a problem. Make identifiers that start with period or tilde perhaps be used for template vars. And only template vars. Might be a bit noisy but less noisy than reusing various types of braces.
- contravariant 6y agoIf you want to indicate they're parameters you're probably best of using syntax that's already being used for parameters like, $a, ?a, :a. I guess {a} is also used, but we wanted to avoid braces. Not sure if .a or ~a are good choices. The latter is especially bad since it clashes with common syntax for "(bitwise) not a".
- a1369209993 6y ago> Why do I need to put <X, Y> in brackets after Map? [...] In F# and friends, there is no need Try: let foo (arg: list<'x>) : list<'x> = ... <inside foo somewhere> let bar (arg: list<'x>) : list<'x> = ... ... Is bar generic over a fresh type that happens to be named 'x, or is it a monomorphicly-typed local function that uses the type 'x that foo is generic over?
- uryga 6y agonot sure about F#, but in haskell it's the former, and you can switch to the latter with the -XScopedTypeVariables language extension. though IME you rarely (if ever) need it thanks to type inference.
- a1369209993 6y agoActually, on further consideration, you could restrict the ' to only occur where the type parameter is introduced, like so: List<X> foo(List<'X> inp) = ... List<X> bar(List<'X> xs) = ... # new X scoped to this declaration List<X> baz(List<X> xs) = ... # use foo's X ...
- tomp 6y agoThat's only possible because F#'s (and OCaml's) type system is so simple (basically the only possible relationship is equality, with is indicated by reusing the same letter twice). But if your system is more complex, even like Java's, you often need additional constraints, e.g. <X : Hashable> or something. In that case, you probably do need to declare your type parameters. Best, of course, would be some middle ground assisted by a fantastic compiler. group_by : list[`a] -> (`a -> (`b : hashable)) -> Map[`b, list[`a]] or something
- uryga 6y ago> you often need additional constraints, e.g. <X : Hashable> or something in haskell: noConstraint :: List a -> (a -> b) -> List b withConstraints :: (Hashable a, Frobable b) => List a -> (a -> b) -> List b you still don't have to declare `a` and `b`, you're just adding a constraint on them. (iirc in haskell types have to start with an uppercase character, which means you can use lowercase letters for type variables without extra apostrophes etc)