
/* reftypes.q: tuples, lists and streams of references
   $Id: reftypes.q,v 1.15 2007/10/04 01:48:37 agraef Exp $ */

/* This file is part of the Q programming system.

   The Q programming system is free software; you can redistribute it and/or
   modify it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 2, or (at your option)
   any later version.

   The Q programming system is distributed in the hope that it will be
   useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */

import clib;

/* Some convenience functions for working with tuples, lists and streams of
   references. These are based on a proposal by John Cowan on the Q mailing
   list (2007/09/30). Written 2007/10/01 by Albert Graef. */

/* NOTE: These are still experimental, so the interface is not carved in stone
   yet. Comments are appreciated. */

public reftuple Xs, reftuple2 Xs, reflist Xs, reflist2 Xs, refstream Xs,
  refstream2 Xs;

/* Construction functions. For convenience, we allow all sequence types to be
   initialized from either tuple, list or stream data. 2D (matrix-like)
   sequences are constructed by reftuple2, reflist2 and refstream2; these will
   always be homogeneous (e.g., a list of lists, but not a tuple of lists). */

reftuple Xs:Tuple		= map ref Xs;
reftuple Xs:List		= tuple (map ref Xs);
reftuple Xs:Stream		= reftuple (list Xs);

reftuple2 Xs:Tuple		= map reftuple Xs;
reftuple2 Xs:List		= tuple (map reftuple Xs);
reftuple2 Xs:Stream		= reftuple2 (list Xs);

reflist Xs:Tuple		= reflist (list Xs);
reflist Xs:List			= map ref Xs;
reflist Xs:Stream		= reflist (list Xs);

reflist2 Xs:Tuple		= reflist2 (list Xs);
reflist2 Xs:List		= map reflist Xs;
reflist2 Xs:Stream		= reflist2 (list Xs);

/* Note that reference streams *must* always be memoized, otherwise the
   references would be reconstructed over and over again as the stream is
   inspected or modified, and thus nothing would be remembered at all. */

refstream Xs:Tuple		= refstream (stream Xs);
refstream Xs:List		= refstream (stream Xs);
refstream Xs:Stream		= lazy (map ref Xs);

refstream2 Xs:Tuple		= refstream2 (stream Xs);
refstream2 Xs:List		= refstream2 (stream Xs);
refstream2 Xs:Stream		= lazy (map refstream Xs);

/* These work like mktuple/2, mklist/2 and mkstream/2, but construct a
   sequence of references all initialized to the given value instead. */

public mkreftuple X N, mkreftuple2 X NM, mkreflist X N, mkreflist2 X NM,
  mkrefstream X, mkrefstream2 X;

mkreftuple X N:Int		= map ref (mktuple X N);
mkreftuple2 X (N:Int,M:Int)	= map (map ref) (mktuple (mktuple X M) N);

mkreflist X N:Int		= map ref (mklist X N);
mkreflist2 X (N:Int,M:Int)	= map (map ref) (mklist (mklist X M) N);

mkrefstream X			= lazy (map ref (mkstream X));
mkrefstream2 X			= lazy (map (lazy.ref)
					(mkstream (mkstream X)));

/* Pseudo type-checkers. Please note that, for efficiency, these only look at
   the first component to see whether it is a reference. */

public isreftuple Xs, isreftuple2 Xs, isreflist Xs, isreflist2 Xs,
  isrefstream Xs, isrefstream2 Xs;

isreftuple ()			= true;
isreftuple (_:Ref|_)		= true;
isreftuple _			= false otherwise;

isreftuple2 ()			= true;
isreftuple2 (X|_)		= isreftuple X;
isreftuple2 _			= false otherwise;

isreflist []			= true;
isreflist [_:Ref|_]		= true;
isreflist _			= false otherwise;

isreflist2 []			= true;
isreflist2 [X|_]		= isreflist X;
isreflist2 _			= false otherwise;

isrefstream {}			= true;
isrefstream {_:Ref|_}		= true;
isrefstream _			= false otherwise;

isrefstream2 {}			= true;
isrefstream2 {X|_}		= isrefstream X;
isrefstream2 _			= false otherwise;

/* Operating on a reference tuple/list/stream as a whole. We overload 'get' so
   that it applies itself to every member, yielding a new sequence with all
   the values from the original one, and 'put' so that it updates all members
   of the sequence with the given tuple, list or stream of values,
   respectively. We also provide new functions 'fill', which fills references
   with a given value, and 'putmap', which does a "destructive map" by
   combining get, map and put on a reference. Note that 'fill X' works just
   like 'putmap (cst X)', but is implemented directly for efficiency. */

public fill Y X, putmap F X;

fill Y X:Ref			= put X Y;
putmap F X:Ref			= put X (F (get X));

get Xs:Tuple			= map get Xs;
get Xs:List			= map get Xs;
get Xs:Stream			= map get Xs;

/* Note that 'fill', 'put' and 'putmap' are strict, so you better don't run
   them on infinite streams. Applying them to a finite section of a stream
   works, though. */

fill X Xs:Tuple			= do (fill X) Xs;
fill X Xs:List			= do (fill X) Xs;
fill X Xs:Stream		= do (fill X) Xs;

put Xs:Tuple Ys:Tuple		= dowith put Xs Ys;
put Xs:Tuple Ys:List		= dowith put Xs (tuple Ys);
put Xs:Tuple Ys:Stream		= dowith put (stream Xs) Ys;

put Xs:List Ys:Tuple		= dowith put Xs (list Ys);
put Xs:List Ys:List		= dowith put Xs Ys;
put Xs:List Ys:Stream		= dowith put (stream Xs) Ys;

put Xs:Stream Ys:Tuple		= dowith put Xs (stream Ys);
put Xs:Stream Ys:List		= dowith put Xs (stream Ys);
put Xs:Stream Ys:Stream		= dowith put Xs Ys;

putmap F Xs:Tuple		= do (putmap F) Xs;
putmap F Xs:List		= do (putmap F) Xs;
putmap F Xs:Stream		= do (putmap F) Xs;

/* Make get, fill, put and putmap work on all reference containers which have
   a 'list', 'list2' or 'vals' function. */

from stddecl import list2, vals;

private refmembers A;
refmembers A			= Xs if isreflist Xs where Xs:List = vals A;
				= Xs if isreflist Xs where Xs:List = list A;
				= Xs if isreflist2 Xs where Xs:List = list2 A;

get Xs				= map get Xs
				    where Xs:List = refmembers Xs;
fill X Xs			= do (fill X) Xs
				    where Xs:List = refmembers Xs;
put Xs Ys:Tuple			= dowith put Xs (list Ys)
				    where Xs:List = refmembers Xs;
put Xs Ys:List			= dowith put Xs Ys
				    where Xs:List = refmembers Xs;
put Xs Ys:Stream		= dowith put (stream Xs) Ys
				    where Xs:List = refmembers Xs;
putmap F Xs			= do (putmap F) Xs
				    where Xs:List = refmembers Xs;

/* Syntactic sugar. Xs!!I implements a dereferencing index operator which gets
   the value of the Ith member. */

public (!!) Xs I @ (!);

Xs!!I				= get X where X:Ref = Xs!I;
