I have a small change request regarding foreign export dynamic. Currently the FFI doc says: topdecl : ... .. | 'foreign' 'export' [callconv] 'dynamic' varid :: prim_type -> IO Addr GHC additionally allows: prim_type -> IO Ptr As usual the FFI "looks through" newtypes. But now that we have FunPtr, the following typing makes much more sense: 'foreign' 'export' [callconv] 'dynamic' varid :: prim_type -> IO (FunPtr prim_type) where both prim_types have to be the *same*. We should probably allow the old Addr-typing as well for some time to facilitate the transition, but not the Ptr-typing (bleeding edge people will know what to do :-). The corresponding changes to GHC look easy, so I'd like to commit this if there are no objections. Furthermore, the FFI docs still talk about Addr only, not Ptr/FunPtr. Now that Addr is deprecated, this should be changed, too. Cheers, Sven
Sven Panne <Sven.Panne@informatik.uni-muenchen.de> wrote,
I have a small change request regarding foreign export dynamic. Currently the FFI doc says:
topdecl : ... .. | 'foreign' 'export' [callconv] 'dynamic' varid :: prim_type -> IO Addr
GHC additionally allows:
prim_type -> IO Ptr
As usual the FFI "looks through" newtypes. But now that we have FunPtr, the following typing makes much more sense:
'foreign' 'export' [callconv] 'dynamic' varid :: prim_type -> IO (FunPtr prim_type)
where both prim_types have to be the *same*. We should probably allow the old Addr-typing as well for some time to facilitate the transition, but not the Ptr-typing (bleeding edge people will know what to do :-). The corresponding changes to GHC look easy, so I'd like to commit this if there are no objections.
Furthermore, the FFI docs still talk about Addr only, not Ptr/FunPtr. Now that Addr is deprecated, this should be changed, too.
Yes, Addr is dead. So, allowing Addr in f.e.d., or anywhere else for that matter, can only be for reasons of temporary backward compatibility. Cheers, Manuel
Yes, Addr is dead. So, allowing Addr in f.e.d., or anywhere else for that matter, can only be for reasons of temporary backward compatibility.
Please note that this temporary backward compatability happens to be an important part of most ffi code currently in existence (at least, all the code that I know and/or care about). I guess you all know this but I just want to make sure that you don't break working code in the rush to move forwards. Alastair ps I have a feeling of feeping creaturism as I watch this list - a nice simple design seems to be getting more complex.
Alastair Reid wrote:
[...] ps I have a feeling of feeping creaturism as I watch this list - a nice simple design seems to be getting more complex.
Could you elaborate on this? The basic FFI *is* still nice and simple IMHO. Mentioning plain Addr in it was a design mistake because: * Pointers to code and data need not have the same size. * The extra type parameter in Ptr/FunPtr tremendously helps to resolve overloading ambiguities. * Addr doesn't carry any type information, so there is no help at all from the type checker, which is even worse than C. Admittedly, we have to be a little bit careful with the FFI libs: Not every little two-liner is really needed in a standard lib, but on the other hand, a standard "vocabulary" for e.g. marshaling improves readability of code written by others a lot and opens up ways for performance improvement which didn't exist before (e.g. alloca & friends). Cheers, Sven
Alastair Reid wrote:
[...] ps I have a feeling of feeping creaturism as I watch this list - a nice simple design seems to be getting more complex.
Could you elaborate on this? The basic FFI *is* still nice and simple IMHO.
I'm thinking of: 1) Trying to support a complex language like Java in the same design (or in any design, really) My sense is that this will gradually infect the ffi design with the complexities of the other language and that this effect will be worsened by the mismatch between the two type systems. 2) Moving library specs, etc. out of the individual ffi decls. This seems to be partly motivated by supporting Java and partly by convenience and a dislike of cpp.
Mentioning plain Addr in it was a design mistake because:
* Pointers to code and data need not have the same size.
I understand this argument though I'm not sure which architectures this applies to. Alphas?
* The extra type parameter in Ptr/FunPtr tremendously helps to resolve overloading ambiguities. * Addr doesn't carry any type information, so there is no help at all from the type checker, which is even worse than C.
I remember briefly using the same trick in the early greencard days but gave up on it because I found myself wanting a many-to-many relationship between C types and Haskell types and using overloading and an extra type parameter I could only get a one-to-many relationship. (This is what greencard's DIS's give you.) So I've been sitting here wondering how things are turning out in practice. For example: How often do you have to create two Haskell types which really ought to be just one type just so that you can use the overloading? Is pointer arithmetic on Ptr's useful? (It's obviously useful for arrays but can you use it (safely and portably) to access fields of structs or do you have to add an explicit byte count to get predictable/safe results.) What's the preferred way to represent an opaque C data type which happens to be a pointer? With Addr, I'd use: newtype AbstractFoo = MkAbstractFoo Addr Should I use Ptr () instead? What libraries is the evolving design being tried out on (I know about your OpenGL library).
Admittedly, we have to be a little bit careful with the FFI libs
I'm less concerned about the ffi libs because: 1) As a user, I only _have_ to use the low level ffi libraries: Addr, Word, Int64, ForeignObj, etc. Its nice if the high level libraries are useful too but not essential. 2) As an ffi implementer/maintainer, I only have to implement the agreed ffi interface and all the libraries on top will "just work". 3) They're not supposed to be standard so it's ok to experiment with the design a bit. 4) I don't have any code that uses them. -- Alastair Reid ps I tried to find a copy of the ffi spec on haskell.org today. After some searching, the only version I could find was more than a year old. I have a note somewhere of where the up-to-date copy is held but if anyone were to go looking for it themselves, they might have a hard time. It'd be good to sprinkle haskell.org with a few pointers to the current spec before Wednesday (which is when Hugs 2001 (including my ffi implementation) will be released.
Mon, 12 Feb 2001 15:49:45 -0700, Alastair Reid <reid@cs.utah.edu> pisze:
1) Trying to support a complex language like Java in the same design (or in any design, really)
Java support has no impact on C support: you can ignore it if you like. The design as is is quite language-independent (and proposed to be yet more independent, by moving some C-specific bits to a better place).
2) Moving library specs, etc. out of the individual ffi decls.
Why would you want to require to repeat the same words with every declaration?!
* Pointers to code and data need not have the same size.
I understand this argument though I'm not sure which architectures this applies to.
AFAIK IA64. And MS-DOS.
How often do you have to create two Haskell types which really ought to be just one type just so that you can use the overloading?
I can't remember I've ever done this.
Is pointer arithmetic on Ptr's useful? (It's obviously useful for arrays but can you use it (safely and portably) to access fields of structs or do you have to add an explicit byte count to get predictable/safe results.)
For structs the type of the pointer to the struct plays no role. You can use hsc2hs and write uid <- (#peek struct stat, st_uid) ptr Otherwise you have to use a C wrappper.
What's the preferred way to represent an opaque C data type which happens to be a pointer?
I prefer this: data FooTag = FooTag -- IMHO should be: data FooTag, but this is not supported. type Foo = Ptr FooTag -- Sometimes ForeignPtr, sometimes both. When the pointer points to known data with unknown representation (data which can be extrated and used to recreate such object), instead of FooTag you can represent the data as a Haskell type and write appropriate Storable instances. It has not happened to me but I guess that some glib types (gstring?) could be treated that way. Sometimes I use just Ptr (), when it's used once or twice locally.
What libraries is the evolving design being tried out on (I know about your OpenGL library).
In QForeign <http://www.sourceforge.net/projects/qforeign/> you can see it applied to bzip2, curses, db3 (incomplete), glob (function), libgr, pcap, readline, zlib, and a bunch of functions mostly from libc (tests/Varia.hsc). -- __("< Marcin Kowalczyk * qrczak@knm.org.pl http://qrczak.ids.net.pl/ \__/ ^^ SYGNATURA ZASTÊPCZA QRCZAK
On 13-Feb-2001, Marcin 'Qrczak' Kowalczyk <qrczak@knm.org.pl> wrote:
Mon, 12 Feb 2001 15:49:45 -0700, Alastair Reid <reid@cs.utah.edu> pisze:
* Pointers to code and data need not have the same size.
I understand this argument though I'm not sure which architectures this applies to.
You don't need to know. It's clearly reasonable to have architectures on which pointers to code and pointers to data have different sizes. That's all you need to know.
AFAIK IA64. And MS-DOS.
You're wrong about IA64. -- Fergus Henderson <fjh@cs.mu.oz.au> | "I have always known that the pursuit | of excellence is a lethal habit" WWW: <http://www.cs.mu.oz.au/~fjh> | -- the last words of T. S. Garp.
Some time ago it was agreed that the typing of `foreign export dynamic' should be changed from prim_type -> IO Addr to prim_type1 -> IO (FunPtr prim_type2) -- where prim_type1 == prim_type2 and deprecating (but keeping) the old typing. While hacking around in GHC's sources to implement this, I came across a few other typing issues. The first one is the typing of `foreign import dynamic', which is documented as Addr -> (prim_args -> IO prim_result) GHC currently implements more or less `Addr -> prim_type', but that's not the point here. I propose changing this to FunPtr prim_type1 -> prim_type2 -- where prim_type1 == prim_type2 again deprecating and keeping the old typing. Apart from the purely technical `IO' (hmmmm, is this *really* necessary?), it makes things almost symmetrical to f.e.d. Another point is the typing of `foreign label', which is currently simply Addr I propose to change this to *two* possible typings: Ptr a FunPtr a because it's not clear if we refer to a data pointer or to a function pointer. This doesn't look particularly nice, or am I missing something here? A last point is the relationship between data type renamings and the FFI: Although we often say that `the FFI looks through newtype' (and `type', of course), it currently only does at certain points, e.g. newtype Foo = Foo Int32 type Bar = Foo -> Word32 foreign import baz :: Bar is allowed, but newtype Blah = Blah (Int32 -> Word32) foreign import booh :: Blah is not. Should we make newtype completely transparent? I'm not sure what this really buys us, but at least it would make the description of the FFI somewhat simpler. I don't have a very strong opinion about this, so I'd like to hear what other people think. Cheers, Sven
participants (5)
-
Alastair Reid -
Fergus Henderson -
Manuel M. T. Chakravarty -
qrczak@knm.org.pl -
Sven Panne