[Discussion] Adding a function to Relay module automatically triggers InferType


#1

Hey,

Forgive my ignorance but I would love to ask the following question: Is there any reason to couple a pass (InferType) and the module definition itself?

Cons of the issue include:

  1. mutual global function call would fail InferType
  2. it poses overly strong requirements over frontend importers
  3. this would be prohibitive for developing a frontend using Relay as IR, which does or does not do code generation – they may not need such a strongly typed module at once

Therefore, we would love to learn if there is specific reason for this design :slight_smile:

CC: @jroesch @haichen @zhiics


#2

maybe I’m missing some context. would you mind give an example?


#3

This line. Every time a function is added to module, InferType is automatically triggered.


#4

Besides mutually recursive globals deferring type checking provides no benefits, code that doesn’t type check is not a valid Relay program and can not be used to do anything, analysis, optimization, or code generation.

We can defer type checking to the first pass, but I don’t see it providing much value, it will also defer all type checking errors for the whole module to a single line in the traceback which makes debugging more challenging.


#5

Hey Jared,

Thank you for the response!

I completely agree that types are important. Without proper type information, we are unable to do anything like type-specific analysis, optimization and code generation. Also, I agree that doing type checking every time a new function is added is beneficial for debugging.

My primary point is: is it that necessary that they are coupled together?

  1. debugging: anyone is free to insert type inference anywhere they insert something to the module without having to break the system.
  2. opt/codegen: we can do type inference pass before these passes.
  3. flexibility: decoupling them would offer developers more flexibility, for example, do mutually recursive global function calls.

Therefore, I would slightly prefer to decouple them :slight_smile:

Thanks,
Junru


#6

@junrushao1994 there are PR to add mutual recursion into relay. In general, the only reason a relay program cant typecheck is because it is wrong, thus we want to catch this as early as possible. For case 1/2, it is still possible to call the type inference function yourself. What is your use case?


#7

the only reason a relay program cant typecheck is because it is wrong.

There are other reasons. Image there is a front end framework using Relay as IR, it may not care about type that much, because 1) it doesn’t necessarily do code generation; 2) It has tons of types that cannot be inferred by HM inference (e.g. if %1 { 1 } else { “a” })


#8

For case 1/2, it is still possible to call the type inference function yourself

Yep, that’s my point. Anyone can call type inference any time, so why bother?


#9

@junrushao1994 there is no plan to make relay dynamically typed. However, you can still do the same by roughly:
data Any =
| AnyIntScalar (Tensor[(), Int])
| AnyFunction (Any -> Any)
| AnyTuple (Any, Any)


#10

If those types can fit into the current type system, sure we can use ADT.