From sgf.dma at gmail.com Sat Nov 14 13:53:35 2020 From: sgf.dma at gmail.com (Dmitriy Matrosov) Date: Sat, 14 Nov 2020 16:53:35 +0300 Subject: [Haskell-beginners] State in IO monad Message-ID: <72b8181f-1e5b-f4a4-c336-8157b26aa4ea@gmail.com> Hi. I want to use [telnet library][1] 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][2], 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. [1]: https://hackage.haskell.org/package/libtelnet [2]: https://wiki.haskell.org/Top_level_mutable_state From coot at coot.me Sat Nov 14 14:36:06 2020 From: coot at coot.me (coot at coot.me) Date: Sat, 14 Nov 2020 14:36:06 +0000 Subject: [Haskell-beginners] State in IO monad In-Reply-To: <72b8181f-1e5b-f4a4-c336-8157b26aa4ea@gmail.com> References: <72b8181f-1e5b-f4a4-c336-8157b26aa4ea@gmail.com> Message-ID: 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. ``` 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) ``` 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 wrote: > Hi. > > I want to use [telnet library][1] 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][2], 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. > > [1]: https://hackage.haskell.org/package/libtelnet > > [2]: https://wiki.haskell.org/Top_level_mutable_state > > 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: From sgf.dma at gmail.com Sun Nov 15 15:07:42 2020 From: sgf.dma at gmail.com (Dmitriy Matrosov) Date: Sun, 15 Nov 2020 18:07:42 +0300 Subject: [Haskell-beginners] State in IO monad In-Reply-To: References: <72b8181f-1e5b-f4a4-c336-8157b26aa4ea@gmail.com> Message-ID: <52c41084-21d0-af05-44b5-5a15fa28b600@gmail.com> 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][1] 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][1]) 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 wrote: > >> Hi. >> > >> I want to use [telnet library][1] 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][2], 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. >> > >> [1]: https://hackage.haskell.org/package/libtelnet >> > >> [2]: https://wiki.haskell.org/Top_level_mutable_state >> > >> 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 From coot at coot.me Sun Nov 15 16:30:36 2020 From: coot at coot.me (coot at coot.me) Date: Sun, 15 Nov 2020 16:30:36 +0000 Subject: [Haskell-beginners] State in IO monad In-Reply-To: <52c41084-21d0-af05-44b5-5a15fa28b600@gmail.com> References: <72b8181f-1e5b-f4a4-c336-8157b26aa4ea@gmail.com> <52c41084-21d0-af05-44b5-5a15fa28b600@gmail.com> Message-ID: <255NJIWDfZJaLLNfspuBJcc7-Y1kzx6p0og_w0yvIVp86lJOJlN6UP9aN09I3MAfHx3aHiY_2G5MGBlhDbVFrWd-Z_zFsAr8qeBzRXedmRA=@coot.me> 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 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: