Search code examples
haskellstmtvar

TVar constructor? I can't get a TVar


I'm new at Haskell and stm and I wanted to make a simple rwlock. First I created the 4 main functions (wlock, wunlock, rlock, runlock) that requires 2 TVar Integers: the amount of reading threads and writing threads.

At this point I can't use it as intended. I try to compile like this

v1 <- atomically(newTVar 0);
v2 <- atomically(newTVar 0);
wlock v1 v2 -- wlock :: TVar Integer -> TVar Integer -> IO ()

which of course is ugly, but it works (not sure why because atomically returns IO (TVar a) instead of TVar a)

What I want:

I'm trying to make it better by hiding the values. I read somewhere that monads might be the way to go, but I haven't study them yet. Instead, I try to make a new type Rwlock as

data Rwlock = Rwlock {  readCant :: TVar Integer 
    ,writeCant :: TVar Integer
}

and a constructor, so I can do something like this:

import Rwlock

do{
    a = rwconst;
    forkIO(reader a);
    forkIO(writer a);
}

where reader will call rlock a, and writer wlock a.

The problem:

I can't make the constructor. This is what I try (ignore maxLectores )

(A):

rwconst :: Integer -> Rwlock
rwconst n = Rwlock {readCant = TVar 0, writeCant = TVar 0, maxLectores = n}
{-rwconst n = Rwlock {readCant = atomically(newTVar 0), writeCant = atomically(newTVar 0), maxLectores = n}-}

But TVar constructor is not exported, and nothing returns a TVar. I don't know why the first block of code works when I do wlock v1 v2, but this way it doesn't.

And (B):

rwconst :: Integer -> Rwlock
rwconst n = do
    a <- (atomically(newTVar 0));
    Rwlock {readCant = a, writeCant = a, maxLectores = n}

Here Rwlock doesn't have a problem, but the do statement returns IO(), instead of Rwlock as I want, and I can't found how to do it.

Anyone can tell me the way to do it? Thanks in advance.


Solution

  • Allocating locks requires doing IO, and you can't get around that. So admit it in the type for your action:

    rwconst :: Integer -> IO Rwlock
    rwcost n = do
        rcount <- newTVarIO 0
        wcount <- newTVarIO 0
        return Rwlock { readCant = rcount, writeCant = wcount, maxLectores = n }
    

    Then, in main, you can write something like this:

    main = do
        a <- rwconst 10
        forkIO (reader a)
        forkIO (writer a)
        -- and you should do something to wait for reader and writer to finish