Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    primarykey
    data
    text
    <p>As you describe the problem, there is no need for state at all:</p> <pre><code>data Browser = Browser { getUrl :: String, getHtml :: String, getLinks :: [String]} getLinksFromHtml :: String -&gt; [String] -- use Text.HTML.TagSoup, it should be lazy goto :: String -&gt; IO Browser goto url = do -- assume getUrlContents is lazy, like hGetContents html &lt;- getUrlContents url let links = getLinksFromHtml html return (Browser url html links) </code></pre> <blockquote> <p>It’s possbile to have 2 or “browsers” at once, with its own separate state:</p> </blockquote> <p>You obviously can have as many as you want, and they can't interfere with each other.</p> <p>Now the equivalent of your snippets. First:</p> <pre><code>htmlFromGooglesFirstLink = do b &lt;- goto "http://www.google.com" let firstLink = head (links b) b2 &lt;- goto firstLink -- note that a new browser is returned putStr (getHtml b2) </code></pre> <p>And second:</p> <pre><code>twoBrowsers = do b1 &lt;- goto "http://www.google.com" b2 &lt;- goto "http://www.stackoverflow.com/" putStr (getHtml b1) putStr (getHtml b2) </code></pre> <p>UPDATE (reply to your update):</p> <blockquote> <p>If Browser has a state, it can send HTTP Referer header and cookies while hiding all mechanics inside itself and giving nice API.</p> </blockquote> <p>No need for state still, <code>goto</code> can just take a Browser argument. First, we'll need to extend the type: </p> <pre><code>data Browser = Browser { getUrl :: String, getHtml :: String, getLinks :: [String], getCookies :: Map String String } -- keys are URLs, values are cookie strings getUrlContents :: String -&gt; String -&gt; String -&gt; IO String getUrlContents url referrer cookies = ... goto :: String -&gt; Browser -&gt; IO Browser goto url browser = let referrer = getUrl browser cookies = getCookies browser ! url in do html &lt;- getUrlContents url referrer cookies let links = getLinksFromHtml html return (Browser url html links) newBrowser :: Browser newBrowser = Browser "" "" [] empty </code></pre> <blockquote> <p>If Browser has no state, the developer is likely to pass around all current URL/HTML/Cookies -- and this adds noise to scenario code.</p> </blockquote> <p>No, you just pass values of type Browser around. For your example,</p> <pre><code>useGoogle :: IO () useGoogle = do b &lt;- goto "http://www.google.com/" newBrowser let b2 = typeIntoInput 0 "haskell" b b3 &lt;- clickButton "Google Search" b2 ... </code></pre> <p>Or you can get rid of those variables:</p> <pre><code>(&gt;&gt;~) = flip mapM -- use for binding pure functions useGoogle = goto "http://www.google.com/" newBrowser &gt;&gt;~ typeIntoInput 0 "haskell" &gt;&gt;= clickButton "Google Search" &gt;&gt;= clickLink "2" &gt;&gt;= clickLink "3" &gt;&gt;~ getHtml &gt;&gt;= putStr </code></pre> <p>Does this look good enough? Note that Browser is still immutable.</p>
    singulars
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. VO
      singulars
      1. This table or related slice is empty.
    2. VO
      singulars
      1. This table or related slice is empty.
    3. VO
      singulars
      1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload