Eigenstate: myrddin-dev mailing list

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Exploring Types and Memory Management


So, it's clear that it would be useful to have some sort of language level
help with managing lifetimes of various types of data. There's a decent
amount of discussion on this, on and off, on IRC.


Options: Owned types, garbage collection, ...something else...?

Currently exploring owned types:

Owned Types:

	The current line of thinking is around 'owned' types (yes, a bit similar to
	C++ std::unique_ptr, or Rust's default pointer), where there is one
	distinguished instance of a type alive at all times, which may be used either
	0 or 1 times, with an escape hatch to take a reference.

	So, the first thing here would be to add an owned attribute to these types:

		var x : owned thing#	/* an owned pointer of thing */

	There would also be a trait for cleaning up the resources within an owned
	value. For example, a sane fallback for any owned pointer could be freeing:

		impl disposable owned @a#
			const __free__ = {val
				std.free(val)
			}
		;;

	Finally, for an owned type, there must only be one instance live from creation
	to the instance falling out of scope, which means any assignments, passing to
	functions, and so on, must invalidate any future uses of the value.  For example:

		const f = {
			var x : owned thing# = std.mk([.thingy=123])
			/* ownership is transferred */
			var y = x
			/* use(x) would be an error here; y now owns the value */
			/* ownership is transferred */
			g(y) 
			/* use(y) would be an error; the ownership has moved
			 * into g */
		}
		const g = {x : owned thing#
			/* the value is freed here */
		}

	The issue with this, of course, is that it's rather restrictive. So
	there needs to be a loophole to get a value out of an owned pointer:


		const f = {
			var x : owned thing# = std.mk([.thingy=123])
			/* As before, ownership is transferred */
			var y = x
			/* 'get' will strip off the ownedness */
			g(get y) 
		}

		const g = {x : thing#
			/* x is not owned, so no problem; just don't hold on
			 * to it too long. */
		}

	So, overall:
	  + Useful for multiple types
	  + Guarantees cleanup
	  - Potentially restrictive
	  - Might not work super-well for things like reference counting.
	  - Might lead into the operator overloading quagmire.

GC:

	This wouldn't involve much new. std.free() and friends would go away,
	you'd still need to deal with closing files and so on.

	I'm assuming most people are at least somewhat familiar with the ideas
	behind a GC, so...

	So, overall:
	  + Simple for users
	  + No new concepts
	  + Makes Myrddin theoretically memory safe (modulo cast-based
	    intentional holes in the type system)
          - Hard to implement well
	  - Makes the runtime much bigger/harder to port.
	  - Means you need to implement more if you want to write a kernel or
	    similar shit.

Other ideas:

	- Regions, specifically region inference: https://en.wikipedia.org/wiki/Region-based_memory_management
	- Other thoughts?

-- 
    Ori Bernstein

Follow-Ups:
Re: Exploring Types and Memory ManagementAndrew Chambers <andrewchamberss@xxxxxxxxx>