Why Julia doesn't have function types?

IIRC type classes also solve the “expression problem” so I guess they are good for something too

What purposes do function types serve? (As this thread figured out, not “function” in the Julia sense, which is just typeof(f).) I’ve never used a language with them, and from the cursory reading I’ve done, I can only imagine something like Go where, in more Julian terms, an abstract type can specify all methods with concretely typed inputs and outputs, e.g. type AbstractFloatyFloat func(x float64) float64. But Julia already dispatches on the types of the inputs (and inferrable outputs), and we can check the output type at runtime if necessary.

Sincerely, as someone that love Haskell (but has not programmed in it for a long time), I really like Haskell function types. Some advantages are:

  1. The function types are great to search for functions in the documentation that you believe that exist but have never really checked (i.e., “there must be a function that does X in stdlib, and if it existed its type would be Y, lets search for its type”, see https://hoogle.haskell.org/).
  2. As Haskell has no statements/attribution/‘mutable variables’, then everything is just a gigantic expression at the end, and the return of each function must fit as input of other, the function types make easy to look at the code and understand if everything fits together or not.
  3. The compiler is able to know (before runtime) if the function you are passing as argument to a high-order function takes the parameters of the correct types and return the correct output, even if the function is highly generic/parameterized. And then it says which is the type the function should have to be a valid parameter. This even without explicit typing of the functions (most of the function types are deduced by the compiler).

However, I know Haskell has some limitations in its type system that Julia does not have, and that allow for static analysis.

2 Likes

Well, I think the multiple-dispatch perhaps kills it’s usefulness. But I can think of a situation where I’m composing a bunch of functions, and at some point one of them will return “the wrong type”, and I’ll only catch the error in the run-time. Which could otherwise be done before, by checking that types didn’t match.

It actually sounds feasible to check for return type correctness with existing stuff. Would need some way to just do type inference on a function and a set of concrete input types (@code_warntype basically does it and prints it out), then @assert the result is equal to a return type.

Still would be a hassle, though. You’ll have to explicitly do these checks after all the imports because modules can extend each other’s functions and affect method dispatches. It would also still have to happen at run-time, just can be in advance of any method calls that trigger full-on compilation then execution, which might be a time-saver. Generic type-inferred multimethods just don’t make for implicit AOT compile-time type checks. Some macro magic over explicit type declarations in a method definition could do something similar to statically typed languages, but you could only write it for one specialization per method.

1 Like

Talking about types for overloaded functions makes no sense, for such two methods of foo, the second has the type String -> Int, and the first is a bit more complex but still typeable when using traits.

Suppose * operator is a function that comes from a trait AddTrait{LeftType, RightType, ResultType}, then the first foo has the type T -> G where AddTrait{Int, T, G}.

1 Like