An updated version is at <http://qrczak.ids.net.pl/qforeign-0.62.tar.gz>. Major changes from 0.60: * 'make install' implemented. Installs as package qforeign. * iconv support is more robust. Works with Konstantin Chuguev's iconv implementation too. * Definitions of character predicates are more correct, after discussions on linux-utf8 and unicode mailing lists. * glue-hsc: #const_str construct added * Curses interface improved. * Pcap interface added (incomplete). * Works under ghc-4.08, 4.08.1, 4.09 (before-ghci-branch). No Unicode or 'make install' with ghc < 4.09 (I just need to use FFI with ghc-4.08*). * Things that went into ghc are removed from here (Ptr, Storable). glue-hsc from here is used only for ghc < 4.09. ------------------------------------------------------------------------ qforeign got smaller because of changes in ghc, but what is ghc is still a bit incomplete. Most important incompleteness is in string handling. Since ghc's Unicode support is half-baked and it is not yet clear how the library interface will look like, I propose adding the following functions to the module Foreign: peekCString :: Ptr CChar -> IO String peekCStringLen :: Ptr CChar -> Int -> IO String withCString :: String -> (Ptr CChar -> IO a) -> IO a withCStringLen :: String -> (Ptr CChar -> Int -> IO a) -> IO a newCString :: String -> IO (Ptr CChar) newCStringLen :: String -> IO (Ptr CChar, Int) -- Memory is obtained by malloc and must be freed using free. Unfortunately versions with explicit length need to be separate. Most code will use those without the length, but in case a C library allows handling strings with '\0' characters embedded by passing the length explicitly, the more common versions are not enough. Their implementation will use ISO-8859-1 for now. Their interface is not constrained to the case where the length of the C string translated from Haskell is the same as of the original, nor vice versa. Eventually they will do the conversion between Unicode and the default local encoding and there will be separate versions allowing to supply another conversion. ------------------------------------------------------------------------ After adding the above, the interface provided by ghc alone should be sufficient to do almost everything currently possible with FFI, without the need of using private ghc's bits and with reasonable efficiency. Apart from that, my library contains some convenience functions. I hope the meaning is clear from names and types. Perhaps some of them are worth inclusion into ghc? ---------------------------------------------------------------- -- Storable arrays. No new expressiveness, but they are commonly -- used. peekArray :: Storable a => Ptr a -> Int -> IO [a] peekArray0 :: (Storable a, Eq a) => a -> Ptr a -> IO [a] pokeArray :: Storable a => Ptr a -> [a] -> IO () pokeArray0 :: Storable a => a -> Ptr a -> [a] -> IO () withObject :: Storable a => a -> (Ptr a -> IO b) -> IO b withArray :: Storable a => [a] -> (Ptr a -> IO b) -> IO b withArray0 :: Storable a => a -> [a] -> (Ptr a -> IO b) -> IO b newObject :: Storable a => a -> IO (Ptr a) newArray :: Storable a => [a] -> IO (Ptr a) newArray0 :: Storable a => a -> [a] -> IO (Ptr a) -- Memory is obtained by malloc and must be freed using free. ---------------------------------------------------------------- -- Haskellish interface to memcpy and memmove. copyArray :: Storable a => Ptr a -> Ptr a -> Int -> IO () copyBytes :: Ptr a -> Ptr a -> Int -> IO () moveArray :: Storable a => Ptr a -> Ptr a -> Int -> IO () moveBytes :: Ptr a -> Ptr a -> Int -> IO () ---------------------------------------------------------------- -- Lifting many functions taking a continuation (which takes a value) -- to a single function taking a continuation (which takes a list). -- This is tricky to write each time by hand. -- Convenient for sequencing alloca and with functions. -- For example to allocate an array of C strings. sequenceCont :: [(a -> res) -> res] -> ([a] -> res) -> res mapCont :: (a -> (b -> res) -> res) -> [a] -> ([b] -> res) -> res ---------------------------------------------------------------- -- Perhaps unsafePerformIO and unsafeInterleaveIO should be -- reexported from module Foreign too. Foreign import may import -- pure functions anyway, but in that case the wrapper cannot do -- memory allocation etc. unsafePerformIO :: IO a -> a unsafeInterleaveIO :: IO a -> IO a ---------------------------------------------------------------- -- Handling errors. Mostly conversion of errno to Haskell's IO -- exceptions. -- Get the current value of C errno. getErrno :: IO Int -- Throw an exception based on function name and given error code. throwCError :: String -> Int -> IO a -- Throw an exception based on function name and current errno -- value. Much more common than the above but less general, -- that's why there are two functions. throwErrno :: String -> IO a -- Perform an action and ignore its result. A stupid thing but quite -- common, especially after the result was converted to exceptions. ignore :: IO a -> IO () -- Wrappers that convert the result to a possible exception. -- *_EINTR variants repeat the action if there was an error -- and errno == EINTR. throwIf :: (a -> Bool) -> String -> IO a -> IO a throwIf_EINTR :: (a -> Bool) -> String -> IO a -> IO a throwIfMinus1 :: Num a => String -> IO a -> IO a throwIfMinus1_EINTR :: Num a => String -> IO a -> IO a throwIfNull :: String -> IO (Ptr a) -> IO (Ptr a) throwIfNull_EINTR :: String -> IO (Ptr a) -> IO (Ptr a) ---------------------------------------------------------------- -- Mutexes. They allow making non-reentrant functions thread-safe, -- without importing the whole Concurrent stuff. They are also -- implementable in a Haskell compiler without concurrency, and -- that's why I think they should be exposed by the FFI instead of -- using Concurrent things directly. -- errno handling is not reentrant, even though it is reentrant -- in native C threads. Instead of protecting all wrappers for C -- functions which use errno by mutexes, I propose that Haskell -- compilers which provide concurrency should be responsible for -- maintaining a thread-local C errno variable. data Mutex newMutex :: IO Mutex threadSafe :: Mutex -> IO a -> IO a -- __("< Marcin Kowalczyk * qrczak@knm.org.pl http://qrczak.ids.net.pl/ \__/ ^^ SYGNATURA ZASTÊPCZA QRCZAK
qrczak@knm.org.pl (Marcin 'Qrczak' Kowalczyk) wrote,
qforeign got smaller because of changes in ghc, but what is ghc is still a bit incomplete.
Most important incompleteness is in string handling. Since ghc's Unicode support is half-baked and it is not yet clear how the library interface will look like, I propose adding the following functions to the module Foreign:
peekCString :: Ptr CChar -> IO String peekCStringLen :: Ptr CChar -> Int -> IO String
withCString :: String -> (Ptr CChar -> IO a) -> IO a withCStringLen :: String -> (Ptr CChar -> Int -> IO a) -> IO a
newCString :: String -> IO (Ptr CChar) newCStringLen :: String -> IO (Ptr CChar, Int) -- Memory is obtained by malloc and must be freed using free.
I believe that this stuff belongs into the higher-level marshalling library. In fact, it should be instances of more general routines dealing with lists of Storable elements. Similar to how it is done in the C2HS library.
---------------------------------------------------------------- -- Storable arrays. No new expressiveness, but they are commonly -- used.
peekArray :: Storable a => Ptr a -> Int -> IO [a] peekArray0 :: (Storable a, Eq a) => a -> Ptr a -> IO [a]
pokeArray :: Storable a => Ptr a -> [a] -> IO () pokeArray0 :: Storable a => a -> Ptr a -> [a] -> IO ()
withObject :: Storable a => a -> (Ptr a -> IO b) -> IO b withArray :: Storable a => [a] -> (Ptr a -> IO b) -> IO b withArray0 :: Storable a => a -> [a] -> (Ptr a -> IO b) -> IO b
newObject :: Storable a => a -> IO (Ptr a) newArray :: Storable a => [a] -> IO (Ptr a) newArray0 :: Storable a => a -> [a] -> IO (Ptr a) -- Memory is obtained by malloc and must be freed using free.
This seems to go into the direction that I mentioned above.
---------------------------------------------------------------- -- Handling errors. Mostly conversion of errno to Haskell's IO -- exceptions.
-- Get the current value of C errno. getErrno :: IO Int
-- Throw an exception based on function name and given error code. throwCError :: String -> Int -> IO a -- Throw an exception based on function name and current errno -- value. Much more common than the above but less general, -- that's why there are two functions. throwErrno :: String -> IO a
-- Perform an action and ignore its result. A stupid thing but quite -- common, especially after the result was converted to exceptions. ignore :: IO a -> IO ()
-- Wrappers that convert the result to a possible exception. -- *_EINTR variants repeat the action if there was an error -- and errno == EINTR. throwIf :: (a -> Bool) -> String -> IO a -> IO a throwIf_EINTR :: (a -> Bool) -> String -> IO a -> IO a
throwIfMinus1 :: Num a => String -> IO a -> IO a throwIfMinus1_EINTR :: Num a => String -> IO a -> IO a throwIfNull :: String -> IO (Ptr a) -> IO (Ptr a) throwIfNull_EINTR :: String -> IO (Ptr a) -> IO (Ptr a)
Again, this belongs into the higher-level marshalling library. Did you have a look at the corresponding routines in C2HS?
---------------------------------------------------------------- -- Mutexes. They allow making non-reentrant functions thread-safe, -- without importing the whole Concurrent stuff. They are also -- implementable in a Haskell compiler without concurrency, and -- that's why I think they should be exposed by the FFI instead of -- using Concurrent things directly.
-- errno handling is not reentrant, even though it is reentrant -- in native C threads. Instead of protecting all wrappers for C -- functions which use errno by mutexes, I propose that Haskell -- compilers which provide concurrency should be responsible for -- maintaining a thread-local C errno variable.
data Mutex newMutex :: IO Mutex threadSafe :: Mutex -> IO a -> IO a
This sounds like a good idea to me. Cheers, Manuel
On Thu, 23 Nov 2000, Manuel M. T. Chakravarty wrote:
I believe that this stuff belongs into the higher-level marshalling library. In fact, it should be instances of more general routines dealing with lists of Storable elements.
Strings require more sophistication because their encoding used in C will be different than Haskell's Unicode. It implies that the API must assume that programmers will not know the length of a C string before actual conversion from a Haskell string (nor vice versa); allocation and translation should be done in one step. Generic Storable versions can be used after the translation from [Char] to [CChar] was done. Or before the translation, producing a C array of HsChar values. But to make it possible to convert strings independently of how exactly charset handling will be done, a few simple functions should be provided. For example ones that I've proposed. They are consistent with bits that are in GHC's FFI now and with what I propose for generic Storable versions.
Again, this belongs into the higher-level marshalling library. Did you have a look at the corresponding routines in C2HS?
Yes, I was inspired by C2HS in a significant way. Handling strings is about the only implementation-dependent thing lacking from the core FFI provided by GHC (and hopefully the official FFI in the future). Handling errors (errno, exceptions) could arguably be the second. But I believe that adding a few higher level functions will make these modules complete enough to write many C interfaces without the need of an additional yet higher level library. All GHC's standard libraries and hslibs should be at some point converted to use a consistent FFI library. This should be done even before Unicode really comes, to make all of them use a proper encoding for OS strings instead of ISO-8859-1 at the same time. These functions are not as high level as I hoped. E.g. there is no wrapper for converting many arguments in an interesting way at the same time, or specialized handlers for out and in/out parameters. I could not think of a good way to express them (making simple things easy and hard things possible, exception safe, and as Haskell 98 conforming as possible). OTOH it makes the library simpler. Dealing with one argument at a time seems to be convenient enough. I hope that the library is small enough for the official FFI. For the convenience of porting the library can be split into implementation-dependent and implementation-independent parts. (I used to have this split but have given up for the moment, because it complicated the module structure, especially when supporting both ghc-4.08 and 4.09). -- Marcin 'Qrczak' Kowalczyk
"Marcin 'Qrczak' Kowalczyk" <mk167280@zodiac.mimuw.edu.pl> wrote,
On Thu, 23 Nov 2000, Manuel M. T. Chakravarty wrote:
I believe that this stuff belongs into the higher-level marshalling library. In fact, it should be instances of more general routines dealing with lists of Storable elements.
Strings require more sophistication because their encoding used in C will be different than Haskell's Unicode.
It implies that the API must assume that programmers will not know the length of a C string before actual conversion from a Haskell string (nor vice versa); allocation and translation should be done in one step.
Generic Storable versions can be used after the translation from [Char] to [CChar] was done. Or before the translation, producing a C array of HsChar values. But to make it possible to convert strings independently of how exactly charset handling will be done, a few simple functions should be provided. For example ones that I've proposed. They are consistent with bits that are in GHC's FFI now and with what I propose for generic Storable versions.
To me it seems as if we should seperate conversion from [Char] to [CChar] from the actual marshalling of CChar. Why is it an advantage to lump it all together? In other words, I don't really agree with ``But to make it possible to convert strings independently of how exactly charset handling will be done, a few simple functions should be provided.'' It seemingly makes the conversion independent, but it makes the low-level FFI module (that's where I understand you propose the routines to go) dependent on the conversion, which I don't like to be honest. But maybe I am missing something. Simon Marlow <simonmar@microsoft.com> wrote,
I think Marcin is right in that C Strings are special (unfortunately). This interface seems reasonable, and simple. Do we have any objections?
My main objection re Strings at the moment is that I would like to seperate the encoding an marshalling business and use for marshalling just instances of generic list marshalling. This would simplify the structure IMHO.
Again, this belongs into the higher-level marshalling library. Did you have a look at the corresponding routines in C2HS?
Yes, I was inspired by C2HS in a significant way.
Handling strings is about the only implementation-dependent thing lacking from the core FFI provided by GHC (and hopefully the official FFI in the future). Handling errors (errno, exceptions) could arguably be the second. But I believe that adding a few higher level functions will make these modules complete enough to write many C interfaces without the need of an additional yet higher level library.
As the high-level library is meant to be completely portable (in the sense that it delegates all non-portable work to lower-level libraries), I don't see much advantage in avoiding the need for using it in some applications.
All GHC's standard libraries and hslibs should be at some point converted to use a consistent FFI library. This should be done even before Unicode really comes, to make all of them use a proper encoding for OS strings instead of ISO-8859-1 at the same time.
These functions are not as high level as I hoped. E.g. there is no wrapper for converting many arguments in an interesting way at the same time, or specialized handlers for out and in/out parameters. I could not think of a good way to express them (making simple things easy and hard things possible, exception safe, and as Haskell 98 conforming as possible). OTOH it makes the library simpler. Dealing with one argument at a time seems to be convenient enough. I hope that the library is small enough for the official FFI.
I am not arguing against you structure - as you know I am quite happy with most of your proposals.
For the convenience of porting the library can be split into implementation-dependent and implementation-independent parts. (I used to have this split but have given up for the moment, because it complicated the module structure, especially when supporting both ghc-4.08 and 4.09).
Hmm, but I think, we really want to have this split in the "official" FFI libraries. It simplifies the work of other Haskell implementors (eg, the York people) a lot, I think. Cheers, Manuel
Fri, 01 Dec 2000 12:53:19 +1100, Manuel M. T. Chakravarty <chak@cse.unsw.edu.au> pisze:
To me it seems as if we should seperate conversion from [Char] to [CChar] from the actual marshalling of CChar. Why is it an advantage to lump it all together?
Efficiency, and the fact that combined conversion+marshalling operations are convenient anyway. I don't want to make passing strings to C functions unnecessarily slow because of Unicode. They will be slower than 8-bit Chars no matter what, but I hope not too slow if done well. The default conversion between Unicode and the local byte encoding is implemented in C (my library uses iconv). It would be silly to store the string in a C array of HsChars, call iconv to transform it to an array of chars, and read it as a a list of CChars only to store it back to a newly allocated array of chars. The former array already looked like what needs to be passed to the C function! Similarly in the other direction. I've already implemented this, but haven't measured efficiency of various variants yet, nor how much does the conversion slow down the whole process compared to non-Unicode variant.
It seemingly makes the conversion independent, but it makes the low-level FFI module (that's where I understand you propose the routines to go) dependent on the conversion, which I don't like to be honest.
I'm afraid it's unavoidable in practice.
My main objection re Strings at the moment is that I would like to seperate the encoding an marshalling business and use for marshalling just instances of generic list marshalling. This would simplify the structure IMHO.
So we would have to provide conversion stubs doing simple casts between Chars and CChars now, to make the code using it work also in future, when the conversions become non-trivial. Otherwise how users will translate between String/HsChar[] and [CChar]/char[]? And we don't know yet how the conversion interface will look like. Since the string almost always needs to be translated before marshalling, it makes sense to provide combined variants anyway.
But I believe that adding a few higher level functions will make these modules complete enough to write many C interfaces without the need of an additional yet higher level library.
As the high-level library is meant to be completely portable (in the sense that it delegates all non-portable work to lower-level libraries), I don't see much advantage in avoiding the need for using it in some applications.
It avoids introducing a dependency on an additional tool. Especially if the tool evolves independently from compilers, has its own version incompatibilities, needs to be installed separately and updated after the compiler update etc. Let's keep things simpler. I don't see an advantage in requiring to use a separate tool to get reasonable convenience. There is little to be added to the basic Foreign module to make it quite complete and usable alone.
Hmm, but I think, we really want to have this split in the "official" FFI libraries. It simplifies the work of other Haskell implementors (eg, the York people) a lot, I think.
Surely it will be split again when it's finished. -- __("< Marcin Kowalczyk * qrczak@knm.org.pl http://qrczak.ids.net.pl/ \__/ ^^ SYGNATURA ZASTÊPCZA QRCZAK
To me it seems as if we should seperate conversion from [Char] to [CChar] from the actual marshalling of CChar. Why is it an advantage to lump it all together?
Efficiency, and the fact that combined conversion+marshalling operations are convenient anyway.
I don't want to talk about [Char] -> [CChar] specifically, but the mention of efficiency reminds me of something that has been bothering me for a little while. In the current design of the FFI, it is assumed that most of the marshalling work is going to be done in Haskell. For example, to return more than one result from a C function, the results are packaged as a C struct, passed as an Addr (or Ptr) to Haskell, which then dereferences from the Addr to each individual component of the struct. Now in GHC, the treatment of the IO monad may make this acceptably fast, but in other implementations (like nhc98 (of course)), this is just horribly inefficient, and can slow up FFI calls by an order of magnitude or more. (Comparing to previous implementations using either the builtin 'primitive' mechanism or GreenCard.) I agree that it is better in principle to have the marshalling code in Haskell because it has much better type-safety than C. But in practice, I have found other ways to do the marshalling, mainly because of the efficiency question. Do other people have any thoughts about the efficiency of the current scheme? Regards, Malcolm
malcolm-ffi@cs.york.ac.uk wrote,
In the current design of the FFI, it is assumed that most of the marshalling work is going to be done in Haskell. For example, to return more than one result from a C function, the results are packaged as a C struct, passed as an Addr (or Ptr) to Haskell, which then dereferences from the Addr to each individual component of the struct.
Now in GHC, the treatment of the IO monad may make this acceptably fast, but in other implementations (like nhc98 (of course)), this is just horribly inefficient, and can slow up FFI calls by an order of magnitude or more. (Comparing to previous implementations using either the builtin 'primitive' mechanism or GreenCard.) I agree that it is better in principle to have the marshalling code in Haskell because it has much better type-safety than C. But in practice, I have found other ways to do the marshalling, mainly because of the efficiency question.
Honestly, I don't think that there is an easy solution to this problem. And - while I understand that you may not be happy about this - I am tempted to put the burden on the compiler writers here. Marshalling-style IO monad operations just have do be optimised properly. The reason for my position is simply that marshalling in C is IMHO not an option for anything but small bindings. It certainly is out of question for something like GTK+Haskell. Except when the marshalling code is generated by a tool (like GreenCard), but I doubt that we will - in the long run - have tools which do C side marshalling. Simply because when writing a tool, it is also easier to generate Haskell marshalling code (as H/Direct demonstrated). In summary, I think, we should have an eye on that we exactly know what we expect a compiler to do to optimise Haskell marshalling code (and avoid code that we cannot reasonably expect a compiler to optimise), but I don't see how we can do without the co-operation of the compiler. Cheers, Manuel
qrczak@knm.org.pl (Marcin 'Qrczak' Kowalczyk) wrote,
Fri, 01 Dec 2000 12:53:19 +1100, Manuel M. T. Chakravarty <chak@cse.unsw.edu.au> pisze:
To me it seems as if we should seperate conversion from [Char] to [CChar] from the actual marshalling of CChar. Why is it an advantage to lump it all together?
Efficiency, and the fact that combined conversion+marshalling operations are convenient anyway.
I don't want to make passing strings to C functions unnecessarily slow because of Unicode. They will be slower than 8-bit Chars no matter what, but I hope not too slow if done well.
The default conversion between Unicode and the local byte encoding is implemented in C (my library uses iconv). It would be silly to store the string in a C array of HsChars, call iconv to transform it to an array of chars, and read it as a a list of CChars only to store it back to a newly allocated array of chars. The former array already looked like what needs to be passed to the C function! Similarly in the other direction.
I've already implemented this, but haven't measured efficiency of various variants yet, nor how much does the conversion slow down the whole process compared to non-Unicode variant.
So far, we have handled similar cases by using the inefficient variant as the "default" and adding a RULES pragma to get the efficient version. See, for example, the various IntXX to Int casts. We can use the same technique here.
My main objection re Strings at the moment is that I would like to seperate the encoding an marshalling business and use for marshalling just instances of generic list marshalling. This would simplify the structure IMHO.
So we would have to provide conversion stubs doing simple casts between Chars and CChars now, to make the code using it work also in future, when the conversions become non-trivial. Otherwise how users will translate between String/HsChar[] and [CChar]/char[]? And we don't know yet how the conversion interface will look like.
We know that the conversion interface can look like conv :: String -> IO [CChar] because the String marshalling routines that you proposed also don't have any additional information to convert a string. So, there must be a default conversion that doesn't get any further information from the user (in the conversion call). Otherwise, your marshalling routines wouldn't work either.
Since the string almost always needs to be translated before marshalling, it makes sense to provide combined variants anyway.
I am not necessarily against having combined variants as shorthands. The questions we are discussing is what should the primitives be. If we do it like I propose, the `Marshall' library doesn't have to know anything about Unicode. In you proposal, the marshalling library depends on the Unicode conversion library. It's merely a structural difference.
But I believe that adding a few higher level functions will make these modules complete enough to write many C interfaces without the need of an additional yet higher level library.
As the high-level library is meant to be completely portable (in the sense that it delegates all non-portable work to lower-level libraries), I don't see much advantage in avoiding the need for using it in some applications.
It avoids introducing a dependency on an additional tool. Especially if the tool evolves independently from compilers, has its own version incompatibilities, needs to be installed separately and updated after the compiler update etc. Let's keep things simpler.
I don't see an advantage in requiring to use a separate tool to get reasonable convenience. There is little to be added to the basic Foreign module to make it quite complete and usable alone.
What tool? I am not talking about a library plus tool, but only about the library. The high-level library should be part of the FFI standard and so will come included with every compiler that supports the FFI (just like the `List', `Maybe' and so on standard libraries). Cheers, Manuel
Mon, 04 Dec 2000 17:17:09 +1100, Manuel M. T. Chakravarty <chak@cse.unsw.edu.au> pisze:
So far, we have handled similar cases by using the inefficient variant as the "default" and adding a RULES pragma to get the efficient version. See, for example, the various IntXX to Int casts. We can use the same technique here.
We can, but it will work only in some cases. It will not work when somebody writes a function which accepts any conversion and applies this function to a conversion where an optimization would be preferable, unless he is very lucky in inlining and writes only code where the conversion can be resolved statically.
We know that the conversion interface can look like
conv :: String -> IO [CChar]
In this case _any_ optimization making use of the representation of particular conversions must use RULES (which is non-portable BTW). An important application of such optimization is I/O. We don't want to read the file contents into char[] buffers, convert them to [CChar] only to put them back into char[] buffers to call iconv and finally read back a String. The first buffer was already a good input to iconv. Since a conversion will happen by default, it should not be unnecessarily slow. If the representation of some conversions makes the fact that they work best on raw C arrays explicit, calling code will be efficient no matter what was inlined and how dynamic is the place where the conversion was specified. I'm afraid that most of the time RULES will not fire and Haskell will be yet slower than it is now. I don't want to force people to use non-standard string representations to get reasonable performance if the situation could be significantly improved by using explicit representation of conversions implemented in C. It should not be surprising that code which needs to convert between Haskell strings and C strings (or Haskell strings and streams of bytes in files) is dependent on a library which deals with these conversions. Unfortunately the library cannot work well enough if the interface is as simple as String -> IO [CChar]. I like making things simple but here I cannot imagine how do do it without compromising efficiency.
What tool? I am not talking about a library plus tool, but only about the library. The high-level library should be part of the FFI standard and so will come included with every compiler that supports the FFI (just like the `List', `Maybe' and so on standard libraries).
OK, so what should this library provide? My functions were a step in this direction, even if they were not explicitly marked as a part of a high level library proposal. -- __("< Marcin Kowalczyk * qrczak@knm.org.pl http://qrczak.ids.net.pl/ \__/ ^^ SYGNATURA ZASTÊPCZA QRCZAK
participants (4)
-
malcolm-ffi@cs.york.ac.uk -
Manuel M. T. Chakravarty -
Marcin 'Qrczak' Kowalczyk -
qrczak@knm.org.pl