Haskell IORef - an answer vs. a function to get an answer





.everyoneloves__top-leaderboard:empty,.everyoneloves__mid-leaderboard:empty,.everyoneloves__bot-mid-leaderboard:empty{ height:90px;width:728px;box-sizing:border-box;
}







4















I'm trying to understand how IORefs are really used, and I'm having trouble following the sample code I found on https://www.seas.upenn.edu/~cis194/spring15/lectures/12-unsafe.html



newCounter :: IO (IO Int)
newCounter = do
r <- newIORef 0
return $ do
v <- readIORef r
writeIORef r (v + 1)
return v

printCounts :: IO ()
printCounts = do
c <- newCounter
print =<< c
print =<< c
print =<< c


When printCounts executes "c <- newCounter", why doesn't c get the result of doing the work in the newCounter "return $ do" block, which seems like it should get assigned to the constant "IO 0" the first time it is called and then never change? Instead, c seems to get assigned the function defined in that "return $ do" block, which is then executed anew every time printCounts gets to another "print =<< c." It seems that the answer somehow lies in newCounter having the double nested "IO (IO Int)" type, but I can't follow why that makes c a function to be re-executed when called instead of a constant evaluated just once.










share|improve this question

























  • A side note: you should not have an IO (IO Int) type, this is bad style.

    – AJFarmar
    Nov 26 '18 at 19:43






  • 6





    @AJFarmar, I don't see anything bad about that style; it's just providing a counter with a restricted interface.

    – dfeuer
    Nov 26 '18 at 20:03






  • 2





    Well, I suppose it would actually be better to provide an abstract Counter type....

    – dfeuer
    Nov 26 '18 at 20:10











  • let-over-lambda to bind-over-return. :)

    – Will Ness
    Nov 26 '18 at 20:14











  • @AJFarmar I wouldn't use IO (IO Int) just for a counter: but that pattern is often the best way of expressing something.

    – Jeremy List
    Nov 28 '18 at 20:40


















4















I'm trying to understand how IORefs are really used, and I'm having trouble following the sample code I found on https://www.seas.upenn.edu/~cis194/spring15/lectures/12-unsafe.html



newCounter :: IO (IO Int)
newCounter = do
r <- newIORef 0
return $ do
v <- readIORef r
writeIORef r (v + 1)
return v

printCounts :: IO ()
printCounts = do
c <- newCounter
print =<< c
print =<< c
print =<< c


When printCounts executes "c <- newCounter", why doesn't c get the result of doing the work in the newCounter "return $ do" block, which seems like it should get assigned to the constant "IO 0" the first time it is called and then never change? Instead, c seems to get assigned the function defined in that "return $ do" block, which is then executed anew every time printCounts gets to another "print =<< c." It seems that the answer somehow lies in newCounter having the double nested "IO (IO Int)" type, but I can't follow why that makes c a function to be re-executed when called instead of a constant evaluated just once.










share|improve this question

























  • A side note: you should not have an IO (IO Int) type, this is bad style.

    – AJFarmar
    Nov 26 '18 at 19:43






  • 6





    @AJFarmar, I don't see anything bad about that style; it's just providing a counter with a restricted interface.

    – dfeuer
    Nov 26 '18 at 20:03






  • 2





    Well, I suppose it would actually be better to provide an abstract Counter type....

    – dfeuer
    Nov 26 '18 at 20:10











  • let-over-lambda to bind-over-return. :)

    – Will Ness
    Nov 26 '18 at 20:14











  • @AJFarmar I wouldn't use IO (IO Int) just for a counter: but that pattern is often the best way of expressing something.

    – Jeremy List
    Nov 28 '18 at 20:40














4












4








4








I'm trying to understand how IORefs are really used, and I'm having trouble following the sample code I found on https://www.seas.upenn.edu/~cis194/spring15/lectures/12-unsafe.html



newCounter :: IO (IO Int)
newCounter = do
r <- newIORef 0
return $ do
v <- readIORef r
writeIORef r (v + 1)
return v

printCounts :: IO ()
printCounts = do
c <- newCounter
print =<< c
print =<< c
print =<< c


When printCounts executes "c <- newCounter", why doesn't c get the result of doing the work in the newCounter "return $ do" block, which seems like it should get assigned to the constant "IO 0" the first time it is called and then never change? Instead, c seems to get assigned the function defined in that "return $ do" block, which is then executed anew every time printCounts gets to another "print =<< c." It seems that the answer somehow lies in newCounter having the double nested "IO (IO Int)" type, but I can't follow why that makes c a function to be re-executed when called instead of a constant evaluated just once.










share|improve this question
















I'm trying to understand how IORefs are really used, and I'm having trouble following the sample code I found on https://www.seas.upenn.edu/~cis194/spring15/lectures/12-unsafe.html



newCounter :: IO (IO Int)
newCounter = do
r <- newIORef 0
return $ do
v <- readIORef r
writeIORef r (v + 1)
return v

printCounts :: IO ()
printCounts = do
c <- newCounter
print =<< c
print =<< c
print =<< c


When printCounts executes "c <- newCounter", why doesn't c get the result of doing the work in the newCounter "return $ do" block, which seems like it should get assigned to the constant "IO 0" the first time it is called and then never change? Instead, c seems to get assigned the function defined in that "return $ do" block, which is then executed anew every time printCounts gets to another "print =<< c." It seems that the answer somehow lies in newCounter having the double nested "IO (IO Int)" type, but I can't follow why that makes c a function to be re-executed when called instead of a constant evaluated just once.







haskell io closures do-notation ioref






share|improve this question















share|improve this question













share|improve this question




share|improve this question








edited Nov 26 '18 at 20:18









Will Ness

46.7k469127




46.7k469127










asked Nov 26 '18 at 19:38









David JacobsDavid Jacobs

233




233













  • A side note: you should not have an IO (IO Int) type, this is bad style.

    – AJFarmar
    Nov 26 '18 at 19:43






  • 6





    @AJFarmar, I don't see anything bad about that style; it's just providing a counter with a restricted interface.

    – dfeuer
    Nov 26 '18 at 20:03






  • 2





    Well, I suppose it would actually be better to provide an abstract Counter type....

    – dfeuer
    Nov 26 '18 at 20:10











  • let-over-lambda to bind-over-return. :)

    – Will Ness
    Nov 26 '18 at 20:14











  • @AJFarmar I wouldn't use IO (IO Int) just for a counter: but that pattern is often the best way of expressing something.

    – Jeremy List
    Nov 28 '18 at 20:40



















  • A side note: you should not have an IO (IO Int) type, this is bad style.

    – AJFarmar
    Nov 26 '18 at 19:43






  • 6





    @AJFarmar, I don't see anything bad about that style; it's just providing a counter with a restricted interface.

    – dfeuer
    Nov 26 '18 at 20:03






  • 2





    Well, I suppose it would actually be better to provide an abstract Counter type....

    – dfeuer
    Nov 26 '18 at 20:10











  • let-over-lambda to bind-over-return. :)

    – Will Ness
    Nov 26 '18 at 20:14











  • @AJFarmar I wouldn't use IO (IO Int) just for a counter: but that pattern is often the best way of expressing something.

    – Jeremy List
    Nov 28 '18 at 20:40

















A side note: you should not have an IO (IO Int) type, this is bad style.

– AJFarmar
Nov 26 '18 at 19:43





A side note: you should not have an IO (IO Int) type, this is bad style.

– AJFarmar
Nov 26 '18 at 19:43




6




6





@AJFarmar, I don't see anything bad about that style; it's just providing a counter with a restricted interface.

– dfeuer
Nov 26 '18 at 20:03





@AJFarmar, I don't see anything bad about that style; it's just providing a counter with a restricted interface.

– dfeuer
Nov 26 '18 at 20:03




2




2





Well, I suppose it would actually be better to provide an abstract Counter type....

– dfeuer
Nov 26 '18 at 20:10





Well, I suppose it would actually be better to provide an abstract Counter type....

– dfeuer
Nov 26 '18 at 20:10













let-over-lambda to bind-over-return. :)

– Will Ness
Nov 26 '18 at 20:14





let-over-lambda to bind-over-return. :)

– Will Ness
Nov 26 '18 at 20:14













@AJFarmar I wouldn't use IO (IO Int) just for a counter: but that pattern is often the best way of expressing something.

– Jeremy List
Nov 28 '18 at 20:40





@AJFarmar I wouldn't use IO (IO Int) just for a counter: but that pattern is often the best way of expressing something.

– Jeremy List
Nov 28 '18 at 20:40












1 Answer
1






active

oldest

votes


















7














You can think of IO as a type of programs. newCounter :: IO (IO Int) is a program that outputs a program. More precisely, newCounter allocates a new counter, and returns a program that, when run, increments the counter and returns its old value. newCounter doesn't execute the program it returns. It would if you wrote instead:



newCounter :: IO (IO Int)
newCounter = do
r <- newIORef 0
let p = do -- name the counter program p
v <- readIORef r
writeIORef r (v + 1)
return v
p -- run the counter program once
return p -- you can still return it to run again later




You can also use equational reasoning to unfold printCounts into a sequence of primitives. All versions of printCounts below are equivalent programs:



-- original definition
printCounts :: IO ()
printCounts = do
c <- newCounter
print =<< c
print =<< c
print =<< c

-- by definition of newCounter...

printCounts = do
c <- do
r <- newIORef 0
return $ do
v <- readIORef r
writeIORef r (v + 1)
return v
print =<< c
print =<< c
print =<< c

-- by the monad laws (quite hand-wavy for brevity)
-- do
-- c <- do
-- X
-- Y
-- .....
-- =
-- do
-- X
-- c <-
-- Y
-- .....
--
-- (more formally,
-- ((m >>= x -> k x) >>= h) = (m >>= (x -> k x >>= h)))

printCounts = do
r <- newIORef 0
c <-
return $ do
v <- readIORef r
writeIORef r (v + 1)
return v
print =<< c
print =<< c
print =<< c

-- c <- return X
-- =
-- let c = X
--
-- (more formally, ((return X) >>= (c -> k c)) = (k X)

printCounts = do
r <- newIORef 0
let c = do
v <- readIORef r
writeIORef r (v + 1)
return v
print =<< c
print =<< c
print =<< c

-- let-substitution

printCounts = do
r <- newIORef 0
print =<< do
v <- readIORef r
writeIORef r (v + 1)
return v
print =<< do
v <- readIORef r
writeIORef r (v + 1)
return v
print =<< do
v <- readIORef r
writeIORef r (v + 1)
return v

-- after many more applications of monad laws and a bit of renaming to avoid shadowing
-- (in particular, one important step is ((return v >>= print) = (print v)))

printCounts = do
r <- newIORef 0
v1 <- readIORef r
writeIORef r (v1 + 1)
print v1
v2 <- readIORef r
writeIORef r (v2 + 1)
print v2
v3 <- readIORef r
writeIORef r (v3 + 1)
print v3


In the final version, you can see that printCounts quite literally allocates a counter and increments it three times, printing each intermediate value.



One key step is the let-substitution one, where the counter program gets duplicated, which is why it gets to run three times. let x = p; ... is different from x <- p; ..., which runs p, and binds x to the result rather than the program p itself.






share|improve this answer


























    Your Answer






    StackExchange.ifUsing("editor", function () {
    StackExchange.using("externalEditor", function () {
    StackExchange.using("snippets", function () {
    StackExchange.snippets.init();
    });
    });
    }, "code-snippets");

    StackExchange.ready(function() {
    var channelOptions = {
    tags: "".split(" "),
    id: "1"
    };
    initTagRenderer("".split(" "), "".split(" "), channelOptions);

    StackExchange.using("externalEditor", function() {
    // Have to fire editor after snippets, if snippets enabled
    if (StackExchange.settings.snippets.snippetsEnabled) {
    StackExchange.using("snippets", function() {
    createEditor();
    });
    }
    else {
    createEditor();
    }
    });

    function createEditor() {
    StackExchange.prepareEditor({
    heartbeatType: 'answer',
    autoActivateHeartbeat: false,
    convertImagesToLinks: true,
    noModals: true,
    showLowRepImageUploadWarning: true,
    reputationToPostImages: 10,
    bindNavPrevention: true,
    postfix: "",
    imageUploader: {
    brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
    contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
    allowUrls: true
    },
    onDemand: true,
    discardSelector: ".discard-answer"
    ,immediatelyShowMarkdownHelp:true
    });


    }
    });














    draft saved

    draft discarded


















    StackExchange.ready(
    function () {
    StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53487894%2fhaskell-ioref-an-answer-vs-a-function-to-get-an-answer%23new-answer', 'question_page');
    }
    );

    Post as a guest















    Required, but never shown

























    1 Answer
    1






    active

    oldest

    votes








    1 Answer
    1






    active

    oldest

    votes









    active

    oldest

    votes






    active

    oldest

    votes









    7














    You can think of IO as a type of programs. newCounter :: IO (IO Int) is a program that outputs a program. More precisely, newCounter allocates a new counter, and returns a program that, when run, increments the counter and returns its old value. newCounter doesn't execute the program it returns. It would if you wrote instead:



    newCounter :: IO (IO Int)
    newCounter = do
    r <- newIORef 0
    let p = do -- name the counter program p
    v <- readIORef r
    writeIORef r (v + 1)
    return v
    p -- run the counter program once
    return p -- you can still return it to run again later




    You can also use equational reasoning to unfold printCounts into a sequence of primitives. All versions of printCounts below are equivalent programs:



    -- original definition
    printCounts :: IO ()
    printCounts = do
    c <- newCounter
    print =<< c
    print =<< c
    print =<< c

    -- by definition of newCounter...

    printCounts = do
    c <- do
    r <- newIORef 0
    return $ do
    v <- readIORef r
    writeIORef r (v + 1)
    return v
    print =<< c
    print =<< c
    print =<< c

    -- by the monad laws (quite hand-wavy for brevity)
    -- do
    -- c <- do
    -- X
    -- Y
    -- .....
    -- =
    -- do
    -- X
    -- c <-
    -- Y
    -- .....
    --
    -- (more formally,
    -- ((m >>= x -> k x) >>= h) = (m >>= (x -> k x >>= h)))

    printCounts = do
    r <- newIORef 0
    c <-
    return $ do
    v <- readIORef r
    writeIORef r (v + 1)
    return v
    print =<< c
    print =<< c
    print =<< c

    -- c <- return X
    -- =
    -- let c = X
    --
    -- (more formally, ((return X) >>= (c -> k c)) = (k X)

    printCounts = do
    r <- newIORef 0
    let c = do
    v <- readIORef r
    writeIORef r (v + 1)
    return v
    print =<< c
    print =<< c
    print =<< c

    -- let-substitution

    printCounts = do
    r <- newIORef 0
    print =<< do
    v <- readIORef r
    writeIORef r (v + 1)
    return v
    print =<< do
    v <- readIORef r
    writeIORef r (v + 1)
    return v
    print =<< do
    v <- readIORef r
    writeIORef r (v + 1)
    return v

    -- after many more applications of monad laws and a bit of renaming to avoid shadowing
    -- (in particular, one important step is ((return v >>= print) = (print v)))

    printCounts = do
    r <- newIORef 0
    v1 <- readIORef r
    writeIORef r (v1 + 1)
    print v1
    v2 <- readIORef r
    writeIORef r (v2 + 1)
    print v2
    v3 <- readIORef r
    writeIORef r (v3 + 1)
    print v3


    In the final version, you can see that printCounts quite literally allocates a counter and increments it three times, printing each intermediate value.



    One key step is the let-substitution one, where the counter program gets duplicated, which is why it gets to run three times. let x = p; ... is different from x <- p; ..., which runs p, and binds x to the result rather than the program p itself.






    share|improve this answer






























      7














      You can think of IO as a type of programs. newCounter :: IO (IO Int) is a program that outputs a program. More precisely, newCounter allocates a new counter, and returns a program that, when run, increments the counter and returns its old value. newCounter doesn't execute the program it returns. It would if you wrote instead:



      newCounter :: IO (IO Int)
      newCounter = do
      r <- newIORef 0
      let p = do -- name the counter program p
      v <- readIORef r
      writeIORef r (v + 1)
      return v
      p -- run the counter program once
      return p -- you can still return it to run again later




      You can also use equational reasoning to unfold printCounts into a sequence of primitives. All versions of printCounts below are equivalent programs:



      -- original definition
      printCounts :: IO ()
      printCounts = do
      c <- newCounter
      print =<< c
      print =<< c
      print =<< c

      -- by definition of newCounter...

      printCounts = do
      c <- do
      r <- newIORef 0
      return $ do
      v <- readIORef r
      writeIORef r (v + 1)
      return v
      print =<< c
      print =<< c
      print =<< c

      -- by the monad laws (quite hand-wavy for brevity)
      -- do
      -- c <- do
      -- X
      -- Y
      -- .....
      -- =
      -- do
      -- X
      -- c <-
      -- Y
      -- .....
      --
      -- (more formally,
      -- ((m >>= x -> k x) >>= h) = (m >>= (x -> k x >>= h)))

      printCounts = do
      r <- newIORef 0
      c <-
      return $ do
      v <- readIORef r
      writeIORef r (v + 1)
      return v
      print =<< c
      print =<< c
      print =<< c

      -- c <- return X
      -- =
      -- let c = X
      --
      -- (more formally, ((return X) >>= (c -> k c)) = (k X)

      printCounts = do
      r <- newIORef 0
      let c = do
      v <- readIORef r
      writeIORef r (v + 1)
      return v
      print =<< c
      print =<< c
      print =<< c

      -- let-substitution

      printCounts = do
      r <- newIORef 0
      print =<< do
      v <- readIORef r
      writeIORef r (v + 1)
      return v
      print =<< do
      v <- readIORef r
      writeIORef r (v + 1)
      return v
      print =<< do
      v <- readIORef r
      writeIORef r (v + 1)
      return v

      -- after many more applications of monad laws and a bit of renaming to avoid shadowing
      -- (in particular, one important step is ((return v >>= print) = (print v)))

      printCounts = do
      r <- newIORef 0
      v1 <- readIORef r
      writeIORef r (v1 + 1)
      print v1
      v2 <- readIORef r
      writeIORef r (v2 + 1)
      print v2
      v3 <- readIORef r
      writeIORef r (v3 + 1)
      print v3


      In the final version, you can see that printCounts quite literally allocates a counter and increments it three times, printing each intermediate value.



      One key step is the let-substitution one, where the counter program gets duplicated, which is why it gets to run three times. let x = p; ... is different from x <- p; ..., which runs p, and binds x to the result rather than the program p itself.






      share|improve this answer




























        7












        7








        7







        You can think of IO as a type of programs. newCounter :: IO (IO Int) is a program that outputs a program. More precisely, newCounter allocates a new counter, and returns a program that, when run, increments the counter and returns its old value. newCounter doesn't execute the program it returns. It would if you wrote instead:



        newCounter :: IO (IO Int)
        newCounter = do
        r <- newIORef 0
        let p = do -- name the counter program p
        v <- readIORef r
        writeIORef r (v + 1)
        return v
        p -- run the counter program once
        return p -- you can still return it to run again later




        You can also use equational reasoning to unfold printCounts into a sequence of primitives. All versions of printCounts below are equivalent programs:



        -- original definition
        printCounts :: IO ()
        printCounts = do
        c <- newCounter
        print =<< c
        print =<< c
        print =<< c

        -- by definition of newCounter...

        printCounts = do
        c <- do
        r <- newIORef 0
        return $ do
        v <- readIORef r
        writeIORef r (v + 1)
        return v
        print =<< c
        print =<< c
        print =<< c

        -- by the monad laws (quite hand-wavy for brevity)
        -- do
        -- c <- do
        -- X
        -- Y
        -- .....
        -- =
        -- do
        -- X
        -- c <-
        -- Y
        -- .....
        --
        -- (more formally,
        -- ((m >>= x -> k x) >>= h) = (m >>= (x -> k x >>= h)))

        printCounts = do
        r <- newIORef 0
        c <-
        return $ do
        v <- readIORef r
        writeIORef r (v + 1)
        return v
        print =<< c
        print =<< c
        print =<< c

        -- c <- return X
        -- =
        -- let c = X
        --
        -- (more formally, ((return X) >>= (c -> k c)) = (k X)

        printCounts = do
        r <- newIORef 0
        let c = do
        v <- readIORef r
        writeIORef r (v + 1)
        return v
        print =<< c
        print =<< c
        print =<< c

        -- let-substitution

        printCounts = do
        r <- newIORef 0
        print =<< do
        v <- readIORef r
        writeIORef r (v + 1)
        return v
        print =<< do
        v <- readIORef r
        writeIORef r (v + 1)
        return v
        print =<< do
        v <- readIORef r
        writeIORef r (v + 1)
        return v

        -- after many more applications of monad laws and a bit of renaming to avoid shadowing
        -- (in particular, one important step is ((return v >>= print) = (print v)))

        printCounts = do
        r <- newIORef 0
        v1 <- readIORef r
        writeIORef r (v1 + 1)
        print v1
        v2 <- readIORef r
        writeIORef r (v2 + 1)
        print v2
        v3 <- readIORef r
        writeIORef r (v3 + 1)
        print v3


        In the final version, you can see that printCounts quite literally allocates a counter and increments it three times, printing each intermediate value.



        One key step is the let-substitution one, where the counter program gets duplicated, which is why it gets to run three times. let x = p; ... is different from x <- p; ..., which runs p, and binds x to the result rather than the program p itself.






        share|improve this answer















        You can think of IO as a type of programs. newCounter :: IO (IO Int) is a program that outputs a program. More precisely, newCounter allocates a new counter, and returns a program that, when run, increments the counter and returns its old value. newCounter doesn't execute the program it returns. It would if you wrote instead:



        newCounter :: IO (IO Int)
        newCounter = do
        r <- newIORef 0
        let p = do -- name the counter program p
        v <- readIORef r
        writeIORef r (v + 1)
        return v
        p -- run the counter program once
        return p -- you can still return it to run again later




        You can also use equational reasoning to unfold printCounts into a sequence of primitives. All versions of printCounts below are equivalent programs:



        -- original definition
        printCounts :: IO ()
        printCounts = do
        c <- newCounter
        print =<< c
        print =<< c
        print =<< c

        -- by definition of newCounter...

        printCounts = do
        c <- do
        r <- newIORef 0
        return $ do
        v <- readIORef r
        writeIORef r (v + 1)
        return v
        print =<< c
        print =<< c
        print =<< c

        -- by the monad laws (quite hand-wavy for brevity)
        -- do
        -- c <- do
        -- X
        -- Y
        -- .....
        -- =
        -- do
        -- X
        -- c <-
        -- Y
        -- .....
        --
        -- (more formally,
        -- ((m >>= x -> k x) >>= h) = (m >>= (x -> k x >>= h)))

        printCounts = do
        r <- newIORef 0
        c <-
        return $ do
        v <- readIORef r
        writeIORef r (v + 1)
        return v
        print =<< c
        print =<< c
        print =<< c

        -- c <- return X
        -- =
        -- let c = X
        --
        -- (more formally, ((return X) >>= (c -> k c)) = (k X)

        printCounts = do
        r <- newIORef 0
        let c = do
        v <- readIORef r
        writeIORef r (v + 1)
        return v
        print =<< c
        print =<< c
        print =<< c

        -- let-substitution

        printCounts = do
        r <- newIORef 0
        print =<< do
        v <- readIORef r
        writeIORef r (v + 1)
        return v
        print =<< do
        v <- readIORef r
        writeIORef r (v + 1)
        return v
        print =<< do
        v <- readIORef r
        writeIORef r (v + 1)
        return v

        -- after many more applications of monad laws and a bit of renaming to avoid shadowing
        -- (in particular, one important step is ((return v >>= print) = (print v)))

        printCounts = do
        r <- newIORef 0
        v1 <- readIORef r
        writeIORef r (v1 + 1)
        print v1
        v2 <- readIORef r
        writeIORef r (v2 + 1)
        print v2
        v3 <- readIORef r
        writeIORef r (v3 + 1)
        print v3


        In the final version, you can see that printCounts quite literally allocates a counter and increments it three times, printing each intermediate value.



        One key step is the let-substitution one, where the counter program gets duplicated, which is why it gets to run three times. let x = p; ... is different from x <- p; ..., which runs p, and binds x to the result rather than the program p itself.







        share|improve this answer














        share|improve this answer



        share|improve this answer








        edited Nov 26 '18 at 21:21

























        answered Nov 26 '18 at 20:05









        Li-yao XiaLi-yao Xia

        13.3k1430




        13.3k1430
































            draft saved

            draft discarded




















































            Thanks for contributing an answer to Stack Overflow!


            • Please be sure to answer the question. Provide details and share your research!

            But avoid



            • Asking for help, clarification, or responding to other answers.

            • Making statements based on opinion; back them up with references or personal experience.


            To learn more, see our tips on writing great answers.




            draft saved


            draft discarded














            StackExchange.ready(
            function () {
            StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53487894%2fhaskell-ioref-an-answer-vs-a-function-to-get-an-answer%23new-answer', 'question_page');
            }
            );

            Post as a guest















            Required, but never shown





















































            Required, but never shown














            Required, but never shown












            Required, but never shown







            Required, but never shown

































            Required, but never shown














            Required, but never shown












            Required, but never shown







            Required, but never shown







            Popular posts from this blog

            Wiesbaden

            Marschland

            Dieringhausen