Im rethinking union operations. I think I should refactor some code while I'm at it. (also, env escaping included!) newUnion() - can yield empty union or one env union - used in function instantiation getUnion() - miau (return reference too) cloneUnion() - used in instantiation too. new id n shii. - both should use some addUnion() function, which just adds a new Union - this will make changing the repr easier. env escaping - go through each environment in the union and do the thing, miau :3 ---- Im thinking how to handle unions here. Two problems: 1. generating unions and the mono/c is kind of a spaghetti (actually not that much, but some stuff should be deduplicated, because it actually shares the behavior.) TWO functions would need to be generated depending on the callsite: *if any function in the union has an environment or not.* however, we can generate the environment in the correct place and function on-demand on callsite. ALTHOUGH, a simple hacky solution while we figure stuff out would be to just put an Env/Void* there and optionally put a NULL as first argument. this is temporary of course, but might solve the problem for now. 2. it would be nice if we could generate a flat assembly. to do this, it would be nice if the functions were explicitly generated and in the correct order. in this case, knowing the calling context would be nice. 3. eliminating unused functions from variables, eg: x =& fn: 1 fn miau() y = 420 x <&= fn: y # miau not called. stuff should be eliminated from the union. x() --- how should i divide unions and shii unions /= (envs == function) so, env id can be shared with a function, because it's a one to one relationship. but a union can be of multiple things. --- for mono-unions, we just make a second mono-set. at function expansion, when we operate with a match, we add the finished mono-set of scheme unions into current unions (mapped obviously.) --- 27.05.26 I really need to compile all the notes now. check the next "part" for what i mean. I've managed to bring in the second, unionfull version up to speed with the current one. obviously, the implementation is still scuffed. env expansion will be broken. but, there is a serious (200%) monomorphisation slowdown - I don't know why that happens. I'll keep the old kkc as a sanity check as `kkc-env`. compilation of kd.kkc: === compilation time: 165ms === === mono call graph: 23ms === === mono compilation: 222ms === === writing and compiling (C) time: 1207ms === === compilation time: 155ms === === mono call graph: 27ms === === mono compilation: 455ms === === writing and compiling (C) time: 1191ms === -- about the notes and stuff. *complete with one hack. In UnionRef Comparator, there is a hack to compare the union STRUCTURALLY if it only has one env, like in most cases. all of my programs compile now, including kd. the thing is, check this: ``` fn a(al) _ = al allocate(420) as Slice U8 al = Alloc.idc() a(al) a(al) ``` If we *don't* compare unions structurally, we will generate two a() functions. that's because we call `instantiateFunction` for `allocate` twice when instantiating `a`. These create new unions (it also created new env slots in TypeContext, but we later compared structurally anyway, because it was cheap). So, the end goal is to either compare structurally OR unions should probably be used pretty sparingly (unless the allocator interface) we can then optimize for the happy path, by just having two (or three counting the special empty case) cases for a union: Empty OneEnv MoreEnvs This may also allow me to implement the union stuff gradually so I don't get burned out like right now. But yeah, there is a lot of small stuff. -- Like env expansion. We should probably keep track of union level, so that we can make a "subslice" of environments which "matter" in the environment. If not, the mono thing will contain all env instantiations including the outer env's stuff. --- maybe I should think about it in terms of general value union? like normal types too: (Int | String) can be "used" provided they have the same typeclass. anyway these concepts can't be exactly the same, otherwise we can't unify function's parameters and return value. how would I know that a union is closed? that it does not interact with type scheme? ``` fn dodis(al) [allocate u1] _ = al allocate(420) # currently al = Alloc.idc() dodis(al) [allocate u2] dodis(al) [allocate u3] # what should happen: al = Alloc.idc() aa = Arena.mk() dodis(al) [allocate u2] dodis(al) [allocate u2] dodis(aa) [allocate u3] dodis(aa) [allocate u3] ```