[RFC] Network.Socket: change get/setSocketOption

Hi, (This mail is related to http://permalink.gmane.org/gmane.comp.lang.haskell.libraries/3788 ) What I'm proposing is to make SocketOption a proper ADT and change get- and setSocketOption to functions that take type constructors instead of tags as arguments, i.e. getSocketOption :: Socket -> (a -> SocketOption) -> IO a setSocketOption :: Socket -> (a -> SocketOption) -> a -> IO () To make that work, the two functions are implemented in a type class. I'll attach a patch bundle (in darcs send -o format) of two patches: 1) implement the infrastructure for this change. it is mostly backward compatible: As long as code doesn't explicitely mention the SocketOption type, it should work without modifications. 2) give the options more sensible types. -- this breaks existing code. For example, setSocketOption sock ReuseAddr 1 becomes setSocketOption sock ReuseAddr True (the patch includes the necessary fixes for the network package) What's missing is a solution for the problem that prompted this change, namely setting struct time_val values. To do that, I'd need some Storable type that maps to struct time_val. I think Network.* is not the proper place to implement that. Where should it go? - Foreign.C.Types - not generally useful enough, I think. - System.Time - not portable enough? - System.Posix.* - seems to be the right place to me. related stuff: http://cryp.to/hsdns/System/Posix/GetTimeOfDay.hsc -- quite close to what I'd like to have; conversions to, say, System.Time.ClockTime would be useful though. http://www.haskell.org/~simonmar/time/NewTime.hsc -- looks like a new System.Time to me; the CTimeVal handling seems to be identical to System.Time though Comments, questions and suggestions are welcome. Bertram P.S. thanks to heatsink on #haskell for the suggestion of passing constructors to the functions. That simplified the code a lot at a small loss of safety, and made the interface more similar to the old one.

On 6/25/06, I wrote:
What I'm proposing is to make SocketOption a proper ADT and change get- and setSocketOption to functions that take type constructors instead of tags as arguments, i.e.
getSocketOption :: Socket -> (a -> SocketOption) -> IO a setSocketOption :: Socket -> (a -> SocketOption) -> a -> IO ()
It should be noted that while the patch defines an ADT, it does sort of abuse it by providing an interface that uses the constructors in a tag-like manner. I did this for two reasons, first, it saves a bunch of code, and second, it mimics the old interface very closely. Passing a non-constructor argument will lead to weird results, but as far as I can see, nothing truly bad happens, no memory corruption at least. I could easily be persuaded to actually make the interface look like this: getSocketOption :: Socket -> SocketOption -> IO SocketOption setSocketOption :: Socket -> SocketOption -> IO () (getSocketOption would fill in the value in SocketOption with the actual value, that is the parameter would act as a template) This increases safety, but it comes at a cost in code size (with ugly #ifdefs), unless I'm missing something. I'd like to avoid creating a second ADT for queries or separate functions for each of the options. Bertram

Bertram Felgenhauer wrote:
(This mail is related to http://permalink.gmane.org/gmane.comp.lang.haskell.libraries/3788 )
What I'm proposing is to make SocketOption a proper ADT and change get- and setSocketOption to functions that take type constructors instead of tags as arguments, i.e.
getSocketOption :: Socket -> (a -> SocketOption) -> IO a setSocketOption :: Socket -> (a -> SocketOption) -> a -> IO ()
To make that work, the two functions are implemented in a type class.
I think this is a reasonable design, despite the slight misuse of algebraic data types. I thought about using a type class with a functional dependency to express the relationship between the option tag and the option type, but that ends up being more elaborate for not much gain. Simon PJ pointed out to me an alternative design using GADTs: data SocketOption a where ReuseAddr :: SocketOption Int ... getSocketOption :: Socket -> SocketOption a -> IO a setSocketOption :: Socket -> SocketOption a -> a -> IO () This seems to me the best design, but requiring GADTs isn't really practical for this widely-used API. So I'm in favour of Bertram's proposal above... any other comments? Cheers, Simon

Simon Marlow wrote:
Simon PJ pointed out to me an alternative design using GADTs:
data SocketOption a where ReuseAddr :: SocketOption Int ...
getSocketOption :: Socket -> SocketOption a -> IO a setSocketOption :: Socket -> SocketOption a -> a -> IO ()
Ah, clever. But that's an easy change if GADTs become more widely adopted.
So I'm in favour of Bertram's proposal above...
I'm glad you like it.
any other comments?
Apparently not? Or did they go to you directly? Should I go ahead and (try to) make a CTimeVal type with Storable instance? I now believe System.Posix.Time would be the best place for that (which exists in the unix package.) regards, Bertram
participants (2)
-
Bertram Felgenhauer
-
Simon Marlow