Ok, I think I understand what you are trying to say. Instead of telling us how we don’t understand various things, how about next time you define your terms, choose your words more carefully and be clearer with your explanations, so we can actually understand what you’re trying to say.

So let me restate what I think you meant. Maybe I’m wrong.

> a limitation of compile time static types is that they cannot do dynamic dispatch, which is relies on information that isn’t known at compile time.

I think we can all agree on this?

Note that I said it’s a limitation, not that it’s bad. Limitations don’t make something bad in themselves, they only constrain when it is an appropriate tool. You wouldn’t say that a submarine is bad because it can’t go on land? It’s a limitation but that doesn’t make it bad.

Then you said that because of this limitation, “you can’t have routers”.

But you can have routers in static languages like C++, Rust or even Haskell.

You have a choice (trade off) to make:

1. either you give up on some dynamism to keep compile time checks by enumerating the types you can dispatch on (as std::variant does)

2. or you choose to give up static checking (for this part of your code only!) and move it to runtime first full dynamic logic (as std::any does). When giving up compile time safety for one part of your code, you do not give it up for all of your code, because you can runtime validate then on the boundaries going back into statically checked code paths, so the compiler cab assume that they are valid if they get back in (as the runtime checks would reject bad types)

Neither case is “you can’t have routers”, but sure you can’t have fully dynamic routers purely at compile time.

Also, both options are perfectly tractable and both cases are typically first class language features (at least in the static types languages I’ve used). In no case are the “bad” options, despite each option having different limitations.

In a dynamic-types-only language, you don’t get to choose the trade offs at all, you only get “fully dynamic no compile time checking, runtime checks only”.

Note also that in real life, few things are truly fully dynamic. You are always constrained by the operations that are expected to be carried out on the dynamic data and while you might not know at runtime what days you could get, you DO know what operations you expect to run on the data. And you can perform compile time checks around that.

So for a router, so you really need it to be fully dynamic? Or can you live with it being generic (ie it’s a library that does routing and it can support any type, but you constrain it to known types when you actually use it). If so, you have options that maintain type safety: you can use OOP inheritance, you can use enumerated types like std::variant, you can use generics/templates. The library works for any types but the user chooses what types to implement based on the operations that will be performed on the data. Even dynamic types do this, they just defer it to runtime.

Or you can have the router operate on purely dynamic types but the handlers that are routed to are statically typed (eg if in C++ the router uses std::any and the handler registers what types it can accept and the router checks validity before handing the data off).