% thunk(B:arglist(M)) ~ a thunk is something that eventually yields M outputs
classdef thunk
	properties (GetAccess=private, SetAccess=immutable)
		head %  ::  (A:arglist(N) -> B | thunk(B))
		args %  ::  A:arglist(N)
	end
	methods
		% thunk - create delayed evaluation
		function f=thunk(h,varargin), f.head=h; f.args=varargin; end
		function s=tostring(f), s=sprintf('%s(%s)',tostring(f.head),commalist(map(@tostring,f.args))); end
		function display(f), display([tostring(f),' :: thunk']); end

		% fforce - Completely force a thunk, if keep evaluating till not a thunk
		function f=fforce(f), while isa(f,'thunk'), f=force(f); end; end

		function varargout=force(f), [varargout{1:nargout}] = feval(f.head,f.args{:}); end

		% SUBSREF - Subscript referencing for FUNC objects
		function varargout=subsref(f,S)
			s=S(1);
			switch s.type
				case '()', [varargout{1:nargout}] = feval(f.head,f.args{:});
			end
		end
	end
end

function s=commalist(a)
	if isempty(a), s='';
	elseif length(a)==1, s=a{1};
	else s=[a{1},',',commalist(a(2:end))];
	end
end
