Какой тип нативного конструктора, который выбрасывает исключение во Фреге?

Я пытаюсь выяснить родной интерфейс. Я пытаюсь отправить сообщение с помощью UDP, Вот что у меня есть:

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

Этот код запускается случайно, но он заставляет меня задуматься о нескольких вещах. Во-первых, какой должен быть тип DatagramSocket.new отразить факт выбрасывания исключения? Я пытался упаковать его в Maybe но это закончилось полным беспорядком. Есть ли способ сделать это? Пока я понятия не имею, как обрабатывать исключения в main и это не решает полностью, или, может быть, я что-то упустил. Во-вторых, почему я был вынужден изменить компилятор InetSocketAddress от pure нечистым, использовать его в DatagramSocket.new? Я также был вынужден использовать изменчивую версию JArray везде, где это было необходимо в коде.

2 ответа

Решение

Относительно исключений: есть 2 способа управления исключениями.

Во-первых, оборачивая возвращаемый тип в Either, Это даст вам значение, которое вы хотите в Right и исключение в Left когда это происходит. Чтобы обработать исключение, вы обычно используете сопоставление с образцом или either функция. К сожалению, в коде ввода-вывода (как в вашем случае) это приведет к

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

что не так приятно. Следовательно Either стиль предпочтителен для чистых функций, а для действий ввода / вывода предпочтителен другой стиль.

Для этого объявите ваши нативные функции с throws ... пункт, как вы уже сделали для send а также new, Действие IO/ST с учетом исключений выглядит следующим образом:

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

Там может быть так много catchЕсли вам нужно, но убедитесь, что вы упорядочили их так, чтобы наиболее конкретный предшествовал менее конкретному, т. е. если ExceptionDerive расширяет ExceptionSuper, то перехват для ExceptionDerived должен происходить раньше другого. finally пункт не является обязательным. Обратите внимание, что у вас нет доступа к переменным, связанным в do не блокировать ни в предложениях catch, ни в пункте finally. Если вам это нужно, вам нужно выполнить обработку исключений на более низком уровне (т. Е. Там, где какая-то переменная связана с нужным вам значением).

Пожалуйста, посмотрите улов и, наконец, во фреге или на Фругле.

Убедитесь, что catch отступ с меньшим отступом, чем код в do it. Это должно убедиться, что компилятор видит:

 do { .... } `catch` handler

Вы также можете писать код, не заботясь об исключениях, и добавлять их только позже. Например, вы начинаете с:

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

Позже вы можете переименовать action1 в action1noex и писать

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

Второй момент Для типов данных, которые могут использоваться только в монаде ввода-вывода, рекомендуется объявить их как

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

Это позволяет просто написать Socket вместо Mutable s Socket или же Mutable RealWorld Socket, поскольку компилятор знает, что такое значение всегда будет изменчивым. Вы можете использовать такие типы только в собственных функциях, которые имеют результат ввода-вывода.

И наоборот, для типов данных, которые вы просто создаете, но никогда не используете нечистым образом, вы можете определить их как pure native,

Я не уверен насчет InetSockAddress но я думаю, что это не модифицируется после того, как построен?

Аналогично, ргд. байтовый массив. Если вы всегда и только хотите преобразовать это из и в строки, это можно рассматривать как текстовый тип utf8 (которого, к сожалению, пока нет в библиотеке). Это будет выглядеть как

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

(не проверено, пожалуйста, игнорируйте предупреждение о byte[] теперь)

Во-первых: вы можете заключить результат вызова функции (метода) в Either, как это: native fn :: A -> Either SomeException B или подслащенная версия: native fn :: (SomeException | B),

Во-вторых: значения Фреге неизменны. Если у вас есть некоторый тип данных, определенный во Фреге, он неизменен. Вам разрешено переносить некоторые типы Java и помечать их как неизменяемые pure: data D = pure native com.acme.D, InetSocketAddress не объявлено pure, Это означает, что ImmutableSocketAddress может быть изменен другим потоком в любое время (например, закрытие сокета). Так что компилятор Фреге отмечает это как Mutable, Вы можете передавать такие данные в функции Java только в Mutable порча. Вы можете написать функцию во Фреге, которая не нуждается Mutable заразить, но чтобы передать это аргумент, вам нужно использовать некоторые readonly или же Freezable избавиться от Mutable,

Другие вопросы по тегам