История изменений
Исправление quasimoto, (текущая версия) :
какая польза от этого в задаче «запретить вызов kmalloc из прерывания»?
Вот:
nomalloc-выражение может состоять только из nomalloc-выражений; а «просто выражение» может состоять из «просто выражений» и nomalloc-выражений.
malloc работает в io, например:
malloc :: Size -> IO (Ptr CChar)
useMalloc :: Size -> IO ()
useMalloc bytes = do
ptr <- malloc bytes
write / read prt
other io
если сделать производную обвёртку от IO:
-- IRQ <: IO
-- up-cast fromIRQ :: IRQ -> IO is safe
-- down-cast IRQ :: IO -> IRQ is unsafe
newtype IRQ a = IRQ { fromIRQ :: IO a } deriving ( Functor, Monad )
и _не_ экспортировать из соответствующего модуля конструктор IRQ :: IO a -> IRQ a, оставив только минимальный нужный интерфейс, то можно будет писать:
handler :: __ -> (__ -> IRQ some) -> IRQ IRQRetType
handler _ fn = do
-- valid irq computations ...
some <- fn __
case some of
_ -> return IRQ_HANDLED
-- etc ...
но ни
foo = malloc
ни любое другое IO нельзя:
handler _ fn = do
foo 100500 -- Couldn't match type `IO' with `IRQ'
вместо конструктора IRQ можно оставить unsafeIO2IRQ для желающих. Аналогично можно разбросать вычисления на большее количество уровней - IO :> Log только для логов, IO :> MM для памяти, IO :> Log :> IRQ (но не MM :> IRQ). Работает за счёт разных вещей - zero-cost newtype, возможность писать императивный код исключительно как монадические комбинаторы обеспечивающие некую форму последовательного выполнения и биндингов в IO, do-нотация, соответственно, rank-1 параметрический полиморфизм и соответствующие проверки типов, сокрытие имён в модулях.
Исправление quasimoto, :
какая польза от этого в задаче «запретить вызов kmalloc из прерывания»?
Вот:
nomalloc-выражение может состоять только из nomalloc-выражений; а «просто выражение» может состоять из «просто выражений» и nomalloc-выражений.
malloc работает в io, например:
malloc :: Size -> IO (Ptr CChar)
useMalloc :: Size -> IO ()
useMalloc bytes = do
ptr <- malloc bytes
write / read prt
other io
если сделать производную обвёртку от IO:
-- IRQ <: IO
-- up-cast fromIRQ :: IRQ -> IO is safe
-- down-cast IRQ :: IO -> IRQ is unsafe
newtype IRQ a = IRQ { fromIRQ :: IO a } deriving ( Functor, Monad )
и _не_ экспортировать из соответствующего модуля конструктор IRQ :: IO a -> IRQ a, оставив только минимальный нужный интерфейс, то можно будет писать:
handler :: __ -> (__ -> IRQ some) -> IRQ IRQRetType
handler _ fn = do
-- valid irq computations ...
some <- fn __
case some of
_ -> return IRQ_HANDLED
-- etc ...
но ни
foo = malloc
ни любое другое IO нельзя:
handler _ fn = do
foo 100500 -- Couldn't match type `IO' with `IRQ'
вместо конструктора IRQ можно оставить unsafeIO2IRQ для желающих. Аналогично можно разбросать вычисления на большее количество уровней - IO :> Log только для логов, IO :> MM для памяти, IO :> Log :> IRQ (но не MM :> IRQ). Работает за счёт разных вещей - zero-cost newtype, общепринятость do-нотации как сахара для комбинаторов обеспечивающих некую форму последовательного выполнения и биндингов в Monad, rank-1 полиморфизм и соответствующие проверки типов, сокрытие имён в модулях.
Исходная версия quasimoto, :
какая польза от этого в задаче «запретить вызов kmalloc из прерывания»?
Вот:
nomalloc-выражение может состоять только из nomalloc-выражений; а «просто выражение» может состоять из «просто выражений» и nomalloc-выражений.
malloc работает в io, например:
malloc :: Size -> IO (Ptr CChar)
useMalloc :: Size -> IO ()
useMalloc bytes = do
ptr <- malloc bytes
write / read prt
other io
если сделать производную обвёртку от IO:
-- IRQ <: IO
-- up-cast fromIRQ :: IRQ -> IO is safe
-- down-cast IRQ :: IO -> IRQ is unsafe
newtype IRQ a = IRQ { fromIRQ :: IO a } deriving ( Functor, Monad )
и _не_ экспортировать из соответствующего модуля конструктор IRQ :: IO a -> IRQ a, оставив только минимальный нужный интерфейс, то можно будет писать:
handler :: __ -> (__ -> IRQ some) -> IRQ IRQRetType
handler _ fn = do
-- valid irq computations ...
some <- fn __
case some of
_ -> return IRQ_HANDLED
-- etc ...
но ни
foo = malloc
ни любое другое IO нельзя:
handler _ fn = do
foo 100500 -- Couldn't match type `IO' with `IRQ'
вместо конструктора IRQ можно оставить unsafeIO2IRQ для желающих. Аналогично можно разбросать вычисления на большее количество уровней - IO :> Log только для логов, IO :> MM для памяти, IO :> Log :> IRQ (но не MM :> IO). Работает за счёт разных вещей - zero-cost newtype, общепринятость do-нотации как сахара для комбинаторов обеспечивающих некую форму последовательного выполнения и биндингов в Monad, rank-1 полиморфизм и соответствующие проверки типов, сокрытие имён в модулях.