What is type of native constructor which throws exception in Frege?

137 Views Asked by At

I'm trying to figure out native interface. I'm trying to send some message using UDP. Here what I have:

module UDPTest where    

data StringAsBytes = native java.lang.String where
  native getBytes :: String -> ST s (Mutable s (JArray Byte))    

data InetSocketAddress = native java.net.InetSocketAddress where
  native new :: String -> Int -> ST s (Mutable s InetSocketAddress)

data DatagramPacket = native java.net.DatagramPacket where
  native new :: Mutable s (JArray Byte)  -> Int -> Mutable s InetSocketAddress ->  ST s (Mutable s DatagramPacket)    

data DatagramSocket = native java.net.DatagramSocket where
  native new :: () -> IOMutable DatagramSocket throws SocketException
  native send ::  Mutable RealWorld DatagramSocket -> MutableIO DatagramPacket -> IO () throws IOException
  native close :: MutableIO DatagramSocket -> IO ()    

data SocketException = native java.net.SocketException
derive Exceptional SocketException   

main _ = do
  messageStr = "hello world;\n"
  messageAsBytes <- StringAsBytes.getBytes messageStr
  address <- InetSocketAddress.new "localhost" 3003
  messageLen <- messageAsBytes.getLength
  packet <- DatagramPacket.new messageAsBytes messageLen address
  socket <- DatagramSocket.new ()
  socket.send packet
  socket.close

This code accidentally runs but it makes me wonder few things. Firstly, what should be a type of DatagramSocket.new to reflect the fact of throwing exception? I had tried to pack it into Maybe but it ended up in a total mess. Is there any way to do it? For now, I have no idea how to handle exceptions in main and this doesn't address it fully or maybe I'm missing something. Secondly, Why I was forced by compiler to change InetSocketAddress from pure to impure, to use it in DatagramSocket.new? I was also forced to use mutable version of JArray wherever it was needed in code.

2

There are 2 best solutions below

4
On BEST ANSWER

Concerning exceptions: There are 2 ways of managing exceptions.

The first is by wrapping the return type in an Either. This will give you the value you want in Right and the exception in Left when it occures. To handle the exception, you generally use pattern matching or the either function. Unfortunately, in IO code (like in your case) this would lead to code like

do
   r1 <- Socket.new ...
   case r1 of
      Left -> -- handle exception
      Right x -> do
           r2 -> x.send ....
           case r2 of
              ....

which is not so nice. Therefore, the Either style is preferred for pure functions, and for IO/ST actions the other style is preferred.

For this, declare your native functions with a throws ... clause, as you have done already for send and new. The exception aware IO/ST action then looks like:

foo = do
          s <- Socket.new
          d <- s.send ....
          ...
   `catch` (\x1::SocketException -> ...)
   `catch` (\x2::IOException -> ....)
   ....
   `finally` finallyaction

There may be as many catches as you need, but make sure to order them so that the most specific one comes before the less specific one, i.e. if ExceptionDerive extends ExceptionSuper, then the catch for ExceptionDerived must occur before the other one. The finally clause is optional. Note that you don't have access to variables bound in the do block neither in the catch clauses nor in the finally clause. If you need this, you need to do the exception handling on a lower level (i.e., where some variable is bound to the value you need).

Please look up catch and finally in the frege doc or on Froogle.

Make sure the catch is indented less than the code in the do it guards. This is to make sure the compiler sees:

 do { .... } `catch` handler

You can as well write code without caring about exceptions, and add them only later. For example, you start with:

 action1 a b c = do 
       dothis a
       dothat b
       dosomethingelse c
       pure whatyouwant

Later, you can rename action1 to action1noex and write

action1 a b c = action1noex a b c
      `catch`   ....

Second point. For data types that can only be used in the IO Monad, it is recommended to declare them as

data Socket = mutable native java......

This makes it possible to simply write Socket instead of Mutable s Socket or Mutable RealWorld Socket, since the compiler knows that such a value will always be mutable. You can use such types only in native functions that have a IO result.

Conversely, for data types that you just construct but never use in an impure way, you can define them as pure native.

I'm not sure about InetSockAddress but I guess it is not modified once constructed?

Likewise, rgd. the byte array. If you always and only ever want to convert this from and to strings, it can be treated as an utf8 text type (which we unfortunately don't have yet in the library). This would look like

data Charset = pure native java.nio.charset.Charset
pure native utf8 "java.nio.charset.StandardCharsets.UTF_8" :: Charset
data Bytes = pure native "byte[]" 
pure native getBytes :: String -> Charset -> Bytes
pure native getString new :: Bytes -> Charset -> String
toBytes s = getBytes s utf8
fromBytes bs = getString bs utf8

(untested, please ignore the warning concerning byte[] for now)

2
On

Firstly: you can wrap result of function (method) throwing exception into Either, like this: native fn :: A -> Either SomeException B or sugared version: native fn :: (SomeException | B).

Secondly: Frege values are immutable. If you have got some data type A defined in Frege it is immutable. You are allowed to wrap some Java types and for immutable you mark them pure: data D = pure native com.acme.D. InetSocketAddress is not declared pure. This means that ImmutableSocketAddress can be changed by some other thread anywhere in time(for example closing socket). So Frege compiler marks it as Mutable. You can pass such data to Java functions only wrapped with Mutable taint. You can write a function in Frege that doesn't need Mutable taint, but to pass it an argument you need to use some readonly or Freezable to get rid of Mutable.