They are computationally equivalent. Except that you can't typecheck actor calculus (since you can send anything to an actor) but you CAN typecheck pi calculus (channels).
Runtime checks can be implemented anywhere — I did similar things for Python and JavaScript, as many others here did, I suppose.
It's better than nothing, but it still bombs in prod, instead of giving you a static analysis error.
The nice thing about static types being a core part of the language (ML, Haskell, C#,...) is that you have concise syntax and type inference at all the required spots. Otherwise (Clojure) you have to be pretty verbose, as if you were writing Java.
In all fairness the dynamic nature of LISP is an advantage for the people using it. Yes you can talk about having bombs in production that would otherwise have been caught by the compiler, but unless you have a really expressive type system, like Haskell, then it's not going to help you much. If you're into static type safety, than Go is a really poor choice.
And this is one of my problems with it. Either a language is statically type safe, or it is dynamic. You can then fully embrace the mentality of those languages.
However Go is in the league of languages created to be popular with the average Joe and just like Java before it, it's neither here nor there.
A zip code is a string even though it looks like a number.
Likewise C/Java/Go "types" are tags even though they look like types. Real types are numbers that obey type algebra, which C/Java/Go have no idea exists.
In regards to Common Lisp, depending on which implementation you use you may get some sort of type checking at compile time.
For example, if you save the following code[1] into a file and try to load it into SBCL, it will give a compile time error:
(ql:quickload 'defstar)
(use-package 'defstar)
(declaim (optimize (debug 3) (speed 0) (safety 3)))
(defun* factorial ((x (integer 0 *))) ;; x is an integer from 0 up to +inf
(if (zerop x)
1
(* x (factorial (1- x)))))
(defun main ()
(factorial "foo") ;; Compile time error: "foo" conflicts with its assert type UNSIGNED-BTYE
(factorial -1) ;; Compile time error: -1 conflicts with its assert type UNSIGNED-BTYE
(factorial 1.0) ;; Compile time error: 1.0 conflicts with its assert type UNSIGNED-BTYE
(factorial 0)) ;; Works
And when you load the file into SBCL you get the following error message (assuming you have Quicklisp installed):
$ sbcl --load compile-time-safety.lisp
This is SBCL 1.2.7, an implementation of ANSI Common Lisp.
More information about SBCL is available at <http://www.sbcl.org/>.
SBCL is free software, provided as is, with absolutely no warranty.
It is mostly in the public domain; some portions are provided under
BSD-style licenses. See the CREDITS and COPYING files in the
distribution for more information.
To load "defstar":
Load 1 ASDF system:
defstar
; Loading "defstar"
; file: /home/rol/src/lisp/compile-time-safety.lisp
; in: DEFUN MAIN
; (FACTORIAL "foo")
;
; caught WARNING:
; Constant "foo" conflicts with its asserted type UNSIGNED-BYTE.
; See also:
; The SBCL Manual, Node "Handling of Types"
;
; compilation unit finished
; caught 1 WARNING condition
It's not as nice as Haskell's type system, but it's something :)
The main problem with this sort of compile-time checking is that it's implementation dependent, so some Common Lisp implementation may not provide it.
You can configure the defstar library that I used on this code so that it automatically adds calls to "(check-type ...)". But then you are back at doing run-time checking of types.
It's pretty much just a nicer way to write all of the "(declaim ...)" and "(declare ...)" needed to provide the most information to the Common Lisp type system.
Remote: Yes
Willing to relocate: No
Technologies: Rust, Erlang/Elixir, Distributed Systems, Performance sensitive systems, Linux, ...
Résumé/CV: https://www.linkedin.com/in/karolskocik/
Email: karol [dot] skocik [at] gmail [dot] com
reply