return function(meta)
	local class = {
		id = function(instance)
			return getmetatable(instance) == meta
		end;
		
		merge = function(dest, src)
			for k,v in pairs(src) do
				dest[k] = v
			end
			if meta.clone then meta.clone(dest) end
		end;
	}
	class.clone = function(instance)
		local new = {}
		class.merge(new, instance)
		setmetatable(new, meta)
		return new
	end
	class.change = function(orig, delta)
		local new = class.clone(orig)
		class.merge(new, delta)
		return new
	end
	class.mk = function(...)
		local new
		if #{...} == 1 then
			if class.id((...)) then
				-- default copy constructor
				return class.clone((...))
			elseif meta.cast then
				if type((...)) == 'table' then
					for from, conv in pairs(meta.cast) do
						if from.id and from.id((...)) then
							new = conv((...))
							goto setup
						end
					end
				else
					local conv = meta.cast[type((...))]
					if conv then
						new = conv((...))
						goto setup
					else assert(false) end
				end
			end
		end
		new = meta.construct(...)

		::setup::
			setmetatable(new, meta)
			return new
	end
	setmetatable(class, { __call = function(self, ...)
		return self.mk(...)
	end })
	return class
end
