Type inference is probably the biggest thing. You would need explicit "phases" to expand macros, disallow macro expansion at runtime, and implement bi-directional type inference HM-style to get even close to what OCaml has.
To be honest, I'd kill for a Lisp that had the same type system as OCaml, but I suspect the closes we'll get is basically Rust (whose macro system is quite good).
Most of those aren't really ready for production use except maybe Typed Racket, which I consider to be too "weak" and took a route with annotations that I'm not a fan of. Coalton is very interesting, I've been following it for a bit. Carp [0] is another one that I've been following.
Coalton is used in production for quantum computing systems and soft real-time control system automation. There are also (a small number of) Coalton jobs.
Quantum computing control systems is exactly the domain I've spent about half a decade doing, it's really not the production-like environment you think it is. Speed of iteration and flexibility to allow for changes to hardware is tantamount to success. It's also a lot easier to accept risk to breakages in the language when the author works at your company too.
Rust is categorically different from all of these other things. Lisps (and OCaml) all have interactivity as a core language feature - Rust is as non-interactive as it gets.
There is a simpler solution than type inference to removing type annotations while retaining types: remove the distinction between variable and type names. The way that you handle multiple instances of a type is to define an alias for the type. In pseudocode it might look like:
type Divisor Number
def divide(Number, Divisor) = Number / Divisor
As compared to:
def divide(number: Number, divisor: Number) = number / Divisor
I have implemented a compiler for a language that does this with different (less familiar) syntax. It's a bit more complicated than described above to handle local variables but it works very well for me.
I'm not super familiar with the details but I've heard that Shen has a really good type system. Aditya Siram addresses types at 12:00 in this video: https://youtu.be/lMcRBdSdO_U
Comments
What are the main differences between OcamML and a statically typed Lisp?
Type inference is probably the biggest thing. You would need explicit "phases" to expand macros, disallow macro expansion at runtime, and implement bi-directional type inference HM-style to get even close to what OCaml has.
To be honest, I'd kill for a Lisp that had the same type system as OCaml, but I suspect the closes we'll get is basically Rust (whose macro system is quite good).
Racket has Typed Racket, which while not Hindley-Miller can do some type inference.
There's also the plait language which says its type system is similar to ML: https://docs.racket-lang.org/plait/index.html
And Hackett, inspired by Haskell: https://lexi-lambda.github.io/hackett/
And Common Lisp has coalton: https://coalton-lang.github.io/
Most of those aren't really ready for production use except maybe Typed Racket, which I consider to be too "weak" and took a route with annotations that I'm not a fan of. Coalton is very interesting, I've been following it for a bit. Carp [0] is another one that I've been following.
[0]: https://github.com/carp-lang/Carp
Coalton is used in production for quantum computing systems and soft real-time control system automation. There are also (a small number of) Coalton jobs.
Quantum computing control systems is exactly the domain I've spent about half a decade doing, it's really not the production-like environment you think it is. Speed of iteration and flexibility to allow for changes to hardware is tantamount to success. It's also a lot easier to accept risk to breakages in the language when the author works at your company too.
Rust is categorically different from all of these other things. Lisps (and OCaml) all have interactivity as a core language feature - Rust is as non-interactive as it gets.
Compared to C, perhaps.
There is a simpler solution than type inference to removing type annotations while retaining types: remove the distinction between variable and type names. The way that you handle multiple instances of a type is to define an alias for the type. In pseudocode it might look like:
As compared to: I have implemented a compiler for a language that does this with different (less familiar) syntax. It's a bit more complicated than described above to handle local variables but it works very well for me.This doesn't remove them, it just moves them.
Is that language a Forth dialect by any chance?
I'm not super familiar with the details but I've heard that Shen has a really good type system. Aditya Siram addresses types at 12:00 in this video: https://youtu.be/lMcRBdSdO_U
Shen homepage: https://shenlanguage.org/
It's also pretty straightforward to use multiple coding styles in the lisps in question, regardless of the typing being static or not.
https://github.com/LuxLang/lux
The module and functor system. Also macros are much more a Lisp family thing.