If only there were a language that allowed the flexibility of dynamic typing (for the benefits to rapid prototyping with poke and prod development via a REPL), as well as the higher level of feedback/warning to the developer to help prevent logic errors resulting from type incompatibilities.

Come to think of it, there is!

Stepping into the ring is Common Lisp!

I prefer my languages like my software, free (https://www.fsf.org/), and no language exists today that allows the developer more freedom in how the underpinnings of their program works than Common Lisp.

Now, Common Lisp is also a dynamically typed language (no static typing), but due to the power of the macro system, we can get it to act as if it had a type system (macros are not just simple text substitutions like they are in C, think of code that writes code, but more controlled than eval).

Lets look at one of the type failings (this matches up to our situation in the PHP example):

GLYPHS> ( defun mod! ( a b ) ( mod a b ) ) MOD! GLYPHS> ( defun call-mod () ( mod! 8 "three" ) ) CALL-MOD GLYPHS> ( call-mod ) ; Evaluation aborted on #<TYPE-ERROR expected-type: REAL datum: "three">.

Okay, it's a little better - we didn't end up with runaway code (Common Lisp catches the error for us in the REPL - that's read-eval-print-loop for the unwashed masses), but this can still end up tucked away as a runtime error until much later.

Lets add a simple static type system with a little haskell flavor (well, loosely based on it anyways).

( defmacro defn ( name types args &rest rest ) "Type safe defun" ( let ( ( types ( remove-if ( lambda ( x ) ( or ( equal '-> x ) ( equal ' → x ) ) ) types ) ) ) ` ( progn ( defun ,name ,args ,@ ( loop for arg in args for type in types collect ` ( check-type ,arg ,type ) ) ,@rest ) ( declaim ( ftype ( function , ( butlast types ) ,@ ( last types ) ) ,name ) ) ) ) )

I'll write a new post and give a more clear breakdown of the aforementioned code for you lispers at a later date (it's actually a pretty basic macro), but for now, lets see what happens when we define our function in a type safe manner using our new function defining call:

GLYPHS> ( defn mod ! ( integer -> integer -> integer ) ( a b ) ( mod a b ) ) ( MOD ! ) GLYPHS> ( defun call-mod () ( mod! 8 "three" ) ) ; in: DEFUN CALL-MOD ; (GLYPHS::MOD! 8 "three") ; ; caught WARNING: ; Constant "three" conflicts with its asserted type INTEGER. ; See also: ; The SBCL Manual, Node "Handling of Types" ; ; compilation unit finished ; caught 1 WARNING condition STYLE-WARNING: redefining GLYPHS ::CALL-MOD in DEFUN CALL-MOD

You'll notice that we received warnings at compile time (when the call-mod function was defined) instead of gladly accepting our obviously wrong call to the mod! function (mod is a built in function, so we used an exclamation mark to differentiate it, similar to the mod' definition in the haskell sample).

With the above macro, our REPL still compiles call-mod (which will hit a runtime error if encountered), but it gives a very clear message to the user that something was incorrect with their code.