Какой тип нативного конструктора, который выбрасывает исключение во Фреге?
Я пытаюсь выяснить родной интерфейс. Я пытаюсь отправить сообщение с помощью 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
,