These remind me of type driven development with holes. It’s not quite the same, since you aren’t really “running” parts of the program, instead you write typed definitions that might have some holes and the type system tells you what the type of the expression in the hole should be.
Edwin Brady has mentioned that he’d love to see someone build a Smalltalk style system on top of Idris, and I concur. It would be really interesting to see the safety of quantiative type theory (that just means some variables are linear) combined with the interactivity of Lisps.
The propagation of modified struct definitions is an interesting challenge, it sounds like database migrations. I’m actually skeptical if a language with neither type inference nor type annotations could deduce what to do with a struct change. Just because a type has a default value does not mean that it’s the correct one to use in a struct. Even if we restrict it to just be the “neutral” element, if you’ve studied some group theory you know that that is not determined by the type (the set) but the operations you use on it. Addition and multiplication have different neutral elements.
I think cubical types could be used here, but that’s just a hunch. If you have a well defined equality relation between the old type and the new, then a sufficiently smart runtime could rewrite all instances in-memory.
Another sticky issue is how this interacts with multiple threads. Would just pausing them during the rewrite be enough?
I’m not sure how the different Common Lisp implementation do it. In Erlang you have old code and new code. You can transform your data from old code to new code with a function. No need for deduction, it’s explicitly done by the programmer. Threads do not need to be paused: new invocations of functions are using the new code, old code still running old functions run the old code. At some point you need to ensure that no old code is running at all before making a new code swap.
In practice this is rarely used outside of embedded systems.
Yeah, the transparent data migration was something I hadn’t heard before. I can see that easily getting hairy in practice. Say you change the default value of a slot. Will CL go through all objects in memory and update the slot? What if someone had explicitly reset the slot in the past to the old default value?
Hmm, here’s a SBCL session that seems to contradict him:
> (defclass point () (x y))
#<STANDARD-CLASS COMMON-LISP-USER::POINT>
> (defvar p1 (make-instance 'point))
#<POINT {1001C4F873}>
> (inspect p1)
The object is a STANDARD-OBJECT of type POINT.
0. X: "unbound"
1. Y: "unbound"
> (defclass point () ((x :initform 3) y))
#<STANDARD-CLASS COMMON-LISP-USER::POINT>
> (inspect p1)
The object is a STANDARD-OBJECT of type POINT.
0. X: "unbound"
1. Y: "unbound"
I set the default value of a slot, but it wasn’t picked up by existing objects. Same result through an accessor as well.
It works for adding new slots, but won’t update default values after the fact:
* (defclass point () (x y))
* (defvar p1 (make-instance 'point))
* (inspect p1)
The object is a STANDARD-OBJECT of type POINT.
0. X: "unbound"
1. Y: "unbound"
* (defclass point () (x y (z :initform 4)))
* (inspect p1)
The object is a STANDARD-OBJECT of type POINT.
0. X: "unbound"
1. Y: "unbound"
2. Z: 4
* (defclass point () ((x :initform 3) y (z :initform 4)))
* (inspect p1)
The object is a STANDARD-OBJECT of type POINT.
0. X: "unbound"
1. Y: "unbound"
2. Z: 4
I love the different perspective that Chas Emerick brings with OCaml. He has a Lisp background so it’s even more interesting.
Thanks, you nudged me to go read the links more carefully.
I actually really liked the Mikel Elvins blog post.
Also, this brief comment by fogus.
These remind me of type driven development with holes. It’s not quite the same, since you aren’t really “running” parts of the program, instead you write typed definitions that might have some holes and the type system tells you what the type of the expression in the hole should be. Edwin Brady has mentioned that he’d love to see someone build a Smalltalk style system on top of Idris, and I concur. It would be really interesting to see the safety of quantiative type theory (that just means some variables are linear) combined with the interactivity of Lisps.
The propagation of modified struct definitions is an interesting challenge, it sounds like database migrations. I’m actually skeptical if a language with neither type inference nor type annotations could deduce what to do with a struct change. Just because a type has a default value does not mean that it’s the correct one to use in a struct. Even if we restrict it to just be the “neutral” element, if you’ve studied some group theory you know that that is not determined by the type (the set) but the operations you use on it. Addition and multiplication have different neutral elements. I think cubical types could be used here, but that’s just a hunch. If you have a well defined equality relation between the old type and the new, then a sufficiently smart runtime could rewrite all instances in-memory.
Another sticky issue is how this interacts with multiple threads. Would just pausing them during the rewrite be enough?
🤔
I’m not sure how the different Common Lisp implementation do it. In Erlang you have old code and new code. You can transform your data from old code to new code with a function. No need for deduction, it’s explicitly done by the programmer. Threads do not need to be paused: new invocations of functions are using the new code, old code still running old functions run the old code. At some point you need to ensure that no old code is running at all before making a new code swap.
In practice this is rarely used outside of embedded systems.
Yeah, the transparent data migration was something I hadn’t heard before. I can see that easily getting hairy in practice. Say you change the default value of a slot. Will CL go through all objects in memory and update the slot? What if someone had explicitly reset the slot in the past to the old default value?
Hmm, here’s a SBCL session that seems to contradict him:
I set the default value of a slot, but it wasn’t picked up by existing objects. Same result through an accessor as well.
It works for adding new slots, but won’t update default values after the fact:
Even this seems like black magic to me.