[Haskell-beginners] State in IO monad

coot at coot.me coot at coot.me
Sun Nov 15 16:30:36 UTC 2020


You are right, with the provided interface, the only way is to preserve the state using a mutable variable.  In this case you could do something like this:

```
main :: IO ()
main = do
    var <- newMVar (undefined :: State) -- the initial state
    serve HostAny "4000" (\(s, _) -> handle var s)
  where
    handle var s = do ...
```

I don't know how the telnet library works in the background, but it's likely that it runs a single event loop, so using `IORef` if you don't have any other `IORef`s around might be ok (check the the haddocs of `atomicModifyIORef`).

Best regards,
Marcin Szamotulski

‐‐‐‐‐‐‐ Original Message ‐‐‐‐‐‐‐

On Sunday, November 15th, 2020 at 16:07, Dmitriy Matrosov <sgf.dma at gmail.com> wrote:

> On 11/14/20 5:36 PM, coot at coot.me wrote:
> 

> > Hi,
> > 

> > First you should ask yourself if you need concurrent access to that state. If not then you can thread this state as argument, e.g.
> 

> No, for the time being i don't need concurrent access. Just make it work.
> 

> >     data State = State
> >     

> >     someAction :: State -> IO a
> >     someOtherAction :: State -> IO b
> >     

> > 

> > This functions compose perfectly well, if you need to update the state then each action should also return it:
> > 

> >     someAction :: State -> IO (State, a)
> >     someOtherAction :: State -> IO (State, b)
> >     

> 

> Thanks, but the problem is how this library works. Here is example
> 

> provided by library author.
> 

> So, essentially, first i need to initialize library with
> 

> telnetInit :: [OptionSpec] -> [Flag] -> EventHandler -> IO Telnet
> 

> and pass it callback function with type
> 

> type EventHandler = TelnetPtr -> Event -> IO ()
> 

> and this callback function will be called upon each telnet event, including
> 

> data reception. But because each time the same function is called and it is in
> 

> IO monad, i don't see a way how to pass something (i.e. state) from one call
> 

> to the second. In fact, i don't even see a way how to save result of the
> 

> single call to use later in the rest of the program. Unless i use global
> 

> state in IO (using 'IORef' or 'MVar' (didn't tried yet)).
> 

> Alternatively, (looking at example) i may parse received data after
> 

> 'recv' call before telling telnet library that data is received with 'telnetRecv',
> 

> but then callback function passed to 'telnetInit' does not needed at all
> 

> (well, at least in my simple case). And this looks a little strange: why do i
> 

> ever need this callback function, if i can't parse data there and save it
> 

> for use in the rest of the program?
> 

> 1: https://git.sr.ht/~jack/libtelnet-haskell/tree/master/example/Main.hs
> 

> > You still can compose those. This is `StateT` monad transformer in disguise (from `transformers` / `stm` package combo), but I personally prefer to pass arguments explicitly and only use `StateT` or `RederT` monad if they bring more clarity.
> > 

> > If you need concurrent access from different threads, then I'd use `MVar` or `stm` for holding a mutable variable, rather than `IORef` (which are useful if the code needs to be as fast as possible, but they present less guarantees).
> > 

> > If you go with mutable cell to hold the state, then there's no need to create it using `unsafePerformIO`, you can directly create it in `main :: IO ()` and pass it around as an argument. `unsafePerformIO` is really rarely needed and most of the time there are well established patterns to avoid using it.
> > 

> > Best regards,
> > 

> > Marcin Szamotulski
> > 

> > ‐‐‐‐‐‐‐ Original Message ‐‐‐‐‐‐‐
> > 

> > On Saturday, November 14th, 2020 at 14:53, Dmitriy Matrosov sgf.dma at gmail.com wrote:
> > 

> > > Hi.
> > 

> > > I want to use telnet library to run some
> > 

> > > commands on cisco switches and then parse output.
> > 

> > > Thus, first i need to login (enter username and
> > 

> > > password, etc) and then run commands and collect
> > 

> > > output. As far as i understand that library API,
> > 

> > > all of output parsing should be done in
> > 

> > > EventHandler
> > 

> > > of type
> > 

> > > TelnetPtr -> Event -> IO ()
> > 

> > > But, these (mine) operations require some state
> > 

> > > (to track where am i now (entered username,
> > 

> > > entered password, etc) and to collect output). But
> > 

> > > the monad of this event handler is IO, so i can't
> > 

> > > see any simple way of adding state to it apart
> > 

> > > from 'unsafePerfromIO' trick, like
> > 

> > > telnetRef :: IORef TelnetRef
> > 

> > > {-# NOINLINE telnetRef #-}
> > 

> > > telnetRef = unsafePerformIO $ newIORef (TelnetRef undefined Unauth M.empty)
> > 

> > > ('undefined' is 'Telnet' pointer, which is
> > 

> > > returned by 'telnetInit' and will be initialized
> > 

> > > later..)
> > 

> > > Is there a better a way to do this?
> > 

> > > Thanks.
> > 

> > > Beginners mailing list
> > 

> > > Beginners at haskell.org
> > 

> > > http://mail.haskell.org/cgi-bin/mailman/listinfo/beginners
> > > 

> > > Beginners mailing list
> > > 

> > > Beginners at haskell.org
> > > 

> > > http://mail.haskell.org/cgi-bin/mailman/listinfo/beginners
> 

> Beginners mailing list
> 

> Beginners at haskell.org
> 

> http://mail.haskell.org/cgi-bin/mailman/listinfo/beginners
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 509 bytes
Desc: OpenPGP digital signature
URL: <http://mail.haskell.org/pipermail/beginners/attachments/20201115/27f4da4c/attachment.sig>


More information about the Beginners mailing list