Арифметика с IORef

Aug 24, 2007 01:15

Пытаюсь с помощью мультипараметрических классов типов смоделировать арифметические операции стандартных императивных языков, в которых могут свободно смешиваться константы и мутабельные переменные.
Получилось не совсем так, как я хотел бы, а именно так:

{-# OPTIONS -fglasgow-exts #-}

module IOMath where

import Data.IORef
import Control.Monad

infixr 1 =:=, +:=, -:=, *:=
infixl 2 .+., .-.
infixl 3 .*.

---------------------------------------------------------------------------------------------
-- Операции присваивания и модификации IORef-переменных

class MutableNum l r where
(=:=), (+:=), (-:=), (*:=) :: l -> r -> IO ()

instance Num a => MutableNum (IORef a) a
where
lvar =:= r = writeIORef lvar r
lvar +:= r = do l <- readIORef lvar; writeIORef lvar $! l + r
lvar -:= r = do l <- readIORef lvar; writeIORef lvar $! l - r
lvar *:= r = do l <- readIORef lvar; writeIORef lvar $! l * r

instance Num a => MutableNum (IORef a) (IO a)
where
lvar =:= rvar = do r <- rvar; writeIORef lvar r
lvar +:= rvar = do l <- readIORef lvar; r <- rvar; writeIORef lvar $! l + r
lvar -:= rvar = do l <- readIORef lvar; r <- rvar; writeIORef lvar $! l - r
lvar *:= rvar = do l <- readIORef lvar; r <- rvar; writeIORef lvar $! l * r

instance Num a => MutableNum (IORef a) (IORef a)
where
lvar =:= rvar = do r <- readIORef rvar; writeIORef lvar r
lvar +:= rvar = do l <- readIORef lvar; r <- readIORef rvar; writeIORef lvar $! l + r
lvar -:= rvar = do l <- readIORef lvar; r <- readIORef rvar; writeIORef lvar $! l - r
lvar *:= rvar = do l <- readIORef lvar; r <- readIORef rvar; writeIORef lvar $! l * r

---------------------------------------------------------------------------------------------
-- Арифметика для IORef-переменных

class IONum l r a where
(.+.), (.-.), (.*.) :: l -> r -> IO a

instance Num a => IONum (IORef a) (IORef a) a
where
lvar .+. rvar = do l <- readIORef lvar; r <- readIORef rvar; return $! l + r
lvar .-. rvar = do l <- readIORef lvar; r <- readIORef rvar; return $! l - r
lvar .*. rvar = do l <- readIORef lvar; r <- readIORef rvar; return $! l * r

instance Num a => IONum a (IORef a) a
where
l .+. rvar = do r <- readIORef rvar; return $! l + r
l .-. rvar = do r <- readIORef rvar; return $! l - r
l .*. rvar = do r <- readIORef rvar; return $! l * r

instance Num a => IONum (IORef a) a a
where
lvar .+. r = do l <- readIORef lvar; return $! l + r
lvar .-. r = do l <- readIORef lvar; return $! l - r
lvar .*. r = do l <- readIORef lvar; return $! l * r

instance Num a => IONum (IO a) (IO a) a
where
lvar .+. rvar = do l <- lvar; r <- rvar; return $! l + r
lvar .-. rvar = do l <- lvar; r <- rvar; return $! l - r
lvar .*. rvar = do l <- lvar; r <- rvar; return $! l * r

-- Для краткости в данном примере опустим эти инстанцирования...
-- instance Num a => IONum (IO a) a a
-- instance Num a => IONum a (IO a) a
-- instance Num a => IONum (IO a) (IORef a) a
-- instance Num a => IONum (IORef a) (IO a) a

---------------------------------------------------------------------------------------------

main = do
x <- newInt 1
y <- newInt 5
z <- newInt 10

x =:= (20 :: Integer) -- Всё это выглядит просто ужастно!!! :-х 8-о :о((
z +:= ((x .+. y) :: IO Integer)
x *:= ((z .-. (3 :: Integer)) :: IO Integer)
y =:= (((((2 :: Integer) .*. x) :: IO Integer) .+. ((y .*. z) :: IO Integer) :: IO Integer))

printRef x -- 640 = 20*((10+(20+5))-3)
printRef y -- 1455 = (2*(20*((10+(20+5))-3))) + (5*(10+(20+5)))
printRef z -- 35 = 10+(20+5)

let n = 10
print =<< fac n
print =<< fib n

---------------------------------------------------------------------------------------------
-- Ну куда ж без этого? :о)

fac :: Integer -> IO Integer
fac n = do
x <- newInt 1
for (1, n) $ \i -> x *:= i
readIORef x

fib :: Integer -> IO Integer
fib n = do
x <- newInt 1
p <- newInt 1
s <- newInt 1
for (2, n) $ \_ -> do
x =:= ((p .+. s) :: IO Integer)
p =:= s
s =:= x
readIORef p

---------------------------------------------------------------------------------------------
-- Утилитки

newInt :: Integer -> IO (IORef Integer)
newInt = newIORef

for (from, to) action = when (from <= to) (action from >> for (from+1, to) action)

printRef ref = print =<< readIORef ref

Собственно, недовольство у меня вызывают такие строки, как:

x =:= (20 :: Integer)
z +:= ((x .+. y) :: IO Integer)
x *:= ((z .-. (3 :: Integer)) :: IO Integer)
y =:= (((((2 :: Integer) .*. x) :: IO Integer) .+. ((y .*. z) :: IO Integer) :: IO Integer))

хотелось бы, что бы они выглядели стандартно:

x =:= 20
z +:= x .+. y
x *:= z .-. 3
y =:= 2 .*. x .+. y .*. z

Однако, увы, не получается - компилятор не справляется с выводом типов этих выражений и приходится указывать вручную...
Кроме того, приходится явно указывать типы функций fac и fib, что тоже не нравится...

Нет ли у кого-нибудь каких-нибудь соображений, как это дело можно улучшить?
Может быть, какие-нибудь экзистенциальные типы смогут помочь? Или фундепсы? (Я в них плаваю...)

haskell

Previous post Next post
Up