Компилятор или интерпретатор любого языка представляет код, как данные, в процессе обработки. Но не всегда эти данные доступны программисту для свободной работы с ними. Гомоиконность означает всего лишь то, что код и данные "выглядят" одинаково. Поэтому, вопрос: вам для чего гомоиконность?
Если, чтобы была, тогда надо смотреть на языки семейства Lisp. Если для практических каких-то целей, вроде написания макросов, то можно смотреть на Rust, Haskell или Scala, в которых такие возможности есть в некотором виде, или на любой другой язык программирования и думать, как вытащить представление кода на этапе компиляции для программиста. Не такая простая задача для классического статически типизированного языка, потому что надо как-то убеждаться, что преобразования кода не сломают типы.
В любом случае система будет многостадийным компилятором, потому что вам нужно будет сначала компилировать код, который требуется для манипуляций синтаксисом, вызывать этот код, получать новый код, возможно, снова его компилировать, чтобы дальше проводить обработку выражений.
Julia примерно так и устроен. Компилятор является частью среды исполнения, поэтому сложно сказать, статическая типизация у Julia или нет. В любом случае, скомпилированный код работает быстро, порой даже быстрее аналогичного кода на Си.
Typed Racket сделан поверх Racket, как набор макросов, и система типов там не обычная, а occurence typing - намного более сложная штука, чем традиционные системы статической типизации. Есть ещё Typed Clojure, сделанный по мотивам Typed Scheme, которая тоже из этой категории.
Наконец, есть относительно сырой, но относительно работоспособный Lisp со статической типизацией: carp-lang. В котором тоже приходится, фактически, иметь два языка: динамический язык для работы с макросами, и статический основной язык. Выглядят они одинаково, но семантика разная.
Может быть, Вы что-нибудь придумаете на этот счёт. Удачи в изысканиях!