Re: [Haskell] System.FilePath survey

Hello Krasimir, Friday, February 03, 2006, 2:03:20 PM, you wrote: KA> * Do you think that we have to use unboxed arrays of Word8/Word16 KA> for file path? utf8-encoded FastPackedString is much more appropriate and about unicode filenames in windows - i have a library what supports such filenames in NT/2000/XP systems. i plan to incorporate this functionality in Standard Hierachical Libraries AS> The task: Remove all files in a directory recursively. AS> The problem: In case the current encoding is UTF-8, filenames stored in AS> a different locale can comprise illegal UTF-8 sequences and are AS> therefore not representable as FilePath which is a Unicode string. Even AS> if the resulting Unicode sting is not 'error ".."', it is impossible to AS> call 'delete' on that file name, since fromUTF8 . toUTF8 cannot be the AS> identity function if the UTF8 byte sequence is illegal. AS> The solution: FilePath must be an abstract data type that is a sequence AS> of bytes. Programmers should only convert these to Unicode for AS> displaying them and otherwise treat them as opaque entities. In case of AS> invalid UTF-8 strings, the corresponding String will have an "invalid AS> unicode code character" substituted. but even my library can't really solve all problems. win95-based Oses don't support unicode APIs, afaik -- Best regards, Bulat mailto:bulatz@HotPOP.com

On 03.02 16:08, Bulat Ziganshin wrote:
KA> * Do you think that we have to use unboxed arrays of Word8/Word16 KA> for file path?
utf8-encoded FastPackedString is much more appropriate
Isn't one reason to have non-string FilePaths because they may not be convertable to unicode? Thus the move from String to FastPackedString would not really solve the problem. I think an ADT for FilePath would make sense. - Einar Karttunen

On Fri, Feb 03, 2006 at 11:00:18PM +0200, Einar Karttunen wrote:
Isn't one reason to have non-string FilePaths because they may not be convertable to unicode? Thus the move from String to FastPackedString would not really solve the problem.
It would be nice if a 'FastByteArray' could be pulled out of FastPackedString since it is a very useful thing in its own right. John -- John Meacham - ⑆repetae.net⑆john⑈

Hello John, Saturday, February 04, 2006, 3:30:49 AM, you wrote: JM> It would be nice if a 'FastByteArray' could be pulled out of JM> FastPackedString since it is a very useful thing in its own right. are UArray/IOUArray/StorableArray is not appropriate? -- Best regards, Bulat mailto:bulatz@HotPOP.com

On Sat, Feb 04, 2006 at 01:26:05PM +0300, Bulat Ziganshin wrote:
Hello John,
Saturday, February 04, 2006, 3:30:49 AM, you wrote: JM> It would be nice if a 'FastByteArray' could be pulled out of JM> FastPackedString since it is a very useful thing in its own right.
are UArray/IOUArray/StorableArray is not appropriate?
appropriate building blocks, but their interfaces are rather austere compared to FastPackedString, and it would nice to have all of its options available in something not dedicated just to strings, since we have already implemented them in any case. John -- John Meacham - ⑆repetae.net⑆john⑈

Hello John, Saturday, February 04, 2006, 10:54:35 PM, you wrote:
JM> It would be nice if a 'FastByteArray' could be pulled out of JM> FastPackedString since it is a very useful thing in its own right.
are UArray/IOUArray/StorableArray is not appropriate?
JM> appropriate building blocks, but their interfaces are rather austere JM> compared to FastPackedString, and it would nice to have all of its JM> options available in something not dedicated just to strings, since we JM> have already implemented them in any case. so it seems logical to add these interfaces to all arrays or at least all unboxed arrays? this also corresponds with problem of having common collections interface, what is a goal of J.P. Bernardy -- Best regards, Bulat mailto:bulatz@HotPOP.com

On Sun, Feb 05, 2006 at 12:39:14AM +0300, Bulat Ziganshin wrote:
JM> appropriate building blocks, but their interfaces are rather austere JM> compared to FastPackedString, and it would nice to have all of its JM> options available in something not dedicated just to strings, since we JM> have already implemented them in any case.
so it seems logical to add these interfaces to all arrays or at least all unboxed arrays? this also corresponds with problem of having common collections interface, what is a goal of J.P. Bernardy
Yeah, the standard array classes need some reworking, we definitly need to take another look at them if we are going to put them in the language. If a list-like interface (as FastPackedString implements) is useful for all of them then it would be good thing to add to a class. (though, it would be an interface useful independently of arrays, lists in particular :) ). Though, independent of that whole ball of wax, the FastByteString concrete data type would be nice to have. John -- John Meacham - ⑆repetae.net⑆john⑈

Hello John, Sunday, February 05, 2006, 5:58:20 PM, you wrote:
so it seems logical to add these interfaces to all arrays or at least all unboxed arrays? this also corresponds with problem of having
JM> Though, independent of that whole ball of wax, the FastByteString JM> concrete data type would be nice to have. how it is different from UArray Int Word8? i think it will be better to add 0-based unboxed arrays -- Best regards, Bulat mailto:bulatz@HotPOP.com

Hello Einar, Saturday, February 04, 2006, 12:00:18 AM, you wrote:
KA> * Do you think that we have to use unboxed arrays of Word8/Word16 KA> for file path?
well, if FilePath should be ADT, then: 1) all operations that returns filenames (getFileContents, anything more?) should return this ADT instead 2) this ADT should support conversion to/from String (for interaction with users) and to/from [Word8] (to send these values across storage/network). Of course, these conversions should be as exact and revertible as possible, i think that at least we can make guarantee for ascii chars 3) all operations that receives filenames should accept a _class_ which includes String/[Word8]/this ADT of course, this means incompatible changes in the APIs, so it's better to introduce new fucntions that returns ADT/accepts class with a new names -- Best regards, Bulat mailto:bulatz@HotPOP.com

On Mon, Feb 06, 2006 at 12:08:12PM +0300, Bulat Ziganshin wrote:
Hello Einar,
Saturday, February 04, 2006, 12:00:18 AM, you wrote:
KA> * Do you think that we have to use unboxed arrays of Word8/Word16 KA> for file path?
well, if FilePath should be ADT, then:
1) all operations that returns filenames (getFileContents, anything more?) should return this ADT instead
I agree. In System.Directory: You mean getDirectoryContents? getCurrentDirectory, getHomeDirectory, getTemporaryDirectory, etc. canonicalizePath. All sorts of Cabal stuff. Some of these could just return String. Others could return the ADT... Actually couldn't they all be made polymorphic? getDirectoryContents :: FilePath p => IO p class FilePath p where fromADT :: ADT -> p toADT :: p -> ADT (where I have declined naming the ADT)
2) this ADT should support conversion to/from String (for interaction with users) and to/from [Word8] (to send these values across storage/network). Of course, these conversions should be as exact and revertible as possible, i think that at least we can make guarantee for ascii chars
Probably we'd also want a withCString equivalent that works with the ADT, for use when working with the FFI. True, you could do the conversion via [Word8], but that's ugly (and potentially inefficient, depending how the ADT is stored internally).
3) all operations that receives filenames should accept a _class_ which includes String/[Word8]/this ADT
That would be nice, but I suspect not strictly necessary.
of course, this means incompatible changes in the APIs, so it's better to introduce new fucntions that returns ADT/accepts class with a new names
I don't know. I'd lean towards not introducing new function names. The point of accepting the class would be that existing code would continue to work. The only code that would be broken would be code that uses getFileContents, which is a sufficiently small minority of code that I would say just let it break. You'd leave the Haskell 98 Directory module returning String, but I'd vote to make System.Directory. -- David Roundy http://www.darcs.net

2006/2/6, David Roundy
Actually couldn't they all be made polymorphic?
getDirectoryContents :: FilePath p => IO p
class FilePath p where fromADT :: ADT -> p toADT :: p -> ADT
Would you mean that we should have two instances: instance FilePath String where .... instance FilePath ADT where .... It will work but I think that it is unnecessary complication. I prefer to use type classes only when they are really necessary i.e. when I have more than 2-3 instances or when I expect to have a lot of generic code that should work on both types. I think that if we should switch from plain String to ADT then this should be made for a new i/o library (the Handle(s) replacement) which will change the thing a lot in any way. For the current library I think that the plain String is more reasonable. If the general consensus is that ADT is the way to go then I think some effort on a new efficient IO library be forced. It should have: - FilePath ADT - Packed string - Fast I/O with unicode support I know some pieces already exists but they need integration and polishing. Cheers, Krasimir

On Mon, Feb 06, 2006 at 04:47:51PM +0200, Krasimir Angelov wrote:
2006/2/6, David Roundy
: Actually couldn't they all be made polymorphic?
getDirectoryContents :: FilePath p => IO p
class FilePath p where fromADT :: ADT -> p toADT :: p -> ADT
Would you mean that we should have two instances:
instance FilePath String where .... instance FilePath ADT where ....
It will work but I think that it is unnecessary complication. I prefer to use type classes only when they are really necessary i.e. when I have more than 2-3 instances or when I expect to have a lot of generic code that should work on both types.
The advantage would be that one could continue to use code that doesn't use the ADT for file paths, which would be convenient. And you'd need the conversion routines anyways, in order to display filepaths. True, it's just a convenience issue, but being able to write writeFile "output" contents would seem to me to be worth the complexity of having a class. In fact, I would think that all filepath-related code would work with either type. Also, this would allow users to define FastPackedStrings or whatever else to be in class FilePath, which is certainly something I've wanted to do with darcs, just to save the memory associated with all those Strings without the cost of converting to a String and then back to a CString (which the IO library must do internally) on every file operation.
I think that if we should switch from plain String to ADT then this should be made for a new i/o library (the Handle(s) replacement) which will change the thing a lot in any way. For the current library I think that the plain String is more reasonable. If the general consensus is that ADT is the way to go then I think some effort on a new efficient IO library be forced. It should have:
- FilePath ADT - Packed string - Fast I/O with unicode support
I know some pieces already exists but they need integration and polishing.
Indeed, it would be great to have a shiny new IO library, but even then I'd lean towards a FilePath class just for convenience. It'd be a royal pain for quickly written programs to have to always use (toADT "filename") when doing file IO with string literals. -- David Roundy http://www.darcs.net

David Roundy wrote:
being able to write
writeFile "output" contents
would seem to me to be worth the complexity of having a class [for pathnames].
I'd rather give string constants the type Str a => a, with class Str a where fromASCII :: ByteArray -> a fromUTF8 :: ByteArray -> a and declare an instance for the pathname ADT. GHC already handles string constants this way internally. Of course, this becomes yet another source of unresolved overloadings and confusion for newbies. -- Ben

Hello David, Monday, February 06, 2006, 4:12:41 PM, you wrote:
well, if FilePath should be ADT, then: 1) all operations that returns filenames (getFileContents, anything more?) should return this ADT instead
DR> You mean getDirectoryContents? DR> getCurrentDirectory, getHomeDirectory, getTemporaryDirectory, etc. DR> canonicalizePath. DR> Actually couldn't they all be made polymorphic? yes, of course they should! now i myself experiment with that sort of polymorphic interfaces. for example, "getFileSize" can return any Integral. it's a user decision, whether he needs to receive Int, Integer or say Word64 in answer. this have some limitations, namely that we need to add type signatures sometimes (although this can be addressed in future Haskell by extending the "default" directive) and that this can be slower/generate more code DR> getDirectoryContents :: FilePath p => IO p DR> class FilePath p where DR> fromADT :: ADT -> p DR> toADT :: p -> ADT DR> (where I have declined naming the ADT) yes, we can define all interfaces using this class, while internally all these functions will convert parameters to the type they need: convertADT = fromADT . toADT -- like "fromIntegral" definition getDirectoryContents :: (FilePath dir, FilePath file) => dir -> IO [file] getDirectoryContents dir = do let (dirStr :: String) = convertADT dir .... return (map convertADT filelist)
2) this ADT should support conversion to/from String (for interaction with users) and to/from [Word8] (to send these values across storage/network). Of course, these conversions should be as exact and revertible as possible, i think that at least we can make guarantee for ascii chars
DR> Probably we'd also want a withCString equivalent that works with the ADT, DR> for use when working with the FFI. True, you could do the conversion via DR> [Word8], but that's ugly (and potentially inefficient, depending how the DR> ADT is stored internally). probably, this ADT should be different for different OSes and just be the same as filenames representation native for this OS. that guarantee us that any filenames possible in this OS will be representable and minimal overhead of OS calls. if someone need another concrete representation - he would convert this ADT to the concrete datatype he need, be it String, [Word8], "UArray Int Word8" or FastPackedString
3) all operations that receives filenames should accept a _class_ which includes String/[Word8]/this ADT
DR> That would be nice, but I suspect not strictly necessary. i mean "all operations in standard library". it's easy to do, at least while we don't count efficiency problems
of course, this means incompatible changes in the APIs, so it's better to introduce new fucntions that returns ADT/accepts class with a new names
DR> I don't know. I'd lean towards not introducing new function names. The DR> point of accepting the class would be that existing code would continue to DR> work. The only code that would be broken would be code that uses DR> getFileContents, which is a sufficiently small minority of code that I DR> would say just let it break. You'd leave the Haskell 98 Directory module DR> returning String, but I'd vote to make System.Directory. really, promoting all operations to using class will be enough to not break compatibility with any existing code -- Best regards, Bulat mailto:bulatz@HotPOP.com

On Mon, Feb 06, 2006 at 05:19:58PM -0800, Ashley Yakeley wrote:
Ben Rudiak-Gould wrote:
Also, getArgs should return [ADT].
I would rather this be left as [String]. I don't want to have to do back-conversion from ADT to get my option flags.
Also, arguments should be treated as if they are in the locale specified by $LANG. so the transformation to String is non-trivial. ideally we would have interfaces to get both the raw bytes and the Stringized versions out. John -- John Meacham - ⑆repetae.net⑆john⑈

The things are going to be too complicated in this way. Very soon we
will end up with ADT instead of String at almost every place. The
getArgs should return [String]. If the back-conversion for a given
name doesn't exist then the only way is to rename the file. We don't
have to be pedantic here.
Cheers,
Krasimir
2006/2/7, Ben Rudiak-Gould
Ashley Yakeley wrote:
I would rather this be left as [String]. I don't want to have to do back-conversion from ADT to get my option flags.
It's not actually a back-conversion -- see my other post.
-- Ben
_______________________________________________ Libraries mailing list Libraries@haskell.org http://www.haskell.org/mailman/listinfo/libraries

Hello Ben, Tuesday, February 07, 2006, 1:33:44 AM, you wrote:
1) all operations that returns filenames (getFileContents, anything more?) should return this ADT instead
BRG> Also, getArgs should return [ADT]. getLine also shoukd return ADT - just for case when user supplied a filename? :) and "++" operation should return ADT too? :) String is filename representation for dialogue with user and this ADT should just support conversion to/from Strings. while getArgs, unlike the current atate of affairs, should return just proper Unicode strings -- Best regards, Bulat mailto:bulatz@HotPOP.com

Bulat Ziganshin wrote:
Hello Ben,
BRG> Also, getArgs should return [ADT].
getLine also shoukd return ADT - just for case when user supplied a filename? :) and "++" operation should return ADT too? :)
The point is that different things are natively handled in different formats under different OSes, e.g. Posix NT Win9x pathnames bytes UTF-16 locale command line bytes UTF-16 locale file contents bytes bytes bytes pipes/sockets bytes bytes bytes File and streams should be read and written as bytes (octets) because that's the interface provided by every supported OS -- and not for any other reason. Pathnames and the command line should have the same representation in Haskell because they have the same representation in every supported OS, and one very frequently needs to convert between them. Using different types would make no sense, as far as I can tell. Ashley Yakeley says that he wants to parse options in his command-line arguments, and so do I, but I also want to find out the extension of a filename, and whether it's a backup file (ends with '~'), and whether it contains non-ASCII characters, and so on. Interconversion is necessary in either case, and the issues involved are the same, so in my opinion they should be handled together. Actually, there's one extra issue with the command line: in Posix it's a list of strings, while in Windows it's just one string. Very few if any Windows applications do their own command line parsing. NT provides a function CommandLineToArgvW, but I don't know what algorithm it uses. -- Ben

On Tue, Feb 07, 2006 at 04:25:35PM +0000, Ben Rudiak-Gould wrote:
The point is that different things are natively handled in different formats under different OSes, e.g.
Posix NT Win9x
pathnames bytes UTF-16 locale command line bytes UTF-16 locale file contents bytes bytes bytes pipes/sockets bytes bytes bytes
actually, Posix systems should be the following
pathnames locale UTF-16 locale command line locale UTF-16 locale file contents * bytes bytes pipes/sockets * bytes bytes
Although the Posix interface is in terms of bytes, the strings should always be interpreted via the locale specified in $LANG or $LC_CTYPE also, for file contents and pipes/sockets, if you are passing text, and in the absence of some overriding standard or protocol, you should be using the encoding specified in the locale too. John -- John Meacham - ⑆repetae.net⑆john⑈

John Meacham wrote:
On Tue, Feb 07, 2006 at 04:25:35PM +0000, Ben Rudiak-Gould wrote:
Posix NT Win9x
pathnames bytes UTF-16 locale command line bytes UTF-16 locale file contents bytes bytes bytes pipes/sockets bytes bytes bytes
actually, Posix systems should be the following
pathnames locale UTF-16 locale command line locale UTF-16 locale file contents * bytes bytes pipes/sockets * bytes bytes
Although the Posix interface is in terms of bytes, the strings should always be interpreted via the locale specified in $LANG or $LC_CTYPE also, for file contents and pipes/sockets, if you are passing text, and in the absence of some overriding standard or protocol, you should be using the encoding specified in the locale too.
But that's an application-level convention; the kernel only knows about bytes. On Windows the encoding of pathnames and the command line is a requirement imposed by the kernel. I think assuming the locale encoding for the command line on Posix is a bad idea. Users are unlikely to pass a misencoded command line explicitly, but I want my-haskell-util `find .` to work even on a mounted volume that uses the wrong encoding. (And I also want your-haskell-util to work, even if you didn't write it with this situation in mind.) -- Ben

On Wed, Feb 08, 2006 at 09:10:37PM +0000, Ben Rudiak-Gould wrote:
John Meacham wrote:
On Tue, Feb 07, 2006 at 04:25:35PM +0000, Ben Rudiak-Gould wrote:
Posix NT Win9x
pathnames bytes UTF-16 locale command line bytes UTF-16 locale file contents bytes bytes bytes pipes/sockets bytes bytes bytes
actually, Posix systems should be the following
pathnames locale UTF-16 locale command line locale UTF-16 locale file contents * bytes bytes pipes/sockets * bytes bytes
Although the Posix interface is in terms of bytes, the strings should always be interpreted via the locale specified in $LANG or $LC_CTYPE also, for file contents and pipes/sockets, if you are passing text, and in the absence of some overriding standard or protocol, you should be using the encoding specified in the locale too.
But that's an application-level convention; the kernel only knows about bytes. On Windows the encoding of pathnames and the command line is a requirement imposed by the kernel. I think assuming the locale encoding for the command line on Posix is a bad idea. Users are unlikely to pass a misencoded command line explicitly, but I want my-haskell-util `find .` to work even on a mounted volume that uses the wrong encoding. (And I also want your-haskell-util to work, even if you didn't write it with this situation in mind.)
when the command line is to be interpreted as a string, then interpreting it in the current locale is definitly the right thing to do. This is why we need two varieties of getArgs, one which returns [String] and one which returns [[Word8]]. though, I doubt the second form will be needed much since in general you usually think of command line arguments as strings, but it should be provided since it can't really be worked around. John -- John Meacham - ⑆repetae.net⑆john⑈

Ben Rudiak-Gould wrote:
The point is that different things are natively handled in different formats under different OSes, e.g.
Posix NT Win9x
pathnames bytes UTF-16 locale command line bytes UTF-16 locale file contents bytes bytes bytes pipes/sockets bytes bytes bytes
Add to that: Mac OS X pathnames UTF-8 command line UTF-8 It's POSIX (or mostly POSIX), but the encoding for path names is always guaranteed to be UTF-8. For the default file system type, HFS +, it is actually stored on disk as UTF-16. Arbitrary strings of bytes are not allowed. For POSIX systems, I'd also like to observe the following: 1) Widely used languages and libraries like Java and GTK+ assume that all file names and command lines are encoded in the system locale, or at least that they can all be converted to unicode strings. 2) Command lines are usually entered as TEXT on a terminal and are therefore encoded in whatever encoding the terminal uses. 3) None of the recent linux distributions I have installed did anything but set up a UTF-8 based system. So I think we should try hard to avoid introducing any additional complexity, like filename ADTs used for program arguments, to deal with the small minority of systems where file names cannot be converted to unicode. Maybe it's possible to use some user-defined unicode code points to achieve a lossless conversion of arbitrary byte strings to unicode? I mean, byte strings that are valid in the system encoding would get transcoded correctly, and invalid bytes would get mapped to some extra code points so that they can be converted back if necessary. Cheers, Wolfgang

On 08.02 14:03, Wolfgang Thaller wrote:
1) Widely used languages and libraries like Java and GTK+ assume that all file names and command lines are encoded in the system locale, or at least that they can all be converted to unicode strings.
Which causes much annoyance to users having to define various environment variables just to get them to open a file.
2) Command lines are usually entered as TEXT on a terminal and are therefore encoded in whatever encoding the terminal uses.
Actually I like the ablity to delete/copy files even if they happen to have filenames in weird chinese encodings too. Users just use wildcards or tab completion to get around filenames that are hard to type.
3) None of the recent linux distributions I have installed did anything but set up a UTF-8 based system.
Very many people needing to use their own language still use other things and will continue so for the foreseeable future.
So I think we should try hard to avoid introducing any additional complexity, like filename ADTs used for program arguments, to deal with the small minority of systems where file names cannot be converted to unicode. Maybe it's possible to use some user-defined unicode code points to achieve a lossless conversion of arbitrary byte strings to unicode? I mean, byte strings that are valid in the system encoding would get transcoded correctly, and invalid bytes would get mapped to some extra code points so that they can be converted back if necessary.
What would happen if you tried to output such a String? The raw bytes or the escaped versions? Also this would mean that Haskell unicode != unicode (isn't Java's broken handling enough). - Einar Karttunen

Einar Karttunen wrote:
On 08.02 14:03, Wolfgang Thaller wrote:
2) Command lines are usually entered as TEXT on a terminal and are therefore encoded in whatever encoding the terminal uses.
Actually I like the ablity to delete/copy files even if they happen to have filenames in weird chinese encodings too.
Your shell wouldn't know about that. Either the weird encoding is UTF-8 anyway, in which case there is no problem, or it is something else, in which case you don't get chinese characters, but gibberish. The program copying the gibberish wouldn't care, though.
Very many people needing to use their own language still use other things [than UTF-8] and will continue so for the foreseeable future.
Which is actually a shame. But anyway, that's the reason why a sane programming language would use the locale settings to decode the command line, file names and anything else that came from related system calls.
Maybe it's possible to use some user-defined unicode code points to achieve a lossless conversion of arbitrary byte strings to unicode?
Definitely. Allocating just 128 code points in the vendor zone shouldn't be too hard.
What would happen if you tried to output such a String? The raw bytes or the escaped versions?
There are no raw bytes. Outputting a string means encoding it into whatever the locale says or whatever the convention of a particular library mandates. This will often be the same encoding that was used to decode filenames in the first place, so you get the same byte sequence back. If that happens to be an invalid UTF-8 sequence, so be it. It was broken to begin with, so we're no worse off than if we ignored encoding issues completely.
Also this would mean that Haskell unicode != unicode
Not at all. The escape codes wouldn't leave the Haskell program in any form other than an invalid UTF-8 sequence, which is also the only way they could ever enter it. Nobody would ever notice the hack. Udo. -- Delusions are often functional. A mother's opinions about her children's beauty, intelligence, goodness, et cetera ad nauseam, keep her from drowning them at birth.

On 07.02 10:48, Bulat Ziganshin wrote:
Hello Ben,
Tuesday, February 07, 2006, 1:33:44 AM, you wrote:
1) all operations that returns filenames (getFileContents, anything more?) should return this ADT instead
BRG> Also, getArgs should return [ADT].
Just define: getArgsAdt :: IO [ADT] getArgsAdt = ... getArgs :: IO [String] getArgs = fmap ADT.toString getArgsAdt - Einar Karttunen

Einar Karttunen wrote:
Just define:
getArgsAdt :: IO [ADT] getArgsAdt = ...
getArgs :: IO [String] getArgs = fmap ADT.toString getArgsAdt
My only problem with this is that I'm afraid people will then write code like main = do [x] <- getArgs y <- readFile x -- assuming readFile :: Path a => a -> IO String ... which will fail on filenames whose encoding doesn't match the locale. A naively-written C program will have no trouble with such files, and I think a naively-written Haskell program should be at least as robust. I'm not sure a pathname ADT is the best approach here (maybe the '\0' hack would be better) but if an ADT is used I'd like the right thing to be easier than the wrong thing. -- Ben
participants (9)
-
Ashley Yakeley
-
Ben Rudiak-Gould
-
Bulat Ziganshin
-
David Roundy
-
Einar Karttunen
-
John Meacham
-
Krasimir Angelov
-
Udo Stenzel
-
Wolfgang Thaller