6 ms·
Are there people saying that you can't implement Common Lisp in OCaml/Haskell, with heterogeneous structures, dynamically typed and so on? You certainly can, bu
by junke 10y ago
Are there people saying that you can't implement Common Lisp in OCaml/Haskell, with heterogeneous structures, dynamically typed and so on? You certainly can, but that's not an easy task.
If your language does not support natively the set of rules you really like to enforce, you can still work in a style you like provided you establishes conventions and abstractions around it.
So if you were actually interested in developing with, say, purely functional collections in Common Lisp, you would use the FSET[0] library:
(equalp (fset:bag 1 2)
(fset:bag 1 2))
T
(equalp (fset:bag 1 2)
(fset:bag 2 1))
T
That's exactly the same approach that people use when they do "C with classes", by reifying the abstraction into the host language.
Unsurprisingly, data-structures in FSET are implemented as Common Lisp structures, which allows having read-only slots as well as type specifiers.
Since we only want to have read-only slots, let's define a wrapper around defstruct (http://pastebin.com/30D6BnRg http://pastebin.com/30D6BnRg):
(deftype tree () '(or node null leaf))
(deft leaf data)
(deft node
(left nil :type tree)
(right nil :type tree))
(equalp (node (node (leaf 3) (leaf 4)) nil)
(node (node (leaf 3) (leaf 4)) nil))
=> T
(compile nil
(lambda ()
(setf (data (leaf 3)) 0)))
; compilation unit finished
; Undefined function:
; (SETF DATA)
; caught 1 STYLE-WARNING condition
(compile nil
(lambda ()
(node "strange" "node")))
; caught WARNING:
; Constant
; "strange" conflicts with its asserted type
; (OR COMMON-LISP-USER::NODE NULL COMMON-LISP-USER::LEAF).
; See also:
; The SBCL Manual, Node "Handling of Types"
The above messages are caught while compiling the functions, not when calling them.
The language specification does not give you another way to mutate structure slots other than opt-in accessors. Granted, your implementation is probably smart enough to let you mutate it with (SETF SLOT-VALUE), but as a CL developer, you know that its bad to use it and that you should rely on accessors provided by the API.
The advantage of Lisp at this point is that you can wrap your abstraction so tightly that it is indistinguishable from core features.
But maybe that's not enough for you, because there are not enough checks and you have your own idea of how things should work and you think you should do more compilation and less interpretation.
The compiler is available right here too, so you can even use it from your own compiler (during macroexpansion if you need) and do the analyzes you like. Or, you can even output a completely different executable file. Then you code a compiler in your new language because that's what grown-up languages do.
So, in order to do ML in Lisp, you develop your own DSL, an interpreter and later you bootstrap a compiler which is then self-sufficient to exist alone. That's roughly what happened decades ago:
"Edinburgh LCF, including the ML interpreter, was implemented in Lisp"[1][2].
ACL2, an applicative, side-effect free subset of Common Lisp used for first-order theorem proving (Boyer-Moore), shares the same approach.
But the language that exists now, even though it does perfectly what it was designed for, is not Lisp anymore, and can't do exactly the things we actually like to do in Common Lisp.
[0] https://common-lisp.net/project/fset/Site/index.html https://common-lisp.net/project/fset/Site/index.html
[1] https://ocaml.org/learn/history.html https://ocaml.org/learn/history.html
[2] https://www.cl.cam.ac.uk/~mjcg/papers/HolHistory.pdf https://www.cl.cam.ac.uk/~mjcg/papers/HolHistory.pdf
- catnaroek 10y ago> If your language does not support natively the set of rules you really like to enforce, you can still work in a style you like provided you establishes conventions and abstractions around it. That only works if the language provides some way to define abstract types. For instance, ML doesn't have any concrete type only inhabited by balanced binary trees, but an abstract type can provide operations never destroy balance. All is good as well as long as clients don't inspect the internal representation, which ML can and does enforce. However, unenforced conventions are just wishful thinking. > but as a CL developer, you know that its bad to use it and that you should rely on accessors provided by the API. As a C programmer, I know that it's very bad to use memory after freeing it. > But maybe that's not enough for you, because there are not enough checks and you have your own idea of how things should work and you think you should do more compilation and less interpretation. More precisely, I think computers are several orders of magnitude better than humans at applying formal logic, which ultimately boils down to mechanically interpretable rules of inference. I want a division of labor between humans and computers that leverages the strengths of both to the maximum possible extent. Humans shouldn't be in the business of performing perfectly automatable trivial checks. > and can't do exactly the things we actually like to do in Common Lisp. Actually, mutable objects, even objects whose class (not to be confused with type!) can change at runtime, are easy to implement in ML. The main reason why this programming style isn't as popular in ML-land is because it results in code that's harder to understand both for humans and for machines. Metaprogramming itself is admittedly not ML's strength, though. Here I look up more to Racket's `syntax-parse`, which is both friendlier and more scalable than `defmacro`.
- qwertyuiop924 10y ago>Metaprogramming itself is admittedly not ML's strength, though. Here I look up more to Racket's `syntax-parse`, which is both friendlier and more scalable than `defmacro`. ...No, not really. syntax-parse and defmacro are good at different things. While unhygenic macros are useless, procedural macros have their place. >unenforced conventions are just wishful thinking. Tell that to everyone developing java. :-). But one of the good things about lisp is that using syntactic extensions, we CAN enforce our own conventions. Just so long as you remember to unbind the native functions... >Actually, mutable objects, even objects whose class (not to be confused with type!) can change at runtime, are easy to implement in ML. The main reason why this programming style isn't as popular in ML-land is because it results in code that's harder to understand both for humans and for machines. Look, you're clearly a hardcore ML fan. You're not going to like Lisp. Lisp and ML are polar opposites of each other. Lisp is hyper-dynamic, ML is incredibly static. If you're the kind of person who can't last 5 minutes without typechecking and immutability guarantees, you'll either flee lisp quickly, or build your own bizarre ML inside of it.