12 ms·
Although I love LISP and it will always have a special place in my heart, there is power in the assistance the compiler for languages with powerful type systems
by leoh 2y ago
Although I love LISP and it will always have a special place in my heart, there is power in the assistance the compiler for languages with powerful type systems like Rust.
Writing in LISP can benefit from, I believe, a kind of psychological stance of wabi-sabi (non-perfectionism), for (barring exotic LISPs with sophisticated type systems), it will always be more challenging to refactor and get things just right than with languages like Rust.
Given the pressure of writing code for firms that can demand we are perfect or pay the price [being on call, etc.] (ie working for perfectionistic firms with perfectionistic customers — it’s a recursive problem) also can push us towards languages with powerful compilers.
Then again, maybe we need to invert things — use the languages we love that are not “perfect” but are so easy to love; for perhaps that love can just as recursively emanate outwards as would-be perfectionism…
- ramenbytes 2y agoSeen this yet? https://github.com/coalton-lang/coalton https://github.com/coalton-lang/coalton
- iLemming 2y agoWhat makes you think that Lisps don't have powerful type systems as well? For many years significant research has been conducted on type systems specifically in Racket. In Clojure, you can do interesting things, such as defining complex data and function specifications like "this function should always return prime numbers", which generally speaking is difficult to achieve in many other languages.
- lispm 2y agospecs are not types. Your Clojure compiler will have a hard time statically checking specs at compile time.
- iLemming 2y agoYes, Clojure.specs are technically not types, they cannot enforce constraints broadly at compile-time. Yet they still can enforce finer-grained constraints at runtime. From the practical point of view, specs are really great tool for building robust software and have other properties like simplifying property-based testing through automatic generation of test cases. Take for example a trivial example when we need to validate user data with the following constraints: - Age: Must be between 18 and 120. - Name: Must be a non-empty string. - Email: Must follow a valid email format. - Credit Card Number: Must pass the Luhn algorithm check. Something like that is pretty straightforward to achieve in Clojure.spec within 20 lines of code. It's close to impossible to get with any advanced static type system, because type systems can't deal well with custom constraints; they often can't offer detailed, human-readable error messages for the cases like that; they often can't describe complex and nested data structures, or trying to do so would be cumbersome and less expressive. So, they are not types. Okay. Cisco built their cybersecurity platform using Clojure. The market capitalization is about $180B. I imagine this would be a few dozen billions more if they had chosen to use an "actual type system", amiright? ---- edit: After thinking about it a bit more, I have to admit that I spoke too harshly. It's not "close to impossible" You probably can create a type that checks for these constraints, but you would likely need to use a dependent type system or refined types, perhaps something like LiquidHaskell., and it still would be very tricky, especially with the credit card part. In Clojure.spec - it's very trivial.
- lispm 2y agoThere is nothing which would require me to use Clojure to check those things. For example... one can easily express all that in Common Lisp: https://www.lispworks.com/documentation/HyperSpec/Body/04_b.htm https://www.lispworks.com/documentation/HyperSpec/Body/04_b.... CL-USER 9 > (defun non-empty-string-p (string) (and (stringp string) (plusp (length string)))) NON-EMPTY-STRING-P CL-USER 10 > (deftype name-type () `(and string (satisfies non-empty-string-p))) NAME-TYPE CL-USER 11 > (deftype age-type () `(integer 18 120)) AGE-TYPE CL-USER 12 > (defun foo (age name) (declare (type age-type age) (type name-type name)) (print (list age name))) FOO also, avoiding SATISFIES: CL-USER 4 > (deftype name-type () `(and string (not (string 0)))) NAME-TYPE CL-USER 5 > (typep "hello" 'name-type) T CL-USER 6 > (typep "" 'name-type) NIL It then has assertions integrated with the condition system, which makes this easily checkable at runtime. SBCL has this extended with type declarations as assertions: http://sbcl.org/manual/index.html#Handling-of-Types http://sbcl.org/manual/index.html#Handling-of-Types Eiffel had promoted Design by Contract for a long time. > Cisco built their cybersecurity platform using Clojure. Cisco uses a lot of software. Your Clojure marketing attempts are getting silly.
- NikkiA 2y agoLisp's type system is fine. (defstruct adult (name 'error :type string) (age 'error :type (integer 18 200))) * (make-adult :name "Bob" :age 3) debugger invoked on a TYPE-ERROR @5529ACEE in thread #<THREAD tid=426288 "main thread" RUNNING {1003F000A3}>: The value 3 is not of type (INTEGER 18 200) when setting slot AGE of structure ADULT * (make-adult :age 18) debugger invoked on a TYPE-ERROR @5529ACF2 in thread #<THREAD tid=426288 "main thread" RUNNING {1003F000A3}>: The value ERROR is not of type STRING when setting slot NAME of structure ADULT
- lispm 2y agoNote that this behavior is completely implementation specific and totally undefined in the Common Lisp standard.
- ducktective 2y agoWhat is standard CL way to enforce these type conditions (apart from declaim)? Some spec library similar to the one Clojure has?
- NikkiA 2y ago`declaim` is just as implementation-dependant as the :type specifiers for defstruct, thus by lispm's rules, there are no 'standard' ways to apply type conditions. But then, the standard /is/ useless.
- lispm 2y agoThe standard leaves it to the implementations/implementors to decide what to do. An implementation can check all these type constraints. It may not do it, for example, if code gets compiled with the opimization quality SAFETY of 0 (and possibly SPEED = 3) or similar. It may not do it all. There are implementations which check these type constraints. So it is best to check the manual or ask the maintainers. IIRC there were implementations which added it on user requests. But don't think all "Common Lisp" implementations will check those constraints, for example ABCL does not. Some don't and in others it can be turned off.