Justin Blank
11/23/2023, 1:59 PMKartik Agaram
Justin Blank
11/23/2023, 3:13 PMKonrad Hinsen
11/23/2023, 3:49 PMA saner option is to recompile and reload the entire codebase whenever a change is made, while preserving the state of the heapAre there any real-life implementations of that idea?
jamii
11/23/2023, 6:42 PMAre there any real-life implementations of that idea?Sort of erlang. It's designed for upgrading production services so it has a lot more toggles and manual controls than you'd want for interactive editing, and there's no undo if you screw up the migration. But it does work fairly well. Certainly compared to everything else. I also see a lot of game developers do this in C and it seems pretty useful even with the complete lack of type safety.
jamii
11/23/2023, 6:45 PMJustin Blank
11/24/2023, 10:12 AMjamii
11/24/2023, 5:15 PMKartik Agaram
shalabh
11/24/2023, 6:35 PMshalabh
11/24/2023, 6:38 PMFor a start, you can actually find the code as a single artifact rather than it being the product of a log of mutations.Some of this gets lost even in dead-langs when youâre dealing with macros, metaprogramming or subsclassing at some level of complexity. Not only do you have to simulate the runtime semantics in your head, you also have to simulate the type checker and compiler semantics.
shalabh
11/24/2023, 6:41 PMJustin Blank
11/24/2023, 6:44 PMJustin Blank
11/24/2023, 6:45 PMKartik Agaram
I believe the issues described with live systems are not actually solved with dead systems, but just deferred until later. Thatâs because dead systems donât often represent entire systems but only a slice of the system.. upgrading [a] downstream service may change or delete an API method and it leave it incompatible with the caller.This depends on the context. If you think of the upstream and downstream as within a common ownership boundary, then such checks are valuable. However, if they span ownership boundaries then these checks can seem onerous. You're right that dead systems work almost accidentally for the second case, by just not doing some work. Ideally we'd have both tools in our pocket, and the flexibility to select from them depending on the situation. However, this is a lot of complexity, and all code carries costs. So worse may be better here. This situation is analogous to structured editing like in @alltom's recent submission https://futureofcoding.slack.com/archives/C5T9GPWFL/p1700405882617679. It's easy for a structured editor to feel like an overbearing presence. The implementor has to juggle both technical complexity and UX nuance. Meanwhile plain text is often "good enough" and so we continue to muddle along..
shalabh
11/24/2023, 7:45 PMItâs possible to do monitoring in your runtime.Yeah, the key requirement is version-awareness - whether the endpoint name encodes the version or some introspection api returns the version of the service, any strategy needs a representation of the version of functions etc within the system itself, and should allow multiple versions to coexist. An interesting implementation here is gemstone smalltalk - it is a live system where you can evolve the schema by providing migration methods and the objects can be migrated to newer definitions either opportunistically or in batch.
shalabh
11/24/2023, 7:46 PMKartik Agaram
shalabh
11/24/2023, 8:11 PMshalabh
11/24/2023, 8:17 PMKonrad Hinsen
11/25/2023, 5:01 PMDynamic programming languages face semantic and performance challenges in the presence of features, such
as eval, that can inject new code into a running program. The Julia programming language introduces the
novel concept of world age to insulate optimized code from one of the most disruptive side-effects of eval:
changes to the definition of an existing function. This paper provides the first formal semantics of world age
in a core calculus named Juliette, and shows how world age enables compiler optimizations, such as inlining,
in the presence of eval. While Julia also provides programmers with the means to bypass world age, we found
that this mechanism is not used extensively: a static analysis of over 4,000 registered Julia packages shows
that only 4â9% of packages bypass world age. This suggests that Juliaâs semantics aligns with programmer
expectations.
jamii
11/26/2023, 12:31 AMjulia> foo(x, y) = x + y
foo (generic function with 1 method)
julia> bar = x -> foo(x, 1)
#3 (generic function with 1 method)
julia> bar(1)
2
julia> @code_llvm bar(1)
; @ REPL[1]:1 within `#3`
define i64 @"julia_#3_122"(i64 signext %0) #0 {
top:
; â @ REPL[7]:1 within `foo`
; ââ @ int.jl:87 within `+`
%1 = add i64 %0, 1
; ââ
ret i64 %1
}
julia> Base.delete_method(@which foo(1,1))
julia> bar(1)
ERROR: MethodError: no method matching foo(::Int64, ::Int64)
Stacktrace:
[1] (::var"#3#4")(x::Int64)
@ Main ./REPL[1]:1
[2] top-level scope
@ REPL[6]:1
julia> @code_llvm bar(1)
; @ REPL[1]:1 within `#3`
; Function Attrs: noreturn
define void @"julia_#3_133"(i64 signext %0) #0 {
top:
%1 = alloca [2 x {}*], align 8
%gcframe2 = alloca [3 x {}*], align 16
%gcframe2.sub = getelementptr inbounds [3 x {}*], [3 x {}*]* %gcframe2, i64 0, i64 0
%.sub = getelementptr inbounds [2 x {}*], [2 x {}*]* %1, i64 0, i64 0
%2 = bitcast [3 x {}*]* %gcframe2 to i8*
call void @llvm.memset.p0i8.i32(i8* noundef nonnull align 16 dereferenceable(24) %2, i8 0, i32 24, i1 false)
%thread_ptr = call i8* asm "movq %fs:0, $0", "=r"() #6
%ppgcstack_i8 = getelementptr i8, i8* %thread_ptr, i64 -8
%ppgcstack = bitcast i8* %ppgcstack_i8 to {}****
%pgcstack = load {}***, {}**** %ppgcstack, align 8
%3 = bitcast [3 x {}*]* %gcframe2 to i64*
store i64 4, i64* %3, align 16
%4 = getelementptr inbounds [3 x {}*], [3 x {}*]* %gcframe2, i64 0, i64 1
%5 = bitcast {}** %4 to {}***
%6 = load {}**, {}*** %pgcstack, align 8
store {}** %6, {}*** %5, align 8
%7 = bitcast {}*** %pgcstack to {}***
store {}** %gcframe2.sub, {}*** %7, align 8
%8 = call nonnull {}* @ijl_box_int64(i64 signext %0)
%9 = getelementptr inbounds [3 x {}*], [3 x {}*]* %gcframe2, i64 0, i64 2
store {}* %8, {}** %9, align 16
store {}* %8, {}** %.sub, align 8
%10 = getelementptr inbounds [2 x {}*], [2 x {}*]* %1, i64 0, i64 1
store {}* inttoptr (i64 140053833609312 to {}*), {}** %10, align 8
%11 = call nonnull {}* @ijl_apply_generic({}* inttoptr (i64 140053835183152 to {}*), {}** nonnull %.sub, i32 2)
call void @llvm.trap()
unreachable
}
jamii
11/26/2023, 12:35 AMKonrad Hinsen
11/26/2023, 10:52 AMjonathoda
11/26/2023, 8:34 PM