My question is how does that work in a dynamically typed language? In static typed language we can know scope & type of a variable and we can't change much in runtime.
In Clojure you know the number of arguments to a function and the name of functions and variables, and the code is all very well structured as an AST (being a Lisp).
So you can do a lot of refactorings with that such as:
Rename function, rename variable, rename namespace, extract constant, extract function, extract local variable, extract global variable, convert to thread-first, convert to thread-last, auto-import, clean imports, find all use, inline function, move function/variable to a different namespace, and some more.
The only thing is you can't change the "type" of something and statically know what broke.
I believe you're mistaken, but please explain otherwise?
None of those seem to require type information from my reasoning (and are also all available in Emacs for Clojure)
For example, moving a function from one namespace to another, you know where this function is being used from the require declarations, and you know where you've been told to move it too and where it currently resides. So you can simply change the old require pointing to its old namespace to point to the new namespace and cut/paste the function from the old to the new. Nothing requires knowing the type or the arguments or the return value of the function.
Even Smalltalk's refactoring browser made mistakes which humans had to fix by hand. Which is not surprising, because in the absence of type annotation, the IDE doesn't have enough knowledge to perform safe refactorings.
That blog is talking about refactoring a method, not a function.
In Clojure, I'm talking about renaming a function, which can be done without types.
See the difference is that with a method:
x.f()
You have to know the type of `x` to find the right `f`, but with a function in Clojure:
(ns foo
(:require [a :refer [f]]))
(f x)
The location of `f` is not dependent on the type of `x`, you known statically that this `f` is inside the namespace `a`, because of the require clause that says that in `foo`, `f` refers to the `f` inside of `a`.
And this is unambiguous in Clojure because there cannot be more than one `f` inside `a`.
If you had two `f` this would be the code in Clojure:
(ns a)
(defn f [] "I'm in a")
(ns b)
(defn f [] "I'm in b")
(ns foo
(:require [a :refer [f]]
[b :refer [f]
:rename {f bf}]))
(f x)
(bf x)
You're forced to rename the other f, and now it's clear statically again that `bf` is the `f` from `b` and `f` is the one from `a`, no need to know the type of `x` for it.
You are pushing fud about not having typing systems at all... they are valuable to automated systems for introspection to some degree.
However you are talking about typing as if all typing is static - static typing has little value beyond warm and fuzzies on the developers part, dynamic typed systems are able to perform just as well. At which point, the dynamic part can allow you to mostly drop the types.
statically typed systems, you will note, tend to come with ecosystems dedicated to using the static bits as little as possible. And they provide no guarantee of correctness.
Yes, new devs might be able to latch onto some specific typing a bit better, but I don't care if you have all the automated refactors and a hundred new employees, if your codebase sucks and is incorrect, your static analysis is worth didly squat.