[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